• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007-2008 Esmertec AG.
3  * Copyright (C) 2007-2008 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.google.android.mms.pdu;
19 
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.text.TextUtils;
24 
25 import java.io.ByteArrayOutputStream;
26 import java.io.FileNotFoundException;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.util.Arrays;
30 import java.util.HashMap;
31 
32 public class PduComposer {
33     /**
34      * Address type.
35      */
36     static private final int PDU_PHONE_NUMBER_ADDRESS_TYPE = 1;
37     static private final int PDU_EMAIL_ADDRESS_TYPE = 2;
38     static private final int PDU_IPV4_ADDRESS_TYPE = 3;
39     static private final int PDU_IPV6_ADDRESS_TYPE = 4;
40     static private final int PDU_UNKNOWN_ADDRESS_TYPE = 5;
41 
42     /**
43      * Address regular expression string.
44      */
45     static final String REGEXP_PHONE_NUMBER_ADDRESS_TYPE = "\\+?[0-9|\\.|\\-]+";
46     static final String REGEXP_EMAIL_ADDRESS_TYPE = "[a-zA-Z| ]*\\<{0,1}[a-zA-Z| ]+@{1}" +
47             "[a-zA-Z| ]+\\.{1}[a-zA-Z| ]+\\>{0,1}";
48     static final String REGEXP_IPV6_ADDRESS_TYPE =
49         "[a-fA-F]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" +
50         "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" +
51         "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}";
52     static final String REGEXP_IPV4_ADDRESS_TYPE = "[0-9]{1,3}\\.{1}[0-9]{1,3}\\.{1}" +
53             "[0-9]{1,3}\\.{1}[0-9]{1,3}";
54 
55     /**
56      * The postfix strings of address.
57      */
58     static final String STRING_PHONE_NUMBER_ADDRESS_TYPE = "/TYPE=PLMN";
59     static final String STRING_IPV4_ADDRESS_TYPE = "/TYPE=IPV4";
60     static final String STRING_IPV6_ADDRESS_TYPE = "/TYPE=IPV6";
61 
62     /**
63      * Error values.
64      */
65     static private final int PDU_COMPOSE_SUCCESS = 0;
66     static private final int PDU_COMPOSE_CONTENT_ERROR = 1;
67     static private final int PDU_COMPOSE_FIELD_NOT_SET = 2;
68     static private final int PDU_COMPOSE_FIELD_NOT_SUPPORTED = 3;
69 
70     /**
71      * WAP values defined in WSP spec.
72      */
73     static private final int QUOTED_STRING_FLAG = 34;
74     static private final int END_STRING_FLAG = 0;
75     static private final int LENGTH_QUOTE = 31;
76     static private final int TEXT_MAX = 127;
77     static private final int SHORT_INTEGER_MAX = 127;
78     static private final int LONG_INTEGER_LENGTH_MAX = 8;
79 
80     /**
81      * Block size when read data from InputStream.
82      */
83     static private final int PDU_COMPOSER_BLOCK_SIZE = 1024;
84 
85     /**
86      * The output message.
87      */
88     @UnsupportedAppUsage
89     protected ByteArrayOutputStream mMessage = null;
90 
91     /**
92      * The PDU.
93      */
94     @UnsupportedAppUsage
95     private GenericPdu mPdu = null;
96 
97     /**
98      * Current visiting position of the mMessage.
99      */
100     @UnsupportedAppUsage
101     protected int mPosition = 0;
102 
103     /**
104      * Message compose buffer stack.
105      */
106     @UnsupportedAppUsage
107     private BufferStack mStack = null;
108 
109     /**
110      * Content resolver.
111      */
112     @UnsupportedAppUsage
113     private final ContentResolver mResolver;
114 
115     /**
116      * Header of this pdu.
117      */
118     @UnsupportedAppUsage
119     private PduHeaders mPduHeader = null;
120 
121     /**
122      * Map of all content type
123      */
124     @UnsupportedAppUsage
125     private static HashMap<String, Integer> mContentTypeMap = null;
126 
127     static {
128         mContentTypeMap = new HashMap<String, Integer>();
129 
130         int i;
131         for (i = 0; i < PduContentTypes.contentTypes.length; i++) {
mContentTypeMap.put(PduContentTypes.contentTypes[i], i)132             mContentTypeMap.put(PduContentTypes.contentTypes[i], i);
133         }
134     }
135 
136     /**
137      * Constructor.
138      *
139      * @param context the context
140      * @param pdu the pdu to be composed
141      */
142     @UnsupportedAppUsage
PduComposer(Context context, GenericPdu pdu)143     public PduComposer(Context context, GenericPdu pdu) {
144         mPdu = pdu;
145         mResolver = context.getContentResolver();
146         mPduHeader = pdu.getPduHeaders();
147         mStack = new BufferStack();
148         mMessage = new ByteArrayOutputStream();
149         mPosition = 0;
150     }
151 
152     /**
153      * Make the message. No need to check whether mandatory fields are set,
154      * because the constructors of outgoing pdus are taking care of this.
155      *
156      * @return OutputStream of maked message. Return null if
157      *         the PDU is invalid.
158      */
159     @UnsupportedAppUsage
make()160     public byte[] make() {
161         // Get Message-type.
162         int type = mPdu.getMessageType();
163 
164         /* make the message */
165         switch (type) {
166             case PduHeaders.MESSAGE_TYPE_SEND_REQ:
167             case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
168                 if (makeSendRetrievePdu(type) != PDU_COMPOSE_SUCCESS) {
169                     return null;
170                 }
171                 break;
172             case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
173                 if (makeNotifyResp() != PDU_COMPOSE_SUCCESS) {
174                     return null;
175                 }
176                 break;
177             case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
178                 if (makeAckInd() != PDU_COMPOSE_SUCCESS) {
179                     return null;
180                 }
181                 break;
182             case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
183                 if (makeReadRecInd() != PDU_COMPOSE_SUCCESS) {
184                     return null;
185                 }
186                 break;
187             default:
188                 return null;
189         }
190 
191         return mMessage.toByteArray();
192     }
193 
194     /**
195      *  Copy buf to mMessage.
196      */
197     @UnsupportedAppUsage
arraycopy(byte[] buf, int pos, int length)198     protected void arraycopy(byte[] buf, int pos, int length) {
199         mMessage.write(buf, pos, length);
200         mPosition = mPosition + length;
201     }
202 
203     /**
204      * Append a byte to mMessage.
205      */
append(int value)206     protected void append(int value) {
207         mMessage.write(value);
208         mPosition ++;
209     }
210 
211     /**
212      * Append short integer value to mMessage.
213      * This implementation doesn't check the validity of parameter, since it
214      * assumes that the values are validated in the GenericPdu setter methods.
215      */
216     @UnsupportedAppUsage
appendShortInteger(int value)217     protected void appendShortInteger(int value) {
218         /*
219          * From WAP-230-WSP-20010705-a:
220          * Short-integer = OCTET
221          * ; Integers in range 0-127 shall be encoded as a one octet value
222          * ; with the most significant bit set to one (1xxx xxxx) and with
223          * ; the value in the remaining least significant bits.
224          * In our implementation, only low 7 bits are stored and otherwise
225          * bits are ignored.
226          */
227         append((value | 0x80) & 0xff);
228     }
229 
230     /**
231      * Append an octet number between 128 and 255 into mMessage.
232      * NOTE:
233      * A value between 0 and 127 should be appended by using appendShortInteger.
234      * This implementation doesn't check the validity of parameter, since it
235      * assumes that the values are validated in the GenericPdu setter methods.
236      */
237     @UnsupportedAppUsage
appendOctet(int number)238     protected void appendOctet(int number) {
239         append(number);
240     }
241 
242     /**
243      * Append a short length into mMessage.
244      * This implementation doesn't check the validity of parameter, since it
245      * assumes that the values are validated in the GenericPdu setter methods.
246      */
appendShortLength(int value)247     protected void appendShortLength(int value) {
248         /*
249          * From WAP-230-WSP-20010705-a:
250          * Short-length = <Any octet 0-30>
251          */
252         append(value);
253     }
254 
255     /**
256      * Append long integer into mMessage. it's used for really long integers.
257      * This implementation doesn't check the validity of parameter, since it
258      * assumes that the values are validated in the GenericPdu setter methods.
259      */
260     @UnsupportedAppUsage
appendLongInteger(long longInt)261     protected void appendLongInteger(long longInt) {
262         /*
263          * From WAP-230-WSP-20010705-a:
264          * Long-integer = Short-length Multi-octet-integer
265          * ; The Short-length indicates the length of the Multi-octet-integer
266          * Multi-octet-integer = 1*30 OCTET
267          * ; The content octets shall be an unsigned integer value with the
268          * ; most significant octet encoded first (big-endian representation).
269          * ; The minimum number of octets must be used to encode the value.
270          */
271         int size;
272         long temp = longInt;
273 
274         // Count the length of the long integer.
275         for(size = 0; (temp != 0) && (size < LONG_INTEGER_LENGTH_MAX); size++) {
276             temp = (temp >>> 8);
277         }
278 
279         // Set Length.
280         appendShortLength(size);
281 
282         // Count and set the long integer.
283         int i;
284         int shift = (size -1) * 8;
285 
286         for (i = 0; i < size; i++) {
287             append((int)((longInt >>> shift) & 0xff));
288             shift = shift - 8;
289         }
290     }
291 
292     /**
293      * Append text string into mMessage.
294      * This implementation doesn't check the validity of parameter, since it
295      * assumes that the values are validated in the GenericPdu setter methods.
296      */
297     @UnsupportedAppUsage
appendTextString(byte[] text)298     protected void appendTextString(byte[] text) {
299         /*
300          * From WAP-230-WSP-20010705-a:
301          * Text-string = [Quote] *TEXT End-of-string
302          * ; If the first character in the TEXT is in the range of 128-255,
303          * ; a Quote character must precede it. Otherwise the Quote character
304          * ;must be omitted. The Quote is not part of the contents.
305          */
306         if (((text[0])&0xff) > TEXT_MAX) { // No need to check for <= 255
307             append(TEXT_MAX);
308         }
309 
310         arraycopy(text, 0, text.length);
311         append(0);
312     }
313 
314     /**
315      * Append text string into mMessage.
316      * This implementation doesn't check the validity of parameter, since it
317      * assumes that the values are validated in the GenericPdu setter methods.
318      */
319     @UnsupportedAppUsage
appendTextString(String str)320     protected void appendTextString(String str) {
321         /*
322          * From WAP-230-WSP-20010705-a:
323          * Text-string = [Quote] *TEXT End-of-string
324          * ; If the first character in the TEXT is in the range of 128-255,
325          * ; a Quote character must precede it. Otherwise the Quote character
326          * ;must be omitted. The Quote is not part of the contents.
327          */
328         appendTextString(str.getBytes());
329     }
330 
331     /**
332      * Append encoded string value to mMessage.
333      * This implementation doesn't check the validity of parameter, since it
334      * assumes that the values are validated in the GenericPdu setter methods.
335      */
336     @UnsupportedAppUsage
appendEncodedString(EncodedStringValue enStr)337     protected void appendEncodedString(EncodedStringValue enStr) {
338         /*
339          * From OMA-TS-MMS-ENC-V1_3-20050927-C:
340          * Encoded-string-value = Text-string | Value-length Char-set Text-string
341          */
342         assert(enStr != null);
343 
344         int charset = enStr.getCharacterSet();
345         byte[] textString = enStr.getTextString();
346         if (null == textString) {
347             return;
348         }
349 
350         /*
351          * In the implementation of EncodedStringValue, the charset field will
352          * never be 0. It will always be composed as
353          * Encoded-string-value = Value-length Char-set Text-string
354          */
355         mStack.newbuf();
356         PositionMarker start = mStack.mark();
357 
358         appendShortInteger(charset);
359         appendTextString(textString);
360 
361         int len = start.getLength();
362         mStack.pop();
363         appendValueLength(len);
364         mStack.copy();
365     }
366 
367     /**
368      * Append uintvar integer into mMessage.
369      * This implementation doesn't check the validity of parameter, since it
370      * assumes that the values are validated in the GenericPdu setter methods.
371      */
372     @UnsupportedAppUsage
appendUintvarInteger(long value)373     protected void appendUintvarInteger(long value) {
374         /*
375          * From WAP-230-WSP-20010705-a:
376          * To encode a large unsigned integer, split it into 7-bit fragments
377          * and place them in the payloads of multiple octets. The most significant
378          * bits are placed in the first octets with the least significant bits
379          * ending up in the last octet. All octets MUST set the Continue bit to 1
380          * except the last octet, which MUST set the Continue bit to 0.
381          */
382         int i;
383         long max = SHORT_INTEGER_MAX;
384 
385         for (i = 0; i < 5; i++) {
386             if (value < max) {
387                 break;
388             }
389 
390             max = (max << 7) | 0x7fl;
391         }
392 
393         while(i > 0) {
394             long temp = value >>> (i * 7);
395             temp = temp & 0x7f;
396 
397             append((int)((temp | 0x80) & 0xff));
398 
399             i--;
400         }
401 
402         append((int)(value & 0x7f));
403     }
404 
405     /**
406      * Append date value into mMessage.
407      * This implementation doesn't check the validity of parameter, since it
408      * assumes that the values are validated in the GenericPdu setter methods.
409      */
appendDateValue(long date)410     protected void appendDateValue(long date) {
411         /*
412          * From OMA-TS-MMS-ENC-V1_3-20050927-C:
413          * Date-value = Long-integer
414          */
415         appendLongInteger(date);
416     }
417 
418     /**
419      * Append value length to mMessage.
420      * This implementation doesn't check the validity of parameter, since it
421      * assumes that the values are validated in the GenericPdu setter methods.
422      */
423     @UnsupportedAppUsage
appendValueLength(long value)424     protected void appendValueLength(long value) {
425         /*
426          * From WAP-230-WSP-20010705-a:
427          * Value-length = Short-length | (Length-quote Length)
428          * ; Value length is used to indicate the length of the value to follow
429          * Short-length = <Any octet 0-30>
430          * Length-quote = <Octet 31>
431          * Length = Uintvar-integer
432          */
433         if (value < LENGTH_QUOTE) {
434             appendShortLength((int) value);
435             return;
436         }
437 
438         append(LENGTH_QUOTE);
439         appendUintvarInteger(value);
440     }
441 
442     /**
443      * Append quoted string to mMessage.
444      * This implementation doesn't check the validity of parameter, since it
445      * assumes that the values are validated in the GenericPdu setter methods.
446      */
447     @UnsupportedAppUsage
appendQuotedString(byte[] text)448     protected void appendQuotedString(byte[] text) {
449         /*
450          * From WAP-230-WSP-20010705-a:
451          * Quoted-string = <Octet 34> *TEXT End-of-string
452          * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing
453          * ;quotation-marks <"> removed.
454          */
455         append(QUOTED_STRING_FLAG);
456         arraycopy(text, 0, text.length);
457         append(END_STRING_FLAG);
458     }
459 
460     /**
461      * Append quoted string to mMessage.
462      * This implementation doesn't check the validity of parameter, since it
463      * assumes that the values are validated in the GenericPdu setter methods.
464      */
465     @UnsupportedAppUsage
appendQuotedString(String str)466     protected void appendQuotedString(String str) {
467         /*
468          * From WAP-230-WSP-20010705-a:
469          * Quoted-string = <Octet 34> *TEXT End-of-string
470          * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing
471          * ;quotation-marks <"> removed.
472          */
473         appendQuotedString(str.getBytes());
474     }
475 
appendAddressType(EncodedStringValue address)476     private EncodedStringValue appendAddressType(EncodedStringValue address) {
477         EncodedStringValue temp = null;
478 
479         try {
480             int addressType = checkAddressType(address.getString());
481             temp = EncodedStringValue.copy(address);
482             if (PDU_PHONE_NUMBER_ADDRESS_TYPE == addressType) {
483                 // Phone number.
484                 temp.appendTextString(STRING_PHONE_NUMBER_ADDRESS_TYPE.getBytes());
485             } else if (PDU_IPV4_ADDRESS_TYPE == addressType) {
486                 // Ipv4 address.
487                 temp.appendTextString(STRING_IPV4_ADDRESS_TYPE.getBytes());
488             } else if (PDU_IPV6_ADDRESS_TYPE == addressType) {
489                 // Ipv6 address.
490                 temp.appendTextString(STRING_IPV6_ADDRESS_TYPE.getBytes());
491             }
492         } catch (NullPointerException e) {
493             return null;
494         }
495 
496         return temp;
497     }
498 
499     /**
500      * Append header to mMessage.
501      */
502     @UnsupportedAppUsage
appendHeader(int field)503     private int appendHeader(int field) {
504         switch (field) {
505             case PduHeaders.MMS_VERSION:
506                 appendOctet(field);
507 
508                 int version = mPduHeader.getOctet(field);
509                 if (0 == version) {
510                     appendShortInteger(PduHeaders.CURRENT_MMS_VERSION);
511                 } else {
512                     appendShortInteger(version);
513                 }
514 
515                 break;
516 
517             case PduHeaders.MESSAGE_ID:
518             case PduHeaders.TRANSACTION_ID:
519                 byte[] textString = mPduHeader.getTextString(field);
520                 if (null == textString) {
521                     return PDU_COMPOSE_FIELD_NOT_SET;
522                 }
523 
524                 appendOctet(field);
525                 appendTextString(textString);
526                 break;
527 
528             case PduHeaders.TO:
529             case PduHeaders.BCC:
530             case PduHeaders.CC:
531                 EncodedStringValue[] addr = mPduHeader.getEncodedStringValues(field);
532 
533                 if (null == addr) {
534                     return PDU_COMPOSE_FIELD_NOT_SET;
535                 }
536 
537                 EncodedStringValue temp;
538                 for (int i = 0; i < addr.length; i++) {
539                     temp = appendAddressType(addr[i]);
540                     if (temp == null) {
541                         return PDU_COMPOSE_CONTENT_ERROR;
542                     }
543 
544                     appendOctet(field);
545                     appendEncodedString(temp);
546                 }
547                 break;
548 
549             case PduHeaders.FROM:
550                 // Value-length (Address-present-token Encoded-string-value | Insert-address-token)
551                 appendOctet(field);
552 
553                 EncodedStringValue from = mPduHeader.getEncodedStringValue(field);
554                 if ((from == null)
555                         || TextUtils.isEmpty(from.getString())
556                         || new String(from.getTextString()).equals(
557                                 PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR)) {
558                     // Length of from = 1
559                     append(1);
560                     // Insert-address-token = <Octet 129>
561                     append(PduHeaders.FROM_INSERT_ADDRESS_TOKEN);
562                 } else {
563                     mStack.newbuf();
564                     PositionMarker fstart = mStack.mark();
565 
566                     // Address-present-token = <Octet 128>
567                     append(PduHeaders.FROM_ADDRESS_PRESENT_TOKEN);
568 
569                     temp = appendAddressType(from);
570                     if (temp == null) {
571                         return PDU_COMPOSE_CONTENT_ERROR;
572                     }
573 
574                     appendEncodedString(temp);
575 
576                     int flen = fstart.getLength();
577                     mStack.pop();
578                     appendValueLength(flen);
579                     mStack.copy();
580                 }
581                 break;
582 
583             case PduHeaders.READ_STATUS:
584             case PduHeaders.STATUS:
585             case PduHeaders.REPORT_ALLOWED:
586             case PduHeaders.PRIORITY:
587             case PduHeaders.DELIVERY_REPORT:
588             case PduHeaders.READ_REPORT:
589             case PduHeaders.RETRIEVE_STATUS:
590                 int octet = mPduHeader.getOctet(field);
591                 if (0 == octet) {
592                     return PDU_COMPOSE_FIELD_NOT_SET;
593                 }
594 
595                 appendOctet(field);
596                 appendOctet(octet);
597                 break;
598 
599             case PduHeaders.DATE:
600                 long date = mPduHeader.getLongInteger(field);
601                 if (-1 == date) {
602                     return PDU_COMPOSE_FIELD_NOT_SET;
603                 }
604 
605                 appendOctet(field);
606                 appendDateValue(date);
607                 break;
608 
609             case PduHeaders.SUBJECT:
610             case PduHeaders.RETRIEVE_TEXT:
611                 EncodedStringValue enString =
612                     mPduHeader.getEncodedStringValue(field);
613                 if (null == enString) {
614                     return PDU_COMPOSE_FIELD_NOT_SET;
615                 }
616 
617                 appendOctet(field);
618                 appendEncodedString(enString);
619                 break;
620 
621             case PduHeaders.MESSAGE_CLASS:
622                 byte[] messageClass = mPduHeader.getTextString(field);
623                 if (null == messageClass) {
624                     return PDU_COMPOSE_FIELD_NOT_SET;
625                 }
626 
627                 appendOctet(field);
628                 if (Arrays.equals(messageClass,
629                         PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes())) {
630                     appendOctet(PduHeaders.MESSAGE_CLASS_ADVERTISEMENT);
631                 } else if (Arrays.equals(messageClass,
632                         PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes())) {
633                     appendOctet(PduHeaders.MESSAGE_CLASS_AUTO);
634                 } else if (Arrays.equals(messageClass,
635                         PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes())) {
636                     appendOctet(PduHeaders.MESSAGE_CLASS_PERSONAL);
637                 } else if (Arrays.equals(messageClass,
638                         PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes())) {
639                     appendOctet(PduHeaders.MESSAGE_CLASS_INFORMATIONAL);
640                 } else {
641                     appendTextString(messageClass);
642                 }
643                 break;
644 
645             case PduHeaders.EXPIRY:
646                 long expiry = mPduHeader.getLongInteger(field);
647                 if (-1 == expiry) {
648                     return PDU_COMPOSE_FIELD_NOT_SET;
649                 }
650 
651                 appendOctet(field);
652 
653                 mStack.newbuf();
654                 PositionMarker expiryStart = mStack.mark();
655 
656                 append(PduHeaders.VALUE_RELATIVE_TOKEN);
657                 appendLongInteger(expiry);
658 
659                 int expiryLength = expiryStart.getLength();
660                 mStack.pop();
661                 appendValueLength(expiryLength);
662                 mStack.copy();
663                 break;
664 
665             default:
666                 return PDU_COMPOSE_FIELD_NOT_SUPPORTED;
667         }
668 
669         return PDU_COMPOSE_SUCCESS;
670     }
671 
672     /**
673      * Make ReadRec.Ind.
674      */
makeReadRecInd()675     private int makeReadRecInd() {
676         if (mMessage == null) {
677             mMessage = new ByteArrayOutputStream();
678             mPosition = 0;
679         }
680 
681         // X-Mms-Message-Type
682         appendOctet(PduHeaders.MESSAGE_TYPE);
683         appendOctet(PduHeaders.MESSAGE_TYPE_READ_REC_IND);
684 
685         // X-Mms-MMS-Version
686         if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
687             return PDU_COMPOSE_CONTENT_ERROR;
688         }
689 
690         // Message-ID
691         if (appendHeader(PduHeaders.MESSAGE_ID) != PDU_COMPOSE_SUCCESS) {
692             return PDU_COMPOSE_CONTENT_ERROR;
693         }
694 
695         // To
696         if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_SUCCESS) {
697             return PDU_COMPOSE_CONTENT_ERROR;
698         }
699 
700         // From
701         if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) {
702             return PDU_COMPOSE_CONTENT_ERROR;
703         }
704 
705         // Date Optional
706         appendHeader(PduHeaders.DATE);
707 
708         // X-Mms-Read-Status
709         if (appendHeader(PduHeaders.READ_STATUS) != PDU_COMPOSE_SUCCESS) {
710             return PDU_COMPOSE_CONTENT_ERROR;
711         }
712 
713         // X-Mms-Applic-ID Optional(not support)
714         // X-Mms-Reply-Applic-ID Optional(not support)
715         // X-Mms-Aux-Applic-Info Optional(not support)
716 
717         return PDU_COMPOSE_SUCCESS;
718     }
719 
720     /**
721      * Make NotifyResp.Ind.
722      */
makeNotifyResp()723     private int makeNotifyResp() {
724         if (mMessage == null) {
725             mMessage = new ByteArrayOutputStream();
726             mPosition = 0;
727         }
728 
729         //    X-Mms-Message-Type
730         appendOctet(PduHeaders.MESSAGE_TYPE);
731         appendOctet(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND);
732 
733         // X-Mms-Transaction-ID
734         if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) {
735             return PDU_COMPOSE_CONTENT_ERROR;
736         }
737 
738         // X-Mms-MMS-Version
739         if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
740             return PDU_COMPOSE_CONTENT_ERROR;
741         }
742 
743         //  X-Mms-Status
744         if (appendHeader(PduHeaders.STATUS) != PDU_COMPOSE_SUCCESS) {
745             return PDU_COMPOSE_CONTENT_ERROR;
746         }
747 
748         // X-Mms-Report-Allowed Optional (not support)
749         return PDU_COMPOSE_SUCCESS;
750     }
751 
752     /**
753      * Make Acknowledge.Ind.
754      */
makeAckInd()755     private int makeAckInd() {
756         if (mMessage == null) {
757             mMessage = new ByteArrayOutputStream();
758             mPosition = 0;
759         }
760 
761         //    X-Mms-Message-Type
762         appendOctet(PduHeaders.MESSAGE_TYPE);
763         appendOctet(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND);
764 
765         // X-Mms-Transaction-ID
766         if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) {
767             return PDU_COMPOSE_CONTENT_ERROR;
768         }
769 
770         //     X-Mms-MMS-Version
771         if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
772             return PDU_COMPOSE_CONTENT_ERROR;
773         }
774 
775         // X-Mms-Report-Allowed Optional
776         appendHeader(PduHeaders.REPORT_ALLOWED);
777 
778         return PDU_COMPOSE_SUCCESS;
779     }
780 
781     /**
782      * Make Send.req.
783      */
makeSendRetrievePdu(int type)784     private int makeSendRetrievePdu(int type) {
785         if (mMessage == null) {
786             mMessage = new ByteArrayOutputStream();
787             mPosition = 0;
788         }
789 
790         // X-Mms-Message-Type
791         appendOctet(PduHeaders.MESSAGE_TYPE);
792         appendOctet(type);
793 
794         // X-Mms-Transaction-ID
795         appendOctet(PduHeaders.TRANSACTION_ID);
796 
797         byte[] trid = mPduHeader.getTextString(PduHeaders.TRANSACTION_ID);
798         if (trid == null) {
799             // Transaction-ID should be set(by Transaction) before make().
800             throw new IllegalArgumentException("Transaction-ID is null.");
801         }
802         appendTextString(trid);
803 
804         //  X-Mms-MMS-Version
805         if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
806             return PDU_COMPOSE_CONTENT_ERROR;
807         }
808 
809         // Date Date-value Optional.
810         appendHeader(PduHeaders.DATE);
811 
812         // From
813         if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) {
814             return PDU_COMPOSE_CONTENT_ERROR;
815         }
816 
817         boolean recipient = false;
818 
819         // To
820         if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_CONTENT_ERROR) {
821             recipient = true;
822         }
823 
824         // Cc
825         if (appendHeader(PduHeaders.CC) != PDU_COMPOSE_CONTENT_ERROR) {
826             recipient = true;
827         }
828 
829         // Bcc
830         if (appendHeader(PduHeaders.BCC) != PDU_COMPOSE_CONTENT_ERROR) {
831             recipient = true;
832         }
833 
834         // Need at least one of "cc", "bcc" and "to".
835         if (false == recipient) {
836             return PDU_COMPOSE_CONTENT_ERROR;
837         }
838 
839         // Subject Optional
840         appendHeader(PduHeaders.SUBJECT);
841 
842         // X-Mms-Message-Class Optional
843         // Message-class-value = Class-identifier | Token-text
844         appendHeader(PduHeaders.MESSAGE_CLASS);
845 
846         // X-Mms-Expiry Optional
847         appendHeader(PduHeaders.EXPIRY);
848 
849         // X-Mms-Priority Optional
850         appendHeader(PduHeaders.PRIORITY);
851 
852         // X-Mms-Delivery-Report Optional
853         appendHeader(PduHeaders.DELIVERY_REPORT);
854 
855         // X-Mms-Read-Report Optional
856         appendHeader(PduHeaders.READ_REPORT);
857 
858         if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
859             // X-Mms-Retrieve-Status Optional
860             appendHeader(PduHeaders.RETRIEVE_STATUS);
861             // X-Mms-Retrieve-Text Optional
862             appendHeader(PduHeaders.RETRIEVE_TEXT);
863         }
864 
865 
866         //    Content-Type
867         appendOctet(PduHeaders.CONTENT_TYPE);
868 
869         //  Message body
870         return makeMessageBody(type);
871     }
872 
873     /**
874      * Make message body.
875      */
makeMessageBody(int type)876     private int makeMessageBody(int type) {
877         // 1. add body informations
878         mStack.newbuf();  // Switching buffer because we need to
879 
880         PositionMarker ctStart = mStack.mark();
881 
882         // This contentTypeIdentifier should be used for type of attachment...
883         String contentType = new String(mPduHeader.getTextString(PduHeaders.CONTENT_TYPE));
884         Integer contentTypeIdentifier = mContentTypeMap.get(contentType);
885         if (contentTypeIdentifier == null) {
886             // content type is mandatory
887             return PDU_COMPOSE_CONTENT_ERROR;
888         }
889 
890         appendShortInteger(contentTypeIdentifier.intValue());
891 
892         // content-type parameter: start
893         PduBody body;
894         if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
895             body = ((RetrieveConf) mPdu).getBody();
896         } else {
897             body = ((SendReq) mPdu).getBody();
898         }
899         if (null == body || body.getPartsNum() == 0) {
900             // empty message
901             appendUintvarInteger(0);
902             mStack.pop();
903             mStack.copy();
904             return PDU_COMPOSE_SUCCESS;
905         }
906 
907         PduPart part;
908         try {
909             part = body.getPart(0);
910 
911             byte[] start = part.getContentId();
912             if (start != null) {
913                 appendOctet(PduPart.P_DEP_START);
914                 if (('<' == start[0]) && ('>' == start[start.length - 1])) {
915                     appendTextString(start);
916                 } else {
917                     appendTextString("<" + new String(start) + ">");
918                 }
919             }
920 
921             // content-type parameter: type
922             appendOctet(PduPart.P_CT_MR_TYPE);
923             appendTextString(part.getContentType());
924         }
925         catch (ArrayIndexOutOfBoundsException e){
926             e.printStackTrace();
927         }
928 
929         int ctLength = ctStart.getLength();
930         mStack.pop();
931         appendValueLength(ctLength);
932         mStack.copy();
933 
934         // 3. add content
935         int partNum = body.getPartsNum();
936         appendUintvarInteger(partNum);
937         for (int i = 0; i < partNum; i++) {
938             part = body.getPart(i);
939             mStack.newbuf();  // Leaving space for header lengh and data length
940             PositionMarker attachment = mStack.mark();
941 
942             mStack.newbuf();  // Leaving space for Content-Type length
943             PositionMarker contentTypeBegin = mStack.mark();
944 
945             byte[] partContentType = part.getContentType();
946 
947             if (partContentType == null) {
948                 // content type is mandatory
949                 return PDU_COMPOSE_CONTENT_ERROR;
950             }
951 
952             // content-type value
953             Integer partContentTypeIdentifier =
954                 mContentTypeMap.get(new String(partContentType));
955             if (partContentTypeIdentifier == null) {
956                 appendTextString(partContentType);
957             } else {
958                 appendShortInteger(partContentTypeIdentifier.intValue());
959             }
960 
961             /* Content-type parameter : name.
962              * The value of name, filename, content-location is the same.
963              * Just one of them is enough for this PDU.
964              */
965             byte[] name = part.getName();
966 
967             if (null == name) {
968                 name = part.getFilename();
969 
970                 if (null == name) {
971                     name = part.getContentLocation();
972 
973                     if (null == name) {
974                         /* at lease one of name, filename, Content-location
975                          * should be available.
976                          */
977                         return PDU_COMPOSE_CONTENT_ERROR;
978                     }
979                 }
980             }
981             appendOctet(PduPart.P_DEP_NAME);
982             appendTextString(name);
983 
984             // content-type parameter : charset
985             int charset = part.getCharset();
986             if (charset != 0) {
987                 appendOctet(PduPart.P_CHARSET);
988                 appendShortInteger(charset);
989             }
990 
991             int contentTypeLength = contentTypeBegin.getLength();
992             mStack.pop();
993             appendValueLength(contentTypeLength);
994             mStack.copy();
995 
996             // content id
997             byte[] contentId = part.getContentId();
998 
999             if (null != contentId) {
1000                 appendOctet(PduPart.P_CONTENT_ID);
1001                 if (('<' == contentId[0]) && ('>' == contentId[contentId.length - 1])) {
1002                     appendQuotedString(contentId);
1003                 } else {
1004                     appendQuotedString("<" + new String(contentId) + ">");
1005                 }
1006             }
1007 
1008             // content-location
1009             byte[] contentLocation = part.getContentLocation();
1010             if (null != contentLocation) {
1011             	appendOctet(PduPart.P_CONTENT_LOCATION);
1012             	appendTextString(contentLocation);
1013             }
1014 
1015             // content
1016             int headerLength = attachment.getLength();
1017 
1018             int dataLength = 0; // Just for safety...
1019             byte[] partData = part.getData();
1020 
1021             if (partData != null) {
1022                 arraycopy(partData, 0, partData.length);
1023                 dataLength = partData.length;
1024             } else {
1025                 InputStream cr = null;
1026                 try {
1027                     byte[] buffer = new byte[PDU_COMPOSER_BLOCK_SIZE];
1028                     cr = mResolver.openInputStream(part.getDataUri());
1029                     int len = 0;
1030                     while ((len = cr.read(buffer)) != -1) {
1031                         mMessage.write(buffer, 0, len);
1032                         mPosition += len;
1033                         dataLength += len;
1034                     }
1035                 } catch (FileNotFoundException e) {
1036                     return PDU_COMPOSE_CONTENT_ERROR;
1037                 } catch (IOException e) {
1038                     return PDU_COMPOSE_CONTENT_ERROR;
1039                 } catch (RuntimeException e) {
1040                     return PDU_COMPOSE_CONTENT_ERROR;
1041                 } finally {
1042                     if (cr != null) {
1043                         try {
1044                             cr.close();
1045                         } catch (IOException e) {
1046                         }
1047                     }
1048                 }
1049             }
1050 
1051             if (dataLength != (attachment.getLength() - headerLength)) {
1052                 throw new RuntimeException("BUG: Length sanity check failed");
1053             }
1054 
1055             mStack.pop();
1056             appendUintvarInteger(headerLength);
1057             appendUintvarInteger(dataLength);
1058             mStack.copy();
1059         }
1060 
1061         return PDU_COMPOSE_SUCCESS;
1062     }
1063 
1064     /**
1065      *  Record current message informations.
1066      */
1067     static private class LengthRecordNode {
1068         ByteArrayOutputStream currentMessage = null;
1069         public int currentPosition = 0;
1070 
1071         public LengthRecordNode next = null;
1072     }
1073 
1074     /**
1075      * Mark current message position and stact size.
1076      */
1077     private class PositionMarker {
1078         private int c_pos;   // Current position
1079         private int currentStackSize;  // Current stack size
1080 
1081         @UnsupportedAppUsage
getLength()1082         int getLength() {
1083             // If these assert fails, likely that you are finding the
1084             // size of buffer that is deep in BufferStack you can only
1085             // find the length of the buffer that is on top
1086             if (currentStackSize != mStack.stackSize) {
1087                 throw new RuntimeException("BUG: Invalid call to getLength()");
1088             }
1089 
1090             return mPosition - c_pos;
1091         }
1092     }
1093 
1094     /**
1095      * This implementation can be OPTIMIZED to use only
1096      * 2 buffers. This optimization involves changing BufferStack
1097      * only... Its usage (interface) will not change.
1098      */
1099     private class BufferStack {
1100         private LengthRecordNode stack = null;
1101         private LengthRecordNode toCopy = null;
1102 
1103         int stackSize = 0;
1104 
1105         /**
1106          *  Create a new message buffer and push it into the stack.
1107          */
1108         @UnsupportedAppUsage
newbuf()1109         void newbuf() {
1110             // You can't create a new buff when toCopy != null
1111             // That is after calling pop() and before calling copy()
1112             // If you do, it is a bug
1113             if (toCopy != null) {
1114                 throw new RuntimeException("BUG: Invalid newbuf() before copy()");
1115             }
1116 
1117             LengthRecordNode temp = new LengthRecordNode();
1118 
1119             temp.currentMessage = mMessage;
1120             temp.currentPosition = mPosition;
1121 
1122             temp.next = stack;
1123             stack = temp;
1124 
1125             stackSize = stackSize + 1;
1126 
1127             mMessage = new ByteArrayOutputStream();
1128             mPosition = 0;
1129         }
1130 
1131         /**
1132          *  Pop the message before and record current message in the stack.
1133          */
1134         @UnsupportedAppUsage
pop()1135         void pop() {
1136             ByteArrayOutputStream currentMessage = mMessage;
1137             int currentPosition = mPosition;
1138 
1139             mMessage = stack.currentMessage;
1140             mPosition = stack.currentPosition;
1141 
1142             toCopy = stack;
1143             // Re using the top element of the stack to avoid memory allocation
1144 
1145             stack = stack.next;
1146             stackSize = stackSize - 1;
1147 
1148             toCopy.currentMessage = currentMessage;
1149             toCopy.currentPosition = currentPosition;
1150         }
1151 
1152         /**
1153          *  Append current message to the message before.
1154          */
1155         @UnsupportedAppUsage
copy()1156         void copy() {
1157             arraycopy(toCopy.currentMessage.toByteArray(), 0,
1158                     toCopy.currentPosition);
1159 
1160             toCopy = null;
1161         }
1162 
1163         /**
1164          *  Mark current message position
1165          */
1166         @UnsupportedAppUsage
mark()1167         PositionMarker mark() {
1168             PositionMarker m = new PositionMarker();
1169 
1170             m.c_pos = mPosition;
1171             m.currentStackSize = stackSize;
1172 
1173             return m;
1174         }
1175     }
1176 
1177     /**
1178      * Check address type.
1179      *
1180      * @param address address string without the postfix stinng type,
1181      *        such as "/TYPE=PLMN", "/TYPE=IPv6" and "/TYPE=IPv4"
1182      * @return PDU_PHONE_NUMBER_ADDRESS_TYPE if it is phone number,
1183      *         PDU_EMAIL_ADDRESS_TYPE if it is email address,
1184      *         PDU_IPV4_ADDRESS_TYPE if it is ipv4 address,
1185      *         PDU_IPV6_ADDRESS_TYPE if it is ipv6 address,
1186      *         PDU_UNKNOWN_ADDRESS_TYPE if it is unknown.
1187      */
checkAddressType(String address)1188     protected static int checkAddressType(String address) {
1189         /**
1190          * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf, section 8.
1191          * address = ( e-mail / device-address / alphanum-shortcode / num-shortcode)
1192          * e-mail = mailbox; to the definition of mailbox as described in
1193          * section 3.4 of [RFC2822], but excluding the
1194          * obsolete definitions as indicated by the "obs-" prefix.
1195          * device-address = ( global-phone-number "/TYPE=PLMN" )
1196          * / ( ipv4 "/TYPE=IPv4" ) / ( ipv6 "/TYPE=IPv6" )
1197          * / ( escaped-value "/TYPE=" address-type )
1198          *
1199          * global-phone-number = ["+"] 1*( DIGIT / written-sep )
1200          * written-sep =("-"/".")
1201          *
1202          * ipv4 = 1*3DIGIT 3( "." 1*3DIGIT ) ; IPv4 address value
1203          *
1204          * ipv6 = 4HEXDIG 7( ":" 4HEXDIG ) ; IPv6 address per RFC 2373
1205          */
1206 
1207         if (null == address) {
1208             return PDU_UNKNOWN_ADDRESS_TYPE;
1209         }
1210 
1211         if (address.matches(REGEXP_IPV4_ADDRESS_TYPE)) {
1212             // Ipv4 address.
1213             return PDU_IPV4_ADDRESS_TYPE;
1214         }else if (address.matches(REGEXP_PHONE_NUMBER_ADDRESS_TYPE)) {
1215             // Phone number.
1216             return PDU_PHONE_NUMBER_ADDRESS_TYPE;
1217         } else if (address.matches(REGEXP_EMAIL_ADDRESS_TYPE)) {
1218             // Email address.
1219             return PDU_EMAIL_ADDRESS_TYPE;
1220         } else if (address.matches(REGEXP_IPV6_ADDRESS_TYPE)) {
1221             // Ipv6 address.
1222             return PDU_IPV6_ADDRESS_TYPE;
1223         } else {
1224             // Unknown address.
1225             return PDU_UNKNOWN_ADDRESS_TYPE;
1226         }
1227     }
1228 }
1229