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 java.io.UnsupportedEncodingException;
18 import java.nio.ByteBuffer;
19 import java.nio.ByteOrder;
20 import java.text.ParseException;
21 import java.text.SimpleDateFormat;
22 import java.util.Arrays;
23 import java.util.Date;
24 
25 import android.util.Log;
26 
27 /**
28  * This class encapsulates the appParams needed for MAP.
29  */
30 public class BluetoothMapAppParams {
31 
32     private static final String TAG = "BluetoothMapAppParams";
33 
34     private static final int MAX_LIST_COUNT           = 0x01;
35     private static final int MAX_LIST_COUNT_LEN       = 0x02; //, 0x0000, 0xFFFF),
36     private static final int START_OFFSET             = 0x02;
37     private static final int START_OFFSET_LEN         = 0x02; //, 0x0000, 0xFFFF),
38     private static final int FILTER_MESSAGE_TYPE      = 0x03;
39     private static final int FILTER_MESSAGE_TYPE_LEN  = 0x01; //, 0x0000, 0x000f),
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_READ_STATUS_LEN   = 0x01; //, 0x0000, 0x0002),
44     private static final int FILTER_RECIPIENT         = 0x07;
45     private static final int FILTER_ORIGINATOR        = 0x08;
46     private static final int FILTER_PRIORITY          = 0x09;
47     private static final int FILTER_PRIORITY_LEN      = 0x01; //, 0x0000, 0x0002),
48     private static final int ATTACHMENT               = 0x0A;
49     private static final int ATTACHMENT_LEN           = 0x01; //, 0x0000, 0x0001),
50     private static final int TRANSPARENT              = 0x0B;
51     private static final int TRANSPARENT_LEN          = 0x01; //, 0x0000, 0x0001),
52     private static final int RETRY                    = 0x0C;
53     private static final int RETRY_LEN                = 0x01; //, 0x0000, 0x0001),
54     private static final int NEW_MESSAGE              = 0x0D;
55     private static final int NEW_MESSAGE_LEN          = 0x01; //, 0x0000, 0x0001),
56     private static final int NOTIFICATION_STATUS      = 0x0E;
57     private static final int NOTIFICATION_STATUS_LEN  = 0x01; //, 0x0000, 0xFFFF),
58     private static final int MAS_INSTANCE_ID          = 0x0F;
59     private static final int MAS_INSTANCE_ID_LEN      = 0x01; //, 0x0000, 0x00FF),
60     private static final int PARAMETER_MASK           = 0x10;
61     private static final int PARAMETER_MASK_LEN       = 0x04; //, 0x0000, 0x0000),
62     private static final int FOLDER_LISTING_SIZE      = 0x11;
63     private static final int FOLDER_LISTING_SIZE_LEN  = 0x02; //, 0x0000, 0xFFFF),
64     private static final int MESSAGE_LISTING_SIZE     = 0x12;
65     private static final int MESSAGE_LISTING_SIZE_LEN = 0x02; //, 0x0000, 0xFFFF),
66     private static final int SUBJECT_LENGTH           = 0x13;
67     private static final int SUBJECT_LENGTH_LEN       = 0x01; //, 0x0000, 0x00FF),
68     private static final int CHARSET                  = 0x14;
69     private static final int CHARSET_LEN              = 0x01; //, 0x0000, 0x0001),
70     private static final int FRACTION_REQUEST         = 0x15;
71     private static final int FRACTION_REQUEST_LEN     = 0x01; //, 0x0000, 0x0001),
72     private static final int FRACTION_DELIVER         = 0x16;
73     private static final int FRACTION_DELIVER_LEN     = 0x01; //, 0x0000, 0x0001),
74     private static final int STATUS_INDICATOR         = 0x17;
75     private static final int STATUS_INDICATOR_LEN     = 0x01; //, 0x0000, 0x0001),
76     private static final int STATUS_VALUE             = 0x18;
77     private static final int STATUS_VALUE_LEN         = 0x01; //, 0x0000, 0x0001),
78     private static final int MSE_TIME                 = 0x19;
79 
80     public static final int INVALID_VALUE_PARAMETER = -1;
81     public static final int NOTIFICATION_STATUS_NO = 0;
82     public static final int NOTIFICATION_STATUS_YES = 1;
83     public static final int STATUS_INDICATOR_READ = 0;
84     public static final int STATUS_INDICATOR_DELETED = 1;
85     public static final int STATUS_VALUE_YES = 1;
86     public static final int STATUS_VALUE_NO = 0;
87     public static final int CHARSET_NATIVE = 0;
88     public static final int CHARSET_UTF8 = 1;
89     public static final int FRACTION_REQUEST_FIRST = 0;
90     public static final int FRACTION_REQUEST_NEXT = 1;
91     public static final int FRACTION_DELIVER_MORE = 0;
92     public static final int FRACTION_DELIVER_LAST = 1;
93 
94 
95     public static final int FILTER_NO_SMS_GSM  = 0x01;
96     public static final int FILTER_NO_SMS_CDMA = 0x02;
97     public static final int FILTER_NO_EMAIL    = 0x04;
98     public static final int FILTER_NO_MMS      = 0x08;
99 
100     /* Default values for omitted application parameters */
101     public static final long PARAMETER_MASK_ALL_ENABLED = 0xFFFF; // TODO: Update when bit 16-31 will be used.
102 
103     private int mMaxListCount        = INVALID_VALUE_PARAMETER;
104     private int mStartOffset         = INVALID_VALUE_PARAMETER;
105     private int mFilterMessageType   = INVALID_VALUE_PARAMETER;
106     private long mFilterPeriodBegin  = INVALID_VALUE_PARAMETER;
107     private long mFilterPeriodEnd    = INVALID_VALUE_PARAMETER;
108     private int mFilterReadStatus    = INVALID_VALUE_PARAMETER;
109     private String mFilterRecipient   = null;
110     private String mFilterOriginator  = null;
111     private int mFilterPriority      = INVALID_VALUE_PARAMETER;
112     private int mAttachment          = INVALID_VALUE_PARAMETER;
113     private int mTransparent         = INVALID_VALUE_PARAMETER;
114     private int mRetry               = INVALID_VALUE_PARAMETER;
115     private int mNewMessage          = INVALID_VALUE_PARAMETER;
116     private int mNotificationStatus  = INVALID_VALUE_PARAMETER;
117     private int mMasInstanceId       = INVALID_VALUE_PARAMETER;
118     private long mParameterMask      = INVALID_VALUE_PARAMETER;
119     private int mFolderListingSize   = INVALID_VALUE_PARAMETER;
120     private int mMessageListingSize  = INVALID_VALUE_PARAMETER;
121     private int mSubjectLength       = INVALID_VALUE_PARAMETER;
122     private int mCharset             = INVALID_VALUE_PARAMETER;
123     private int mFractionRequest     = INVALID_VALUE_PARAMETER;
124     private int mFractionDeliver     = INVALID_VALUE_PARAMETER;
125     private int mStatusIndicator     = INVALID_VALUE_PARAMETER;
126     private int mStatusValue         = INVALID_VALUE_PARAMETER;
127     private long mMseTime            = INVALID_VALUE_PARAMETER;
128 
129     /**
130      * Default constructor, used to build an application parameter object to be
131      * encoded. By default the member variables will be initialized to
132      * {@link INVALID_VALUE_PARAMETER} for values, and empty strings for String
133      * typed members.
134      */
BluetoothMapAppParams()135     public BluetoothMapAppParams() {
136     }
137 
138     /**
139      * Creates an application parameter object based on a application parameter
140      * OBEX header. The content of the {@link appParam} byte array will be
141      * parsed, and its content will be stored in the member variables.
142      * {@link INVALID_VALUE_PARAMETER} can be used to determine if a value is
143      * set or not, where strings will be empty, if {@link appParam} did not
144      * contain the parameter.
145      *
146      * @param appParams
147      *            the byte array containing the application parameters OBEX
148      *            header
149      * @throws IllegalArgumentException
150      *             when a parameter does not respect the valid ranges specified
151      *             in the MAP spec.
152      * @throws ParseException
153      *             if a parameter string if formated incorrectly.
154      */
BluetoothMapAppParams(final byte[] appParams)155     public BluetoothMapAppParams(final byte[] appParams)
156                  throws IllegalArgumentException, ParseException {
157         ParseParams(appParams);
158     }
159 
160     /**
161      * Parse an application parameter OBEX header stored in a ByteArray.
162      *
163      * @param appParams
164      *            the byte array containing the application parameters OBEX
165      *            header
166      * @throws IllegalArgumentException
167      *             when a parameter does not respect the valid ranges specified
168      *             in the MAP spec.
169      * @throws ParseException
170      *             if a parameter string if formated incorrectly.
171      */
ParseParams(final byte[] appParams)172     private void ParseParams(final byte[] appParams) throws ParseException,
173               IllegalArgumentException {
174         int i = 0;
175         int tagId, tagLength;
176         ByteBuffer appParamBuf = ByteBuffer.wrap(appParams);
177         appParamBuf.order(ByteOrder.BIG_ENDIAN);
178         while (i < appParams.length) {
179             tagId = appParams[i++] & 0xff;     // Convert to unsigned to support values above 127
180             tagLength = appParams[i++] & 0xff; // Convert to unsigned to support values above 127
181             switch (tagId) {
182             case MAX_LIST_COUNT:
183                 if (tagLength != MAX_LIST_COUNT_LEN) {
184                     Log.w(TAG, "MAX_LIST_COUNT: Wrong length received: " + tagLength
185                                + " expected: " + MAX_LIST_COUNT_LEN);
186                     break;
187                 }
188                 setMaxListCount(appParamBuf.getShort(i) & 0xffff); // Make it unsigned
189                 break;
190             case START_OFFSET:
191                 if (tagLength != START_OFFSET_LEN) {
192                     Log.w(TAG, "START_OFFSET: Wrong length received: " + tagLength + " expected: "
193                                + START_OFFSET_LEN);
194                     break;
195                 }
196                 setStartOffset(appParamBuf.getShort(i) & 0xffff); // Make it unsigned
197                 break;
198             case FILTER_MESSAGE_TYPE:
199                 if (tagLength != FILTER_MESSAGE_TYPE_LEN) {
200                     Log.w(TAG, "FILTER_MESSAGE_TYPE: Wrong length received: " + tagLength + " expected: "
201                             + FILTER_MESSAGE_TYPE_LEN);
202                     break;
203                 }
204                     setFilterMessageType(appParams[i] & 0x0f);
205                 break;
206             case FILTER_PERIOD_BEGIN:
207                 if(tagLength != 0) {
208                     setFilterPeriodBegin(new String(appParams, i, tagLength));
209                 }
210                 break;
211             case FILTER_PERIOD_END:
212                 if(tagLength != 0) {
213                     setFilterPeriodEnd(new String(appParams, i, tagLength));
214                 }
215                 break;
216             case FILTER_READ_STATUS:
217                 if (tagLength != FILTER_READ_STATUS_LEN) {
218                      Log.w(TAG, "FILTER_READ_STATUS: Wrong length received: " + tagLength + " expected: "
219                              + FILTER_READ_STATUS_LEN);
220                      break;
221                  }
222                 setFilterReadStatus(appParams[i] & 0x03); // Lower two bits
223                 break;
224             case FILTER_RECIPIENT:
225                 if(tagLength != 0) {
226                     setFilterRecipient(new String(appParams, i, tagLength));
227                 }
228                 break;
229             case FILTER_ORIGINATOR:
230                 if(tagLength != 0) {
231                     setFilterOriginator(new String(appParams, i, tagLength));
232                 }
233                 break;
234             case FILTER_PRIORITY:
235                 if (tagLength != FILTER_PRIORITY_LEN) {
236                      Log.w(TAG, "FILTER_PRIORITY: Wrong length received: " + tagLength + " expected: "
237                              + FILTER_PRIORITY_LEN);
238                      break;
239                 }
240                 setFilterPriority(appParams[i] & 0x03); // Lower two bits
241                 break;
242             case ATTACHMENT:
243                 if (tagLength != ATTACHMENT_LEN) {
244                      Log.w(TAG, "ATTACHMENT: Wrong length received: " + tagLength + " expected: "
245                              + ATTACHMENT_LEN);
246                      break;
247                 }
248                 setAttachment(appParams[i] & 0x01); // Lower bit
249                 break;
250             case TRANSPARENT:
251                 if (tagLength != TRANSPARENT_LEN) {
252                      Log.w(TAG, "TRANSPARENT: Wrong length received: " + tagLength + " expected: "
253                              + TRANSPARENT_LEN);
254                      break;
255                 }
256                 setTransparent(appParams[i] & 0x01); // Lower bit
257                 break;
258             case RETRY:
259                 if (tagLength != RETRY_LEN) {
260                      Log.w(TAG, "RETRY: Wrong length received: " + tagLength + " expected: "
261                              + RETRY_LEN);
262                      break;
263                 }
264                 setRetry(appParams[i] & 0x01); // Lower bit
265                 break;
266             case NEW_MESSAGE:
267                 if (tagLength != NEW_MESSAGE_LEN) {
268                      Log.w(TAG, "NEW_MESSAGE: Wrong length received: " + tagLength + " expected: "
269                              + NEW_MESSAGE_LEN);
270                      break;
271                 }
272                 setNewMessage(appParams[i] & 0x01); // Lower bit
273                 break;
274             case NOTIFICATION_STATUS:
275                 if (tagLength != NOTIFICATION_STATUS_LEN) {
276                      Log.w(TAG, "NOTIFICATION_STATUS: Wrong length received: " + tagLength + " expected: "
277                              + NOTIFICATION_STATUS_LEN);
278                      break;
279                 }
280                 setNotificationStatus(appParams[i] & 0x01); // Lower bit
281                 break;
282             case MAS_INSTANCE_ID:
283                 if (tagLength != MAS_INSTANCE_ID_LEN) {
284                     Log.w(TAG, "MAS_INSTANCE_ID: Wrong length received: " + tagLength + " expected: "
285                             + MAS_INSTANCE_ID_LEN);
286                     break;
287                 }
288                 setMasInstanceId(appParams[i] & 0xff);
289                 break;
290             case PARAMETER_MASK:
291                 if (tagLength != PARAMETER_MASK_LEN) {
292                     Log.w(TAG, "PARAMETER_MASK: Wrong length received: " + tagLength + " expected: "
293                             + PARAMETER_MASK_LEN);
294                     break;
295                 }
296                 setParameterMask(appParamBuf.getInt(i) & 0xffffffffL); // Make it unsigned
297                 break;
298             case FOLDER_LISTING_SIZE:
299                 if (tagLength != FOLDER_LISTING_SIZE_LEN) {
300                     Log.w(TAG, "FOLDER_LISTING_SIZE: Wrong length received: " + tagLength + " expected: "
301                             + FOLDER_LISTING_SIZE_LEN);
302                     break;
303                 }
304                 setFolderListingSize(appParamBuf.getShort(i) & 0xffff); // Make it unsigned
305                 break;
306             case MESSAGE_LISTING_SIZE:
307                 if (tagLength != MESSAGE_LISTING_SIZE_LEN) {
308                     Log.w(TAG, "MESSAGE_LISTING_SIZE: Wrong length received: " + tagLength + " expected: "
309                             + MESSAGE_LISTING_SIZE_LEN);
310                     break;
311                 }
312                 setMessageListingSize(appParamBuf.getShort(i) & 0xffff); // Make it unsigned
313                 break;
314             case SUBJECT_LENGTH:
315                 if (tagLength != SUBJECT_LENGTH_LEN) {
316                     Log.w(TAG, "SUBJECT_LENGTH: Wrong length received: " + tagLength + " expected: "
317                             + SUBJECT_LENGTH_LEN);
318                     break;
319                 }
320                 setSubjectLength(appParams[i] & 0xff);
321                 break;
322             case CHARSET:
323                 if (tagLength != CHARSET_LEN) {
324                     Log.w(TAG, "CHARSET: Wrong length received: " + tagLength + " expected: "
325                             + CHARSET_LEN);
326                     break;
327                 }
328                 setCharset(appParams[i] & 0x01); // Lower bit
329                 break;
330             case FRACTION_REQUEST:
331                 if (tagLength != FRACTION_REQUEST_LEN) {
332                     Log.w(TAG, "FRACTION_REQUEST: Wrong length received: " + tagLength + " expected: "
333                             + FRACTION_REQUEST_LEN);
334                     break;
335                 }
336                 setFractionRequest(appParams[i] & 0x01); // Lower bit
337                 break;
338             case FRACTION_DELIVER:
339                 if (tagLength != FRACTION_DELIVER_LEN) {
340                     Log.w(TAG, "FRACTION_DELIVER: Wrong length received: " + tagLength + " expected: "
341                             + FRACTION_DELIVER_LEN);
342                     break;
343                 }
344                 setFractionDeliver(appParams[i] & 0x01); // Lower bit
345                 break;
346             case STATUS_INDICATOR:
347                 if (tagLength != STATUS_INDICATOR_LEN) {
348                     Log.w(TAG, "STATUS_INDICATOR: Wrong length received: " + tagLength + " expected: "
349                             + STATUS_INDICATOR_LEN);
350                     break;
351                 }
352                 setStatusIndicator(appParams[i] & 0x01); // Lower bit
353                 break;
354             case STATUS_VALUE:
355                 if (tagLength != STATUS_VALUE_LEN) {
356                     Log.w(TAG, "STATUS_VALUER: Wrong length received: " + tagLength + " expected: "
357                             + STATUS_VALUE_LEN);
358                     break;
359                 }
360                 setStatusValue(appParams[i] & 0x01); // Lower bit
361                 break;
362             case MSE_TIME:
363                 setMseTime(new String(appParams, i, tagLength));
364                 break;
365             default:
366                 // Just skip unknown Tags, no need to report error
367                 Log.w(TAG, "Unknown TagId received ( 0x" + Integer.toString(tagId, 16)
368                            + "), skipping...");
369                 break;
370             }
371             i += tagLength; // Offset to next TagId
372         }
373     }
374 
375     /**
376      * Get the approximate length needed to store the appParameters in a byte
377      * array.
378      *
379      * @return the length in bytes
380      * @throws UnsupportedEncodingException
381      *             if the platform does not support UTF-8 encoding.
382      */
getParamMaxLength()383     private int getParamMaxLength() throws UnsupportedEncodingException {
384         int length = 0;
385         length += 25 * 2; // tagId + tagLength
386         length += 27; // fixed sizes
387         length += getFilterPeriodBegin() == INVALID_VALUE_PARAMETER ? 0 : 15;
388         length += getFilterPeriodEnd() == INVALID_VALUE_PARAMETER ? 0 : 15;
389         if (getFilterRecipient() != null)
390             length += getFilterRecipient().getBytes("UTF-8").length;
391         if (getFilterOriginator() != null)
392             length += getFilterOriginator().getBytes("UTF-8").length;
393         length += getMseTime() == INVALID_VALUE_PARAMETER ? 0 : 20;
394         return length;
395     }
396 
397     /**
398      * Encode the application parameter object to a byte array.
399      *
400      * @return a byte Array representation of the application parameter object.
401      * @throws UnsupportedEncodingException
402      *             if the platform does not support UTF-8 encoding.
403      */
EncodeParams()404     public byte[] EncodeParams() throws UnsupportedEncodingException {
405         ByteBuffer appParamBuf = ByteBuffer.allocate(getParamMaxLength());
406         appParamBuf.order(ByteOrder.BIG_ENDIAN);
407         byte[] retBuf;
408 
409         if (getMaxListCount() != INVALID_VALUE_PARAMETER) {
410             appParamBuf.put((byte) MAX_LIST_COUNT);
411             appParamBuf.put((byte) MAX_LIST_COUNT_LEN);
412             appParamBuf.putShort((short) getMaxListCount());
413         }
414         if (getStartOffset() != INVALID_VALUE_PARAMETER) {
415             appParamBuf.put((byte) START_OFFSET);
416             appParamBuf.put((byte) START_OFFSET_LEN);
417             appParamBuf.putShort((short) getStartOffset());
418         }
419         if (getFilterMessageType() != INVALID_VALUE_PARAMETER) {
420             appParamBuf.put((byte) FILTER_MESSAGE_TYPE);
421             appParamBuf.put((byte) FILTER_MESSAGE_TYPE_LEN);
422             appParamBuf.put((byte) getFilterMessageType());
423         }
424         if (getFilterPeriodBegin() != INVALID_VALUE_PARAMETER) {
425             appParamBuf.put((byte) FILTER_PERIOD_BEGIN);
426             appParamBuf.put((byte) getFilterPeriodBeginString().getBytes("UTF-8").length);
427             appParamBuf.put(getFilterPeriodBeginString().getBytes("UTF-8"));
428         }
429         if (getFilterPeriodEnd() != INVALID_VALUE_PARAMETER) {
430             appParamBuf.put((byte) FILTER_PERIOD_END);
431             appParamBuf.put((byte) getFilterPeriodEndString().getBytes("UTF-8").length);
432             appParamBuf.put(getFilterPeriodEndString().getBytes("UTF-8"));
433         }
434         if (getFilterReadStatus() != INVALID_VALUE_PARAMETER) {
435             appParamBuf.put((byte) FILTER_READ_STATUS);
436             appParamBuf.put((byte) FILTER_READ_STATUS_LEN);
437             appParamBuf.put((byte) getFilterReadStatus());
438         }
439         if (getFilterRecipient() != null) {
440             appParamBuf.put((byte) FILTER_RECIPIENT);
441             appParamBuf.put((byte) getFilterRecipient().getBytes("UTF-8").length);
442             appParamBuf.put(getFilterRecipient().getBytes("UTF-8"));
443         }
444         if (getFilterOriginator() != null) {
445             appParamBuf.put((byte) FILTER_ORIGINATOR);
446             appParamBuf.put((byte) getFilterOriginator().getBytes("UTF-8").length);
447             appParamBuf.put(getFilterOriginator().getBytes("UTF-8"));
448         }
449         if (getFilterPriority() != INVALID_VALUE_PARAMETER) {
450             appParamBuf.put((byte) FILTER_PRIORITY);
451             appParamBuf.put((byte) FILTER_PRIORITY_LEN);
452             appParamBuf.put((byte) getFilterPriority());
453         }
454         if (getAttachment() != INVALID_VALUE_PARAMETER) {
455             appParamBuf.put((byte) ATTACHMENT);
456             appParamBuf.put((byte) ATTACHMENT_LEN);
457             appParamBuf.put((byte) getAttachment());
458         }
459         if (getTransparent() != INVALID_VALUE_PARAMETER) {
460             appParamBuf.put((byte) TRANSPARENT);
461             appParamBuf.put((byte) TRANSPARENT_LEN);
462             appParamBuf.put((byte) getTransparent());
463         }
464         if (getRetry() != INVALID_VALUE_PARAMETER) {
465             appParamBuf.put((byte) RETRY);
466             appParamBuf.put((byte) RETRY_LEN);
467             appParamBuf.put((byte) getRetry());
468         }
469         if (getNewMessage() != INVALID_VALUE_PARAMETER) {
470             appParamBuf.put((byte) NEW_MESSAGE);
471             appParamBuf.put((byte) NEW_MESSAGE_LEN);
472             appParamBuf.put((byte) getNewMessage());
473         }
474         if (getNotificationStatus() != INVALID_VALUE_PARAMETER) {
475             appParamBuf.put((byte) NOTIFICATION_STATUS);
476             appParamBuf.put((byte) NOTIFICATION_STATUS_LEN);
477             appParamBuf.putShort((short) getNotificationStatus());
478         }
479         if (getMasInstanceId() != INVALID_VALUE_PARAMETER) {
480             appParamBuf.put((byte) MAS_INSTANCE_ID);
481             appParamBuf.put((byte) MAS_INSTANCE_ID_LEN);
482             appParamBuf.put((byte) getMasInstanceId());
483         }
484         if (getParameterMask() != INVALID_VALUE_PARAMETER) {
485             appParamBuf.put((byte) PARAMETER_MASK);
486             appParamBuf.put((byte) PARAMETER_MASK_LEN);
487             appParamBuf.putInt((int) getParameterMask());
488         }
489         if (getFolderListingSize() != INVALID_VALUE_PARAMETER) {
490             appParamBuf.put((byte) FOLDER_LISTING_SIZE);
491             appParamBuf.put((byte) FOLDER_LISTING_SIZE_LEN);
492             appParamBuf.putShort((short) getFolderListingSize());
493         }
494         if (getMessageListingSize() != INVALID_VALUE_PARAMETER) {
495             appParamBuf.put((byte) MESSAGE_LISTING_SIZE);
496             appParamBuf.put((byte) MESSAGE_LISTING_SIZE_LEN);
497             appParamBuf.putShort((short) getMessageListingSize());
498         }
499         if (getSubjectLength() != INVALID_VALUE_PARAMETER) {
500             appParamBuf.put((byte) SUBJECT_LENGTH);
501             appParamBuf.put((byte) SUBJECT_LENGTH_LEN);
502             appParamBuf.put((byte) getSubjectLength());
503         }
504         if (getCharset() != INVALID_VALUE_PARAMETER) {
505             appParamBuf.put((byte) CHARSET);
506             appParamBuf.put((byte) CHARSET_LEN);
507             appParamBuf.put((byte) getCharset());
508         }
509         if (getFractionRequest() != INVALID_VALUE_PARAMETER) {
510             appParamBuf.put((byte) FRACTION_REQUEST);
511             appParamBuf.put((byte) FRACTION_REQUEST_LEN);
512             appParamBuf.put((byte) getFractionRequest());
513         }
514         if (getFractionDeliver() != INVALID_VALUE_PARAMETER) {
515             appParamBuf.put((byte) FRACTION_DELIVER);
516             appParamBuf.put((byte) FRACTION_DELIVER_LEN);
517             appParamBuf.put((byte) getFractionDeliver());
518         }
519         if (getStatusIndicator() != INVALID_VALUE_PARAMETER) {
520             appParamBuf.put((byte) STATUS_INDICATOR);
521             appParamBuf.put((byte) STATUS_INDICATOR_LEN);
522             appParamBuf.put((byte) getStatusIndicator());
523         }
524         if (getStatusValue() != INVALID_VALUE_PARAMETER) {
525             appParamBuf.put((byte) STATUS_VALUE);
526             appParamBuf.put((byte) STATUS_VALUE_LEN);
527             appParamBuf.put((byte) getStatusValue());
528         }
529         if (getMseTime() != INVALID_VALUE_PARAMETER) {
530             appParamBuf.put((byte) MSE_TIME);
531             appParamBuf.put((byte) getMseTimeString().getBytes("UTF-8").length);
532             appParamBuf.put(getMseTimeString().getBytes("UTF-8"));
533         }
534         // We need to reduce the length of the array to match the content
535         retBuf = Arrays.copyOfRange(appParamBuf.array(), appParamBuf.arrayOffset(),
536                                     appParamBuf.arrayOffset() + appParamBuf.position());
537         return retBuf;
538     }
539 
getMaxListCount()540     public int getMaxListCount() {
541         return mMaxListCount;
542     }
543 
setMaxListCount(int maxListCount)544     public void setMaxListCount(int maxListCount) throws IllegalArgumentException {
545         if (maxListCount < 0 || maxListCount > 0xFFFF)
546             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0xFFFF");
547         this.mMaxListCount = maxListCount;
548     }
549 
getStartOffset()550     public int getStartOffset() {
551         return mStartOffset;
552     }
553 
setStartOffset(int startOffset)554     public void setStartOffset(int startOffset) throws IllegalArgumentException {
555         if (startOffset < 0 || startOffset > 0xFFFF)
556             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0xFFFF");
557         this.mStartOffset = startOffset;
558     }
559 
getFilterMessageType()560     public int getFilterMessageType() {
561         return mFilterMessageType;
562     }
563 
setFilterMessageType(int filterMessageType)564     public void setFilterMessageType(int filterMessageType) throws IllegalArgumentException {
565         if (filterMessageType < 0 || filterMessageType > 0x000F)
566             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x000F");
567         this.mFilterMessageType = filterMessageType;
568     }
569 
getFilterPeriodBegin()570     public long getFilterPeriodBegin() {
571         return mFilterPeriodBegin;
572     }
573 
getFilterPeriodBeginString()574     public String getFilterPeriodBeginString() {
575         SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
576         Date date = new Date(mFilterPeriodBegin);
577         return format.format(date); // Format to YYYYMMDDTHHMMSS local time
578     }
579 
setFilterPeriodBegin(long filterPeriodBegin)580     public void setFilterPeriodBegin(long filterPeriodBegin) {
581         this.mFilterPeriodBegin = filterPeriodBegin;
582     }
583 
setFilterPeriodBegin(String filterPeriodBegin)584     public void setFilterPeriodBegin(String filterPeriodBegin) throws ParseException {
585         SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
586         Date date = format.parse(filterPeriodBegin);
587         this.mFilterPeriodBegin = date.getTime();
588     }
589 
getFilterPeriodEnd()590     public long getFilterPeriodEnd() {
591         return mFilterPeriodEnd;
592     }
593 
getFilterPeriodEndString()594     public String getFilterPeriodEndString() {
595         SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
596         Date date = new Date(mFilterPeriodEnd);
597         return format.format(date); // Format to YYYYMMDDTHHMMSS local time
598     }
599 
setFilterPeriodEnd(long filterPeriodEnd)600     public void setFilterPeriodEnd(long filterPeriodEnd) {
601         this.mFilterPeriodEnd = filterPeriodEnd;
602     }
603 
setFilterPeriodEnd(String filterPeriodEnd)604     public void setFilterPeriodEnd(String filterPeriodEnd) throws ParseException {
605         SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
606         Date date = format.parse(filterPeriodEnd);
607         this.mFilterPeriodEnd = date.getTime();
608     }
609 
getFilterReadStatus()610     public int getFilterReadStatus() {
611         return mFilterReadStatus;
612     }
613 
setFilterReadStatus(int filterReadStatus)614     public void setFilterReadStatus(int filterReadStatus) throws IllegalArgumentException {
615         if (filterReadStatus < 0 || filterReadStatus > 0x0002)
616             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0002");
617         this.mFilterReadStatus = filterReadStatus;
618     }
619 
getFilterRecipient()620     public String getFilterRecipient() {
621         return mFilterRecipient;
622     }
623 
setFilterRecipient(String filterRecipient)624     public void setFilterRecipient(String filterRecipient) {
625         this.mFilterRecipient = filterRecipient;
626     }
627 
getFilterOriginator()628     public String getFilterOriginator() {
629         return mFilterOriginator;
630     }
631 
setFilterOriginator(String filterOriginator)632     public void setFilterOriginator(String filterOriginator) {
633         this.mFilterOriginator = filterOriginator;
634     }
635 
getFilterPriority()636     public int getFilterPriority() {
637         return mFilterPriority;
638     }
639 
setFilterPriority(int filterPriority)640     public void setFilterPriority(int filterPriority) throws IllegalArgumentException {
641         if (filterPriority < 0 || filterPriority > 0x0002)
642             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0002");
643         this.mFilterPriority = filterPriority;
644     }
645 
getAttachment()646     public int getAttachment() {
647         return mAttachment;
648     }
649 
setAttachment(int attachment)650     public void setAttachment(int attachment) throws IllegalArgumentException {
651         if (attachment < 0 || attachment > 0x0001)
652             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
653         this.mAttachment = attachment;
654     }
655 
getTransparent()656     public int getTransparent() {
657         return mTransparent;
658     }
659 
setTransparent(int transparent)660     public void setTransparent(int transparent) throws IllegalArgumentException {
661         if (transparent < 0 || transparent > 0x0001)
662             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
663         this.mTransparent = transparent;
664     }
665 
getRetry()666     public int getRetry() {
667         return mRetry;
668     }
669 
setRetry(int retry)670     public void setRetry(int retry) throws IllegalArgumentException {
671         if (retry < 0 || retry > 0x0001)
672             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
673         this.mRetry = retry;
674     }
675 
getNewMessage()676     public int getNewMessage() {
677         return mNewMessage;
678     }
679 
setNewMessage(int newMessage)680     public void setNewMessage(int newMessage) throws IllegalArgumentException {
681         if (newMessage < 0 || newMessage > 0x0001)
682             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
683         this.mNewMessage = newMessage;
684     }
685 
getNotificationStatus()686     public int getNotificationStatus() {
687         return mNotificationStatus;
688     }
689 
setNotificationStatus(int notificationStatus)690     public void setNotificationStatus(int notificationStatus) throws IllegalArgumentException {
691         if (notificationStatus < 0 || notificationStatus > 0x0001)
692             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
693         this.mNotificationStatus = notificationStatus;
694     }
695 
getMasInstanceId()696     public int getMasInstanceId() {
697         return mMasInstanceId;
698     }
699 
setMasInstanceId(int masInstanceId)700     public void setMasInstanceId(int masInstanceId) {
701         if (masInstanceId < 0 || masInstanceId > 0x00FF)
702             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x00FF");
703         this.mMasInstanceId = masInstanceId;
704     }
705 
getParameterMask()706     public long getParameterMask() {
707         return mParameterMask;
708     }
709 
setParameterMask(long parameterMask)710     public void setParameterMask(long parameterMask) {
711         if (parameterMask < 0 || parameterMask > 0xFFFFFFFFL)
712             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0xFFFFFFFF");
713         this.mParameterMask = parameterMask;
714     }
715 
getFolderListingSize()716     public int getFolderListingSize() {
717         return mFolderListingSize;
718     }
719 
setFolderListingSize(int folderListingSize)720     public void setFolderListingSize(int folderListingSize) {
721         if (folderListingSize < 0 || folderListingSize > 0xFFFF)
722             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0xFFFF");
723         this.mFolderListingSize = folderListingSize;
724     }
725 
getMessageListingSize()726     public int getMessageListingSize() {
727         return mMessageListingSize;
728     }
729 
setMessageListingSize(int messageListingSize)730     public void setMessageListingSize(int messageListingSize) {
731         if (messageListingSize < 0 || messageListingSize > 0xFFFF)
732             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0xFFFF");
733         this.mMessageListingSize = messageListingSize;
734     }
735 
getSubjectLength()736     public int getSubjectLength() {
737         return mSubjectLength;
738     }
739 
setSubjectLength(int subjectLength)740     public void setSubjectLength(int subjectLength) {
741         if (subjectLength < 0 || subjectLength > 0xFF)
742             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x00FF");
743         this.mSubjectLength = subjectLength;
744     }
745 
getCharset()746     public int getCharset() {
747         return mCharset;
748     }
749 
setCharset(int charset)750     public void setCharset(int charset) {
751         if (charset < 0 || charset > 0x1)
752             throw new IllegalArgumentException("Out of range: " + charset + ", valid range is 0x0000 to 0x0001");
753         this.mCharset = charset;
754     }
755 
getFractionRequest()756     public int getFractionRequest() {
757         return mFractionRequest;
758     }
759 
setFractionRequest(int fractionRequest)760     public void setFractionRequest(int fractionRequest) {
761         if (fractionRequest < 0 || fractionRequest > 0x1)
762             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
763         this.mFractionRequest = fractionRequest;
764     }
765 
getFractionDeliver()766     public int getFractionDeliver() {
767         return mFractionDeliver;
768     }
769 
setFractionDeliver(int fractionDeliver)770     public void setFractionDeliver(int fractionDeliver) {
771         if (fractionDeliver < 0 || fractionDeliver > 0x1)
772             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
773         this.mFractionDeliver = fractionDeliver;
774     }
775 
getStatusIndicator()776     public int getStatusIndicator() {
777         return mStatusIndicator;
778     }
779 
setStatusIndicator(int statusIndicator)780     public void setStatusIndicator(int statusIndicator) {
781         if (statusIndicator < 0 || statusIndicator > 0x1)
782             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
783         this.mStatusIndicator = statusIndicator;
784     }
785 
getStatusValue()786     public int getStatusValue() {
787         return mStatusValue;
788     }
789 
setStatusValue(int statusValue)790     public void setStatusValue(int statusValue) {
791         if (statusValue < 0 || statusValue > 0x1)
792             throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
793         this.mStatusValue = statusValue;
794     }
795 
getMseTime()796     public long getMseTime() {
797         return mMseTime;
798     }
799 
getMseTimeString()800     public String getMseTimeString() {
801         SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmssZ");
802         Date date = new Date(getMseTime());
803         return format.format(date); // Format to YYYYMMDDTHHMMSS±hhmm UTC time ± offset
804     }
805 
setMseTime(long mseTime)806     public void setMseTime(long mseTime) {
807         this.mMseTime = mseTime;
808     }
809 
setMseTime(String mseTime)810     public void setMseTime(String mseTime) throws ParseException {
811         SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmssZ");
812         Date date = format.parse(mseTime);
813         this.mMseTime = date.getTime();
814     }
815 }
816