diff --git a/samples/ApiDemos/Android.mk b/samples/ApiDemos/Android.mk index 0d8a7a829..a70a84687 100644 --- a/samples/ApiDemos/Android.mk +++ b/samples/ApiDemos/Android.mk @@ -10,8 +10,6 @@ LOCAL_SRC_FILES := \ src/com/example/android/apis/app/IRemoteServiceCallback.aidl \ src/com/example/android/apis/app/ISecondary.aidl \ -LOCAL_JAVA_LIBRARIES := telephony-common - LOCAL_STATIC_ANDROID_LIBRARIES += \ android-support-v4 diff --git a/samples/ApiDemos/src/com/example/android/apis/os/MmsMessagingDemo.java b/samples/ApiDemos/src/com/example/android/apis/os/MmsMessagingDemo.java index 0b66d6f5b..3217f263e 100644 --- a/samples/ApiDemos/src/com/example/android/apis/os/MmsMessagingDemo.java +++ b/samples/ApiDemos/src/com/example/android/apis/os/MmsMessagingDemo.java @@ -16,19 +16,19 @@ package com.example.android.apis.os; -import com.google.android.mms.ContentType; -import com.google.android.mms.InvalidHeaderValueException; -import com.google.android.mms.pdu.CharacterSets; -import com.google.android.mms.pdu.EncodedStringValue; -import com.google.android.mms.pdu.GenericPdu; -import com.google.android.mms.pdu.PduBody; -import com.google.android.mms.pdu.PduComposer; -import com.google.android.mms.pdu.PduHeaders; -import com.google.android.mms.pdu.PduParser; -import com.google.android.mms.pdu.PduPart; -import com.google.android.mms.pdu.RetrieveConf; -import com.google.android.mms.pdu.SendConf; -import com.google.android.mms.pdu.SendReq; +import com.example.android.mmslib.ContentType; +import com.example.android.mmslib.InvalidHeaderValueException; +import com.example.android.mmslib.pdu.CharacterSets; +import com.example.android.mmslib.pdu.EncodedStringValue; +import com.example.android.mmslib.pdu.GenericPdu; +import com.example.android.mmslib.pdu.PduBody; +import com.example.android.mmslib.pdu.PduComposer; +import com.example.android.mmslib.pdu.PduHeaders; +import com.example.android.mmslib.pdu.PduParser; +import com.example.android.mmslib.pdu.PduPart; +import com.example.android.mmslib.pdu.RetrieveConf; +import com.example.android.mmslib.pdu.SendConf; +import com.example.android.mmslib.pdu.SendReq; import android.app.Activity; import android.app.PendingIntent; diff --git a/samples/ApiDemos/src/com/example/android/apis/os/MmsWapPushReceiver.java b/samples/ApiDemos/src/com/example/android/apis/os/MmsWapPushReceiver.java index f2ca090b0..b2bcbe85e 100644 --- a/samples/ApiDemos/src/com/example/android/apis/os/MmsWapPushReceiver.java +++ b/samples/ApiDemos/src/com/example/android/apis/os/MmsWapPushReceiver.java @@ -22,11 +22,11 @@ import android.content.Intent; import android.provider.Telephony; import android.util.Log; -import com.google.android.mms.ContentType; -import com.google.android.mms.pdu.GenericPdu; -import com.google.android.mms.pdu.NotificationInd; -import com.google.android.mms.pdu.PduHeaders; -import com.google.android.mms.pdu.PduParser; +import com.example.android.mmslib.ContentType; +import com.example.android.mmslib.pdu.GenericPdu; +import com.example.android.mmslib.pdu.NotificationInd; +import com.example.android.mmslib.pdu.PduHeaders; +import com.example.android.mmslib.pdu.PduParser; /** * Receiver for MMS WAP push diff --git a/samples/ApiDemos/src/com/example/android/mmslib/ContentType.java b/samples/ApiDemos/src/com/example/android/mmslib/ContentType.java new file mode 100644 index 000000000..a681367fe --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/ContentType.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib; + +import java.util.ArrayList; + +public class ContentType { + public static final String MMS_MESSAGE = "application/vnd.wap.mms-message"; + // The phony content type for generic PDUs (e.g. ReadOrig.ind, + // Notification.ind, Delivery.ind). + public static final String MMS_GENERIC = "application/vnd.wap.mms-generic"; + public static final String MULTIPART_MIXED = "application/vnd.wap.multipart.mixed"; + public static final String MULTIPART_RELATED = "application/vnd.wap.multipart.related"; + public static final String MULTIPART_ALTERNATIVE = "application/vnd.wap.multipart.alternative"; + + public static final String TEXT_PLAIN = "text/plain"; + public static final String TEXT_HTML = "text/html"; + public static final String TEXT_VCALENDAR = "text/x-vCalendar"; + public static final String TEXT_VCARD = "text/x-vCard"; + + public static final String IMAGE_UNSPECIFIED = "image/*"; + public static final String IMAGE_JPEG = "image/jpeg"; + public static final String IMAGE_JPG = "image/jpg"; + public static final String IMAGE_GIF = "image/gif"; + public static final String IMAGE_WBMP = "image/vnd.wap.wbmp"; + public static final String IMAGE_PNG = "image/png"; + public static final String IMAGE_X_MS_BMP = "image/x-ms-bmp"; + + public static final String AUDIO_UNSPECIFIED = "audio/*"; + public static final String AUDIO_AAC = "audio/aac"; + public static final String AUDIO_AMR = "audio/amr"; + public static final String AUDIO_IMELODY = "audio/imelody"; + public static final String AUDIO_MID = "audio/mid"; + public static final String AUDIO_MIDI = "audio/midi"; + public static final String AUDIO_MP3 = "audio/mp3"; + public static final String AUDIO_MPEG3 = "audio/mpeg3"; + public static final String AUDIO_MPEG = "audio/mpeg"; + public static final String AUDIO_MPG = "audio/mpg"; + public static final String AUDIO_MP4 = "audio/mp4"; + public static final String AUDIO_X_MID = "audio/x-mid"; + public static final String AUDIO_X_MIDI = "audio/x-midi"; + public static final String AUDIO_X_MP3 = "audio/x-mp3"; + public static final String AUDIO_X_MPEG3 = "audio/x-mpeg3"; + public static final String AUDIO_X_MPEG = "audio/x-mpeg"; + public static final String AUDIO_X_MPG = "audio/x-mpg"; + public static final String AUDIO_3GPP = "audio/3gpp"; + public static final String AUDIO_X_WAV = "audio/x-wav"; + public static final String AUDIO_OGG = "application/ogg"; + + public static final String VIDEO_UNSPECIFIED = "video/*"; + public static final String VIDEO_3GPP = "video/3gpp"; + public static final String VIDEO_3G2 = "video/3gpp2"; + public static final String VIDEO_H263 = "video/h263"; + public static final String VIDEO_MP4 = "video/mp4"; + + public static final String APP_SMIL = "application/smil"; + public static final String APP_WAP_XHTML = "application/vnd.wap.xhtml+xml"; + public static final String APP_XHTML = "application/xhtml+xml"; + + public static final String APP_DRM_CONTENT = "application/vnd.oma.drm.content"; + public static final String APP_DRM_MESSAGE = "application/vnd.oma.drm.message"; + + private static final ArrayList sSupportedContentTypes = new ArrayList(); + private static final ArrayList sSupportedImageTypes = new ArrayList(); + private static final ArrayList sSupportedAudioTypes = new ArrayList(); + private static final ArrayList sSupportedVideoTypes = new ArrayList(); + + static { + sSupportedContentTypes.add(TEXT_PLAIN); + sSupportedContentTypes.add(TEXT_HTML); + sSupportedContentTypes.add(TEXT_VCALENDAR); + sSupportedContentTypes.add(TEXT_VCARD); + + sSupportedContentTypes.add(IMAGE_JPEG); + sSupportedContentTypes.add(IMAGE_GIF); + sSupportedContentTypes.add(IMAGE_WBMP); + sSupportedContentTypes.add(IMAGE_PNG); + sSupportedContentTypes.add(IMAGE_JPG); + sSupportedContentTypes.add(IMAGE_X_MS_BMP); + //supportedContentTypes.add(IMAGE_SVG); not yet supported. + + sSupportedContentTypes.add(AUDIO_AAC); + sSupportedContentTypes.add(AUDIO_AMR); + sSupportedContentTypes.add(AUDIO_IMELODY); + sSupportedContentTypes.add(AUDIO_MID); + sSupportedContentTypes.add(AUDIO_MIDI); + sSupportedContentTypes.add(AUDIO_MP3); + sSupportedContentTypes.add(AUDIO_MP4); + sSupportedContentTypes.add(AUDIO_MPEG3); + sSupportedContentTypes.add(AUDIO_MPEG); + sSupportedContentTypes.add(AUDIO_MPG); + sSupportedContentTypes.add(AUDIO_X_MID); + sSupportedContentTypes.add(AUDIO_X_MIDI); + sSupportedContentTypes.add(AUDIO_X_MP3); + sSupportedContentTypes.add(AUDIO_X_MPEG3); + sSupportedContentTypes.add(AUDIO_X_MPEG); + sSupportedContentTypes.add(AUDIO_X_MPG); + sSupportedContentTypes.add(AUDIO_X_WAV); + sSupportedContentTypes.add(AUDIO_3GPP); + sSupportedContentTypes.add(AUDIO_OGG); + + sSupportedContentTypes.add(VIDEO_3GPP); + sSupportedContentTypes.add(VIDEO_3G2); + sSupportedContentTypes.add(VIDEO_H263); + sSupportedContentTypes.add(VIDEO_MP4); + + sSupportedContentTypes.add(APP_SMIL); + sSupportedContentTypes.add(APP_WAP_XHTML); + sSupportedContentTypes.add(APP_XHTML); + + sSupportedContentTypes.add(APP_DRM_CONTENT); + sSupportedContentTypes.add(APP_DRM_MESSAGE); + + // add supported image types + sSupportedImageTypes.add(IMAGE_JPEG); + sSupportedImageTypes.add(IMAGE_GIF); + sSupportedImageTypes.add(IMAGE_WBMP); + sSupportedImageTypes.add(IMAGE_PNG); + sSupportedImageTypes.add(IMAGE_JPG); + sSupportedImageTypes.add(IMAGE_X_MS_BMP); + + // add supported audio types + sSupportedAudioTypes.add(AUDIO_AAC); + sSupportedAudioTypes.add(AUDIO_AMR); + sSupportedAudioTypes.add(AUDIO_IMELODY); + sSupportedAudioTypes.add(AUDIO_MID); + sSupportedAudioTypes.add(AUDIO_MIDI); + sSupportedAudioTypes.add(AUDIO_MP3); + sSupportedAudioTypes.add(AUDIO_MPEG3); + sSupportedAudioTypes.add(AUDIO_MPEG); + sSupportedAudioTypes.add(AUDIO_MPG); + sSupportedAudioTypes.add(AUDIO_MP4); + sSupportedAudioTypes.add(AUDIO_X_MID); + sSupportedAudioTypes.add(AUDIO_X_MIDI); + sSupportedAudioTypes.add(AUDIO_X_MP3); + sSupportedAudioTypes.add(AUDIO_X_MPEG3); + sSupportedAudioTypes.add(AUDIO_X_MPEG); + sSupportedAudioTypes.add(AUDIO_X_MPG); + sSupportedAudioTypes.add(AUDIO_X_WAV); + sSupportedAudioTypes.add(AUDIO_3GPP); + sSupportedAudioTypes.add(AUDIO_OGG); + + // add supported video types + sSupportedVideoTypes.add(VIDEO_3GPP); + sSupportedVideoTypes.add(VIDEO_3G2); + sSupportedVideoTypes.add(VIDEO_H263); + sSupportedVideoTypes.add(VIDEO_MP4); + } + + // This class should never be instantiated. + private ContentType() { + } + + public static boolean isSupportedType(String contentType) { + return (null != contentType) && sSupportedContentTypes.contains(contentType); + } + + public static boolean isSupportedImageType(String contentType) { + return isImageType(contentType) && isSupportedType(contentType); + } + + public static boolean isSupportedAudioType(String contentType) { + return isAudioType(contentType) && isSupportedType(contentType); + } + + public static boolean isSupportedVideoType(String contentType) { + return isVideoType(contentType) && isSupportedType(contentType); + } + + public static boolean isTextType(String contentType) { + return (null != contentType) && contentType.startsWith("text/"); + } + + public static boolean isImageType(String contentType) { + return (null != contentType) && contentType.startsWith("image/"); + } + + public static boolean isAudioType(String contentType) { + return (null != contentType) && contentType.startsWith("audio/"); + } + + public static boolean isVideoType(String contentType) { + return (null != contentType) && contentType.startsWith("video/"); + } + + public static boolean isDrmType(String contentType) { + return (null != contentType) + && (contentType.equals(APP_DRM_CONTENT) + || contentType.equals(APP_DRM_MESSAGE)); + } + + public static boolean isUnspecified(String contentType) { + return (null != contentType) && contentType.endsWith("*"); + } + + @SuppressWarnings("unchecked") + public static ArrayList getImageTypes() { + return (ArrayList) sSupportedImageTypes.clone(); + } + + @SuppressWarnings("unchecked") + public static ArrayList getAudioTypes() { + return (ArrayList) sSupportedAudioTypes.clone(); + } + + @SuppressWarnings("unchecked") + public static ArrayList getVideoTypes() { + return (ArrayList) sSupportedVideoTypes.clone(); + } + + @SuppressWarnings("unchecked") + public static ArrayList getSupportedTypes() { + return (ArrayList) sSupportedContentTypes.clone(); + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/InvalidHeaderValueException.java b/samples/ApiDemos/src/com/example/android/mmslib/InvalidHeaderValueException.java new file mode 100644 index 000000000..8cfe108db --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/InvalidHeaderValueException.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib; + +/** + * Thrown when an invalid header value was set. + */ +public class InvalidHeaderValueException extends MmsException { + private static final long serialVersionUID = -2053384496042052262L; + + /** + * Constructs an InvalidHeaderValueException with no detailed message. + */ + public InvalidHeaderValueException() { + super(); + } + + /** + * Constructs an InvalidHeaderValueException with the specified detailed message. + * + * @param message the detailed message. + */ + public InvalidHeaderValueException(String message) { + super(message); + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/MmsException.java b/samples/ApiDemos/src/com/example/android/mmslib/MmsException.java new file mode 100644 index 000000000..fabd3a8d4 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/MmsException.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib; + +/** + * A generic exception that is thrown by the Mms client. + */ +public class MmsException extends Exception { + private static final long serialVersionUID = -7323249827281485390L; + + /** + * Creates a new MmsException. + */ + public MmsException() { + super(); + } + + /** + * Creates a new MmsException with the specified detail message. + * + * @param message the detail message. + */ + public MmsException(String message) { + super(message); + } + + /** + * Creates a new MmsException with the specified cause. + * + * @param cause the cause. + */ + public MmsException(Throwable cause) { + super(cause); + } + + /** + * Creates a new MmsException with the specified detail message and cause. + * + * @param message the detail message. + * @param cause the cause. + */ + public MmsException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/AcknowledgeInd.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/AcknowledgeInd.java new file mode 100644 index 000000000..d10469b88 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/AcknowledgeInd.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import com.example.android.mmslib.InvalidHeaderValueException; + +/** + * M-Acknowledge.ind PDU. + */ +public class AcknowledgeInd extends GenericPdu { + /** + * Constructor, used when composing a M-Acknowledge.ind pdu. + * + * @param mmsVersion current viersion of mms + * @param transactionId the transaction-id value + * @throws InvalidHeaderValueException if parameters are invalid. + * NullPointerException if transactionId is null. + */ + public AcknowledgeInd(int mmsVersion, byte[] transactionId) + throws InvalidHeaderValueException { + super(); + + setMessageType(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND); + setMmsVersion(mmsVersion); + setTransactionId(transactionId); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + AcknowledgeInd(PduHeaders headers) { + super(headers); + } + + /** + * Get X-Mms-Report-Allowed field value. + * + * @return the X-Mms-Report-Allowed value + */ + public int getReportAllowed() { + return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED); + } + + /** + * Set X-Mms-Report-Allowed field value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setReportAllowed(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED); + } + + /** + * Get X-Mms-Transaction-Id field value. + * + * @return the X-Mms-Report-Allowed value + */ + public byte[] getTransactionId() { + return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); + } + + /** + * Set X-Mms-Transaction-Id field value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTransactionId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/Base64.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/Base64.java new file mode 100644 index 000000000..632fa4d2e --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/Base64.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +public class Base64 { + /** + * Used to get the number of Quadruples. + */ + static final int FOURBYTE = 4; + + /** + * Byte used to pad output. + */ + static final byte PAD = (byte) '='; + + /** + * The base length. + */ + static final int BASELENGTH = 255; + + // Create arrays to hold the base64 characters + private static byte[] base64Alphabet = new byte[BASELENGTH]; + + // Populating the character arrays + static { + for (int i = 0; i < BASELENGTH; i++) { + base64Alphabet[i] = (byte) -1; + } + for (int i = 'Z'; i >= 'A'; i--) { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + for (int i = '9'; i >= '0'; i--) { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + } + + /** + * Decodes Base64 data into octects + * + * @param base64Data Byte array containing Base64 data + * @return Array containing decoded data. + */ + public static byte[] decodeBase64(byte[] base64Data) { + // RFC 2045 requires that we discard ALL non-Base64 characters + base64Data = discardNonBase64(base64Data); + + // handle the edge case, so we don't have to worry about it later + if (base64Data.length == 0) { + return new byte[0]; + } + + int numberQuadruple = base64Data.length / FOURBYTE; + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0; + + // Throw away anything not in base64Data + + int encodedIndex = 0; + int dataIndex = 0; + { + // this sizes the output array properly - rlw + int lastData = base64Data.length; + // ignore the '=' padding + while (base64Data[lastData - 1] == PAD) { + if (--lastData == 0) { + return new byte[0]; + } + } + decodedData = new byte[lastData - numberQuadruple]; + } + + for (int i = 0; i < numberQuadruple; i++) { + dataIndex = i * 4; + marker0 = base64Data[dataIndex + 2]; + marker1 = base64Data[dataIndex + 3]; + + b1 = base64Alphabet[base64Data[dataIndex]]; + b2 = base64Alphabet[base64Data[dataIndex + 1]]; + + if (marker0 != PAD && marker1 != PAD) { + //No PAD e.g 3cQl + b3 = base64Alphabet[marker0]; + b4 = base64Alphabet[marker1]; + + decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex + 1] = + (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4); + } else if (marker0 == PAD) { + //Two PAD e.g. 3c[Pad][Pad] + decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + } else if (marker1 == PAD) { + //One PAD e.g. 3cQ[Pad] + b3 = base64Alphabet[marker0]; + + decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex + 1] = + (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + } + encodedIndex += 3; + } + return decodedData; + } + + /** + * Check octect wheter it is a base64 encoding. + * + * @param octect to be checked byte + * @return ture if it is base64 encoding, false otherwise. + */ + private static boolean isBase64(byte octect) { + if (octect == PAD) { + return true; + } else if (base64Alphabet[octect] == -1) { + return false; + } else { + return true; + } + } + + /** + * Discards any characters outside of the base64 alphabet, per + * the requirements on page 25 of RFC 2045 - "Any characters + * outside of the base64 alphabet are to be ignored in base64 + * encoded data." + * + * @param data The base-64 encoded data to groom + * @return The data, less non-base64 characters (see RFC 2045). + */ + static byte[] discardNonBase64(byte[] data) { + byte groomedData[] = new byte[data.length]; + int bytesCopied = 0; + + for (int i = 0; i < data.length; i++) { + if (isBase64(data[i])) { + groomedData[bytesCopied++] = data[i]; + } + } + + byte packedData[] = new byte[bytesCopied]; + + System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); + + return packedData; + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/CharacterSets.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/CharacterSets.java new file mode 100644 index 000000000..c7ccd4aad --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/CharacterSets.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import java.io.UnsupportedEncodingException; +import java.util.HashMap; + +public class CharacterSets { + /** + * IANA assigned MIB enum numbers. + * + * From wap-230-wsp-20010705-a.pdf + * Any-charset = + * Equivalent to the special RFC2616 charset value "*" + */ + public static final int ANY_CHARSET = 0x00; + public static final int US_ASCII = 0x03; + public static final int ISO_8859_1 = 0x04; + public static final int ISO_8859_2 = 0x05; + public static final int ISO_8859_3 = 0x06; + public static final int ISO_8859_4 = 0x07; + public static final int ISO_8859_5 = 0x08; + public static final int ISO_8859_6 = 0x09; + public static final int ISO_8859_7 = 0x0A; + public static final int ISO_8859_8 = 0x0B; + public static final int ISO_8859_9 = 0x0C; + public static final int SHIFT_JIS = 0x11; + public static final int UTF_8 = 0x6A; + public static final int BIG5 = 0x07EA; + public static final int UCS2 = 0x03E8; + public static final int UTF_16 = 0x03F7; + + /** + * If the encoding of given data is unsupported, use UTF_8 to decode it. + */ + public static final int DEFAULT_CHARSET = UTF_8; + + /** + * Array of MIB enum numbers. + */ + private static final int[] MIBENUM_NUMBERS = { + ANY_CHARSET, + US_ASCII, + ISO_8859_1, + ISO_8859_2, + ISO_8859_3, + ISO_8859_4, + ISO_8859_5, + ISO_8859_6, + ISO_8859_7, + ISO_8859_8, + ISO_8859_9, + SHIFT_JIS, + UTF_8, + BIG5, + UCS2, + UTF_16, + }; + + /** + * The Well-known-charset Mime name. + */ + public static final String MIMENAME_ANY_CHARSET = "*"; + public static final String MIMENAME_US_ASCII = "us-ascii"; + public static final String MIMENAME_ISO_8859_1 = "iso-8859-1"; + public static final String MIMENAME_ISO_8859_2 = "iso-8859-2"; + public static final String MIMENAME_ISO_8859_3 = "iso-8859-3"; + public static final String MIMENAME_ISO_8859_4 = "iso-8859-4"; + public static final String MIMENAME_ISO_8859_5 = "iso-8859-5"; + public static final String MIMENAME_ISO_8859_6 = "iso-8859-6"; + public static final String MIMENAME_ISO_8859_7 = "iso-8859-7"; + public static final String MIMENAME_ISO_8859_8 = "iso-8859-8"; + public static final String MIMENAME_ISO_8859_9 = "iso-8859-9"; + public static final String MIMENAME_SHIFT_JIS = "shift_JIS"; + public static final String MIMENAME_UTF_8 = "utf-8"; + public static final String MIMENAME_BIG5 = "big5"; + public static final String MIMENAME_UCS2 = "iso-10646-ucs-2"; + public static final String MIMENAME_UTF_16 = "utf-16"; + + public static final String DEFAULT_CHARSET_NAME = MIMENAME_UTF_8; + + /** + * Array of the names of character sets. + */ + private static final String[] MIME_NAMES = { + MIMENAME_ANY_CHARSET, + MIMENAME_US_ASCII, + MIMENAME_ISO_8859_1, + MIMENAME_ISO_8859_2, + MIMENAME_ISO_8859_3, + MIMENAME_ISO_8859_4, + MIMENAME_ISO_8859_5, + MIMENAME_ISO_8859_6, + MIMENAME_ISO_8859_7, + MIMENAME_ISO_8859_8, + MIMENAME_ISO_8859_9, + MIMENAME_SHIFT_JIS, + MIMENAME_UTF_8, + MIMENAME_BIG5, + MIMENAME_UCS2, + MIMENAME_UTF_16, + }; + + private static final HashMap MIBENUM_TO_NAME_MAP; + private static final HashMap NAME_TO_MIBENUM_MAP; + + static { + // Create the HashMaps. + MIBENUM_TO_NAME_MAP = new HashMap(); + NAME_TO_MIBENUM_MAP = new HashMap(); + assert(MIBENUM_NUMBERS.length == MIME_NAMES.length); + int count = MIBENUM_NUMBERS.length - 1; + for(int i = 0; i <= count; i++) { + MIBENUM_TO_NAME_MAP.put(MIBENUM_NUMBERS[i], MIME_NAMES[i]); + NAME_TO_MIBENUM_MAP.put(MIME_NAMES[i], MIBENUM_NUMBERS[i]); + } + } + + private CharacterSets() {} // Non-instantiatable + + /** + * Map an MIBEnum number to the name of the charset which this number + * is assigned to by IANA. + * + * @param mibEnumValue An IANA assigned MIBEnum number. + * @return The name string of the charset. + * @throws UnsupportedEncodingException + */ + public static String getMimeName(int mibEnumValue) + throws UnsupportedEncodingException { + String name = MIBENUM_TO_NAME_MAP.get(mibEnumValue); + if (name == null) { + throw new UnsupportedEncodingException(); + } + return name; + } + + /** + * Map a well-known charset name to its assigned MIBEnum number. + * + * @param mimeName The charset name. + * @return The MIBEnum number assigned by IANA for this charset. + * @throws UnsupportedEncodingException + */ + public static int getMibEnumValue(String mimeName) + throws UnsupportedEncodingException { + if(null == mimeName) { + return -1; + } + + Integer mibEnumValue = NAME_TO_MIBENUM_MAP.get(mimeName); + if (mibEnumValue == null) { + throw new UnsupportedEncodingException(); + } + return mibEnumValue; + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/DeliveryInd.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/DeliveryInd.java new file mode 100644 index 000000000..afc5386dc --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/DeliveryInd.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import com.example.android.mmslib.InvalidHeaderValueException; + +/** + * M-Delivery.Ind Pdu. + */ +public class DeliveryInd extends GenericPdu { + /** + * Empty constructor. + * Since the Pdu corresponding to this class is constructed + * by the Proxy-Relay server, this class is only instantiated + * by the Pdu Parser. + * + * @throws InvalidHeaderValueException if error occurs. + */ + public DeliveryInd() throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_DELIVERY_IND); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + DeliveryInd(PduHeaders headers) { + super(headers); + } + + /** + * Get Date value. + * + * @return the value + */ + public long getDate() { + return mPduHeaders.getLongInteger(PduHeaders.DATE); + } + + /** + * Set Date value. + * + * @param value the value + */ + public void setDate(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.DATE); + } + + /** + * Get Message-ID value. + * + * @return the value + */ + public byte[] getMessageId() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); + } + + /** + * Set Message-ID value. + * + * @param value the value, should not be null + * @throws NullPointerException if the value is null. + */ + public void setMessageId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); + } + + /** + * Get Status value. + * + * @return the value + */ + public int getStatus() { + return mPduHeaders.getOctet(PduHeaders.STATUS); + } + + /** + * Set Status value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setStatus(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.STATUS); + } + + /** + * Get To value. + * + * @return the value + */ + public EncodedStringValue[] getTo() { + return mPduHeaders.getEncodedStringValues(PduHeaders.TO); + } + + /** + * set To value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTo(EncodedStringValue[] value) { + mPduHeaders.setEncodedStringValues(value, PduHeaders.TO); + } + + /* + * Optional, not supported header fields: + * + * public byte[] getApplicId() {return null;} + * public void setApplicId(byte[] value) {} + * + * public byte[] getAuxApplicId() {return null;} + * public void getAuxApplicId(byte[] value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + * + * public EncodedStringValue getStatusText() {return null;} + * public void setStatusText(EncodedStringValue value) {} + */ +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/EncodedStringValue.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/EncodedStringValue.java new file mode 100644 index 000000000..1daec34b6 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/EncodedStringValue.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; + +/** + * Encoded-string-value = Text-string | Value-length Char-set Text-string + */ +public class EncodedStringValue implements Cloneable { + private static final String TAG = "EncodedStringValue"; + private static final boolean DEBUG = false; + private static final boolean LOCAL_LOGV = false; + + /** + * The Char-set value. + */ + private int mCharacterSet; + + /** + * The Text-string value. + */ + private byte[] mData; + + /** + * Constructor. + * + * @param charset the Char-set value + * @param data the Text-string value + * @throws NullPointerException if Text-string value is null. + */ + public EncodedStringValue(int charset, byte[] data) { + // TODO: CharSet needs to be validated against MIBEnum. + if(null == data) { + throw new NullPointerException("EncodedStringValue: Text-string is null."); + } + + mCharacterSet = charset; + mData = new byte[data.length]; + System.arraycopy(data, 0, mData, 0, data.length); + } + + /** + * Constructor. + * + * @param data the Text-string value + * @throws NullPointerException if Text-string value is null. + */ + public EncodedStringValue(byte[] data) { + this(CharacterSets.DEFAULT_CHARSET, data); + } + + public EncodedStringValue(String data) { + try { + mData = data.getBytes(CharacterSets.DEFAULT_CHARSET_NAME); + mCharacterSet = CharacterSets.DEFAULT_CHARSET; + } catch (UnsupportedEncodingException e) { + Log.e(TAG, "Default encoding must be supported.", e); + } + } + + /** + * Get Char-set value. + * + * @return the value + */ + public int getCharacterSet() { + return mCharacterSet; + } + + /** + * Set Char-set value. + * + * @param charset the Char-set value + */ + public void setCharacterSet(int charset) { + // TODO: CharSet needs to be validated against MIBEnum. + mCharacterSet = charset; + } + + /** + * Get Text-string value. + * + * @return the value + */ + public byte[] getTextString() { + byte[] byteArray = new byte[mData.length]; + + System.arraycopy(mData, 0, byteArray, 0, mData.length); + return byteArray; + } + + /** + * Set Text-string value. + * + * @param textString the Text-string value + * @throws NullPointerException if Text-string value is null. + */ + public void setTextString(byte[] textString) { + if(null == textString) { + throw new NullPointerException("EncodedStringValue: Text-string is null."); + } + + mData = new byte[textString.length]; + System.arraycopy(textString, 0, mData, 0, textString.length); + } + + /** + * Convert this object to a {@link java.lang.String}. If the encoding of + * the EncodedStringValue is null or unsupported, it will be + * treated as iso-8859-1 encoding. + * + * @return The decoded String. + */ + public String getString() { + if (CharacterSets.ANY_CHARSET == mCharacterSet) { + return new String(mData); // system default encoding. + } else { + try { + String name = CharacterSets.getMimeName(mCharacterSet); + return new String(mData, name); + } catch (UnsupportedEncodingException e) { + if (LOCAL_LOGV) { + Log.v(TAG, e.getMessage(), e); + } + try { + return new String(mData, CharacterSets.MIMENAME_ISO_8859_1); + } catch (UnsupportedEncodingException e2) { + return new String(mData); // system default encoding. + } + } + } + } + + /** + * Append to Text-string. + * + * @param textString the textString to append + * @throws NullPointerException if the text String is null + * or an IOException occured. + */ + public void appendTextString(byte[] textString) { + if(null == textString) { + throw new NullPointerException("Text-string is null."); + } + + if(null == mData) { + mData = new byte[textString.length]; + System.arraycopy(textString, 0, mData, 0, textString.length); + } else { + ByteArrayOutputStream newTextString = new ByteArrayOutputStream(); + try { + newTextString.write(mData); + newTextString.write(textString); + } catch (IOException e) { + e.printStackTrace(); + throw new NullPointerException( + "appendTextString: failed when write a new Text-string"); + } + + mData = newTextString.toByteArray(); + } + } + + /* + * (non-Javadoc) + * @see java.lang.Object#clone() + */ + @Override + public Object clone() throws CloneNotSupportedException { + super.clone(); + int len = mData.length; + byte[] dstBytes = new byte[len]; + System.arraycopy(mData, 0, dstBytes, 0, len); + + try { + return new EncodedStringValue(mCharacterSet, dstBytes); + } catch (Exception e) { + Log.e(TAG, "failed to clone an EncodedStringValue: " + this); + e.printStackTrace(); + throw new CloneNotSupportedException(e.getMessage()); + } + } + + /** + * Split this encoded string around matches of the given pattern. + * + * @param pattern the delimiting pattern + * @return the array of encoded strings computed by splitting this encoded + * string around matches of the given pattern + */ + public EncodedStringValue[] split(String pattern) { + String[] temp = getString().split(pattern); + EncodedStringValue[] ret = new EncodedStringValue[temp.length]; + for (int i = 0; i < ret.length; ++i) { + try { + ret[i] = new EncodedStringValue(mCharacterSet, + temp[i].getBytes()); + } catch (NullPointerException e) { + // Can't arrive here + return null; + } + } + return ret; + } + + /** + * Extract an EncodedStringValue[] from a given String. + */ + public static EncodedStringValue[] extract(String src) { + String[] values = src.split(";"); + + ArrayList list = new ArrayList(); + for (int i = 0; i < values.length; i++) { + if (values[i].length() > 0) { + list.add(new EncodedStringValue(values[i])); + } + } + + int len = list.size(); + if (len > 0) { + return list.toArray(new EncodedStringValue[len]); + } else { + return null; + } + } + + /** + * Concatenate an EncodedStringValue[] into a single String. + */ + public static String concat(EncodedStringValue[] addr) { + StringBuilder sb = new StringBuilder(); + int maxIndex = addr.length - 1; + for (int i = 0; i <= maxIndex; i++) { + sb.append(addr[i].getString()); + if (i < maxIndex) { + sb.append(";"); + } + } + + return sb.toString(); + } + + public static EncodedStringValue copy(EncodedStringValue value) { + if (value == null) { + return null; + } + + return new EncodedStringValue(value.mCharacterSet, value.mData); + } + + public static EncodedStringValue[] encodeStrings(String[] array) { + int count = array.length; + if (count > 0) { + EncodedStringValue[] encodedArray = new EncodedStringValue[count]; + for (int i = 0; i < count; i++) { + encodedArray[i] = new EncodedStringValue(array[i]); + } + return encodedArray; + } + return null; + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/GenericPdu.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/GenericPdu.java new file mode 100644 index 000000000..a01ce1082 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/GenericPdu.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import com.example.android.mmslib.InvalidHeaderValueException; + +public class GenericPdu { + /** + * The headers of pdu. + */ + PduHeaders mPduHeaders = null; + + /** + * Constructor. + */ + public GenericPdu() { + mPduHeaders = new PduHeaders(); + } + + /** + * Constructor. + * + * @param headers Headers for this PDU. + */ + GenericPdu(PduHeaders headers) { + mPduHeaders = headers; + } + + /** + * Get the headers of this PDU. + * + * @return A PduHeaders of this PDU. + */ + PduHeaders getPduHeaders() { + return mPduHeaders; + } + + /** + * Get X-Mms-Message-Type field value. + * + * @return the X-Mms-Report-Allowed value + */ + public int getMessageType() { + return mPduHeaders.getOctet(PduHeaders.MESSAGE_TYPE); + } + + /** + * Set X-Mms-Message-Type field value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + * RuntimeException if field's value is not Octet. + */ + public void setMessageType(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.MESSAGE_TYPE); + } + + /** + * Get X-Mms-MMS-Version field value. + * + * @return the X-Mms-MMS-Version value + */ + public int getMmsVersion() { + return mPduHeaders.getOctet(PduHeaders.MMS_VERSION); + } + + /** + * Set X-Mms-MMS-Version field value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + * RuntimeException if field's value is not Octet. + */ + public void setMmsVersion(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.MMS_VERSION); + } + + /** + * Get From value. + * From-value = Value-length + * (Address-present-token Encoded-string-value | Insert-address-token) + * + * @return the value + */ + public EncodedStringValue getFrom() { + return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); + } + + /** + * Set From value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setFrom(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/MultimediaMessagePdu.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/MultimediaMessagePdu.java new file mode 100644 index 000000000..4c8dcb3e7 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/MultimediaMessagePdu.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import com.example.android.mmslib.InvalidHeaderValueException; + +/** + * Multimedia message PDU. + */ +public class MultimediaMessagePdu extends GenericPdu{ + /** + * The body. + */ + private PduBody mMessageBody; + + /** + * Constructor. + */ + public MultimediaMessagePdu() { + super(); + } + + /** + * Constructor. + * + * @param header the header of this PDU + * @param body the body of this PDU + */ + public MultimediaMessagePdu(PduHeaders header, PduBody body) { + super(header); + mMessageBody = body; + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + MultimediaMessagePdu(PduHeaders headers) { + super(headers); + } + + /** + * Get body of the PDU. + * + * @return the body + */ + public PduBody getBody() { + return mMessageBody; + } + + /** + * Set body of the PDU. + * + * @param body the body + */ + public void setBody(PduBody body) { + mMessageBody = body; + } + + /** + * Get subject. + * + * @return the value + */ + public EncodedStringValue getSubject() { + return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT); + } + + /** + * Set subject. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setSubject(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT); + } + + /** + * Get To value. + * + * @return the value + */ + public EncodedStringValue[] getTo() { + return mPduHeaders.getEncodedStringValues(PduHeaders.TO); + } + + /** + * Add a "To" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void addTo(EncodedStringValue value) { + mPduHeaders.appendEncodedStringValue(value, PduHeaders.TO); + } + + /** + * Get X-Mms-Priority value. + * + * @return the value + */ + public int getPriority() { + return mPduHeaders.getOctet(PduHeaders.PRIORITY); + } + + /** + * Set X-Mms-Priority value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setPriority(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.PRIORITY); + } + + /** + * Get Date value. + * + * @return the value + */ + public long getDate() { + return mPduHeaders.getLongInteger(PduHeaders.DATE); + } + + /** + * Set Date value in seconds. + * + * @param value the value + */ + public void setDate(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.DATE); + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/NotificationInd.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/NotificationInd.java new file mode 100644 index 000000000..4269d5416 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/NotificationInd.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import com.example.android.mmslib.InvalidHeaderValueException; + +/** + * M-Notification.ind PDU. + */ +public class NotificationInd extends GenericPdu { + /** + * Empty constructor. + * Since the Pdu corresponding to this class is constructed + * by the Proxy-Relay server, this class is only instantiated + * by the Pdu Parser. + * + * @throws InvalidHeaderValueException if error occurs. + * RuntimeException if an undeclared error occurs. + */ + public NotificationInd() throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + NotificationInd(PduHeaders headers) { + super(headers); + } + + /** + * Get X-Mms-Content-Class Value. + * + * @return the value + */ + public int getContentClass() { + return mPduHeaders.getOctet(PduHeaders.CONTENT_CLASS); + } + + /** + * Set X-Mms-Content-Class Value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + * RuntimeException if an undeclared error occurs. + */ + public void setContentClass(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.CONTENT_CLASS); + } + + /** + * Get X-Mms-Content-Location value. + * When used in a PDU other than M-Mbox-Delete.conf and M-Delete.conf: + * Content-location-value = Uri-value + * + * @return the value + */ + public byte[] getContentLocation() { + return mPduHeaders.getTextString(PduHeaders.CONTENT_LOCATION); + } + + /** + * Set X-Mms-Content-Location value. + * + * @param value the value + * @throws NullPointerException if the value is null. + * RuntimeException if an undeclared error occurs. + */ + public void setContentLocation(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.CONTENT_LOCATION); + } + + /** + * Get X-Mms-Expiry value. + * + * Expiry-value = Value-length + * (Absolute-token Date-value | Relative-token Delta-seconds-value) + * + * @return the value + */ + public long getExpiry() { + return mPduHeaders.getLongInteger(PduHeaders.EXPIRY); + } + + /** + * Set X-Mms-Expiry value. + * + * @param value the value + * @throws RuntimeException if an undeclared error occurs. + */ + public void setExpiry(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY); + } + + /** + * Get From value. + * From-value = Value-length + * (Address-present-token Encoded-string-value | Insert-address-token) + * + * @return the value + */ + public EncodedStringValue getFrom() { + return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); + } + + /** + * Set From value. + * + * @param value the value + * @throws NullPointerException if the value is null. + * RuntimeException if an undeclared error occurs. + */ + public void setFrom(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); + } + + /** + * Get X-Mms-Message-Class value. + * Message-class-value = Class-identifier | Token-text + * Class-identifier = Personal | Advertisement | Informational | Auto + * + * @return the value + */ + public byte[] getMessageClass() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS); + } + + /** + * Set X-Mms-Message-Class value. + * + * @param value the value + * @throws NullPointerException if the value is null. + * RuntimeException if an undeclared error occurs. + */ + public void setMessageClass(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS); + } + + /** + * Get X-Mms-Message-Size value. + * Message-size-value = Long-integer + * + * @return the value + */ + public long getMessageSize() { + return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE); + } + + /** + * Set X-Mms-Message-Size value. + * + * @param value the value + * @throws RuntimeException if an undeclared error occurs. + */ + public void setMessageSize(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE); + } + + /** + * Get subject. + * + * @return the value + */ + public EncodedStringValue getSubject() { + return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT); + } + + /** + * Set subject. + * + * @param value the value + * @throws NullPointerException if the value is null. + * RuntimeException if an undeclared error occurs. + */ + public void setSubject(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT); + } + + /** + * Get X-Mms-Transaction-Id. + * + * @return the value + */ + public byte[] getTransactionId() { + return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); + } + + /** + * Set X-Mms-Transaction-Id. + * + * @param value the value + * @throws NullPointerException if the value is null. + * RuntimeException if an undeclared error occurs. + */ + public void setTransactionId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); + } + + /** + * Get X-Mms-Delivery-Report Value. + * + * @return the value + */ + public int getDeliveryReport() { + return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT); + } + + /** + * Set X-Mms-Delivery-Report Value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + * RuntimeException if an undeclared error occurs. + */ + public void setDeliveryReport(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT); + } + + /* + * Optional, not supported header fields: + * + * public byte[] getApplicId() {return null;} + * public void setApplicId(byte[] value) {} + * + * public byte[] getAuxApplicId() {return null;} + * public void getAuxApplicId(byte[] value) {} + * + * public byte getDrmContent() {return 0x00;} + * public void setDrmContent(byte value) {} + * + * public byte getDistributionIndicator() {return 0x00;} + * public void setDistributionIndicator(byte value) {} + * + * public ElementDescriptorValue getElementDescriptor() {return null;} + * public void getElementDescriptor(ElementDescriptorValue value) {} + * + * public byte getPriority() {return 0x00;} + * public void setPriority(byte value) {} + * + * public byte getRecommendedRetrievalMode() {return 0x00;} + * public void setRecommendedRetrievalMode(byte value) {} + * + * public byte getRecommendedRetrievalModeText() {return 0x00;} + * public void setRecommendedRetrievalModeText(byte value) {} + * + * public byte[] getReplaceId() {return 0x00;} + * public void setReplaceId(byte[] value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + * + * public byte getReplyCharging() {return 0x00;} + * public void setReplyCharging(byte value) {} + * + * public byte getReplyChargingDeadline() {return 0x00;} + * public void setReplyChargingDeadline(byte value) {} + * + * public byte[] getReplyChargingId() {return 0x00;} + * public void setReplyChargingId(byte[] value) {} + * + * public long getReplyChargingSize() {return 0;} + * public void setReplyChargingSize(long value) {} + * + * public byte getStored() {return 0x00;} + * public void setStored(byte value) {} + */ +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/NotifyRespInd.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/NotifyRespInd.java new file mode 100644 index 000000000..e55038fc7 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/NotifyRespInd.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import com.example.android.mmslib.InvalidHeaderValueException; + +/** + * M-NofifyResp.ind PDU. + */ +public class NotifyRespInd extends GenericPdu { + /** + * Constructor, used when composing a M-NotifyResp.ind pdu. + * + * @param mmsVersion current version of mms + * @param transactionId the transaction-id value + * @param status the status value + * @throws InvalidHeaderValueException if parameters are invalid. + * NullPointerException if transactionId is null. + * RuntimeException if an undeclared error occurs. + */ + public NotifyRespInd(int mmsVersion, + byte[] transactionId, + int status) throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND); + setMmsVersion(mmsVersion); + setTransactionId(transactionId); + setStatus(status); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + NotifyRespInd(PduHeaders headers) { + super(headers); + } + + /** + * Get X-Mms-Report-Allowed field value. + * + * @return the X-Mms-Report-Allowed value + */ + public int getReportAllowed() { + return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED); + } + + /** + * Set X-Mms-Report-Allowed field value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + * RuntimeException if an undeclared error occurs. + */ + public void setReportAllowed(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED); + } + + /** + * Set X-Mms-Status field value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + * RuntimeException if an undeclared error occurs. + */ + public void setStatus(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.STATUS); + } + + /** + * GetX-Mms-Status field value. + * + * @return the X-Mms-Status value + */ + public int getStatus() { + return mPduHeaders.getOctet(PduHeaders.STATUS); + } + + /** + * Get X-Mms-Transaction-Id field value. + * + * @return the X-Mms-Report-Allowed value + */ + public byte[] getTransactionId() { + return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); + } + + /** + * Set X-Mms-Transaction-Id field value. + * + * @param value the value + * @throws NullPointerException if the value is null. + * RuntimeException if an undeclared error occurs. + */ + public void setTransactionId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduBody.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduBody.java new file mode 100644 index 000000000..4f67ed28c --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduBody.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +public class PduBody { + private Vector mParts = null; + + private Map mPartMapByContentId = null; + private Map mPartMapByContentLocation = null; + private Map mPartMapByName = null; + private Map mPartMapByFileName = null; + + /** + * Constructor. + */ + public PduBody() { + mParts = new Vector(); + + mPartMapByContentId = new HashMap(); + mPartMapByContentLocation = new HashMap(); + mPartMapByName = new HashMap(); + mPartMapByFileName = new HashMap(); + } + + private void putPartToMaps(PduPart part) { + // Put part to mPartMapByContentId. + byte[] contentId = part.getContentId(); + if(null != contentId) { + mPartMapByContentId.put(new String(contentId), part); + } + + // Put part to mPartMapByContentLocation. + byte[] contentLocation = part.getContentLocation(); + if(null != contentLocation) { + String clc = new String(contentLocation); + mPartMapByContentLocation.put(clc, part); + } + + // Put part to mPartMapByName. + byte[] name = part.getName(); + if(null != name) { + String clc = new String(name); + mPartMapByName.put(clc, part); + } + + // Put part to mPartMapByFileName. + byte[] fileName = part.getFilename(); + if(null != fileName) { + String clc = new String(fileName); + mPartMapByFileName.put(clc, part); + } + } + + /** + * Appends the specified part to the end of this body. + * + * @param part part to be appended + * @return true when success, false when fail + * @throws NullPointerException when part is null + */ + public boolean addPart(PduPart part) { + if(null == part) { + throw new NullPointerException(); + } + + putPartToMaps(part); + return mParts.add(part); + } + + /** + * Inserts the specified part at the specified position. + * + * @param index index at which the specified part is to be inserted + * @param part part to be inserted + * @throws NullPointerException when part is null + */ + public void addPart(int index, PduPart part) { + if(null == part) { + throw new NullPointerException(); + } + + putPartToMaps(part); + mParts.add(index, part); + } + + /** + * Removes the part at the specified position. + * + * @param index index of the part to return + * @return part at the specified index + */ + public PduPart removePart(int index) { + return mParts.remove(index); + } + + /** + * Remove all of the parts. + */ + public void removeAll() { + mParts.clear(); + } + + /** + * Get the part at the specified position. + * + * @param index index of the part to return + * @return part at the specified index + */ + public PduPart getPart(int index) { + return mParts.get(index); + } + + /** + * Get the index of the specified part. + * + * @param part the part object + * @return index the index of the first occurrence of the part in this body + */ + public int getPartIndex(PduPart part) { + return mParts.indexOf(part); + } + + /** + * Get the number of parts. + * + * @return the number of parts + */ + public int getPartsNum() { + return mParts.size(); + } + + /** + * Get pdu part by content id. + * + * @param cid the value of content id. + * @return the pdu part. + */ + public PduPart getPartByContentId(String cid) { + return mPartMapByContentId.get(cid); + } + + /** + * Get pdu part by Content-Location. Content-Location of part is + * the same as filename and name(param of content-type). + * + * @param fileName the value of filename. + * @return the pdu part. + */ + public PduPart getPartByContentLocation(String contentLocation) { + return mPartMapByContentLocation.get(contentLocation); + } + + /** + * Get pdu part by name. + * + * @param fileName the value of filename. + * @return the pdu part. + */ + public PduPart getPartByName(String name) { + return mPartMapByName.get(name); + } + + /** + * Get pdu part by filename. + * + * @param fileName the value of filename. + * @return the pdu part. + */ + public PduPart getPartByFileName(String filename) { + return mPartMapByFileName.get(filename); + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduComposer.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduComposer.java new file mode 100644 index 000000000..242e7eea5 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduComposer.java @@ -0,0 +1,1186 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import android.content.ContentResolver; +import android.content.Context; +import android.util.Log; +import android.text.TextUtils; + +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashMap; + +public class PduComposer { + /** + * Address type. + */ + static private final int PDU_PHONE_NUMBER_ADDRESS_TYPE = 1; + static private final int PDU_EMAIL_ADDRESS_TYPE = 2; + static private final int PDU_IPV4_ADDRESS_TYPE = 3; + static private final int PDU_IPV6_ADDRESS_TYPE = 4; + static private final int PDU_UNKNOWN_ADDRESS_TYPE = 5; + + /** + * Address regular expression string. + */ + static final String REGEXP_PHONE_NUMBER_ADDRESS_TYPE = "\\+?[0-9|\\.|\\-]+"; + static final String REGEXP_EMAIL_ADDRESS_TYPE = "[a-zA-Z| ]*\\<{0,1}[a-zA-Z| ]+@{1}" + + "[a-zA-Z| ]+\\.{1}[a-zA-Z| ]+\\>{0,1}"; + static final String REGEXP_IPV6_ADDRESS_TYPE = + "[a-fA-F]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" + + "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" + + "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}"; + static final String REGEXP_IPV4_ADDRESS_TYPE = "[0-9]{1,3}\\.{1}[0-9]{1,3}\\.{1}" + + "[0-9]{1,3}\\.{1}[0-9]{1,3}"; + + /** + * The postfix strings of address. + */ + static final String STRING_PHONE_NUMBER_ADDRESS_TYPE = "/TYPE=PLMN"; + static final String STRING_IPV4_ADDRESS_TYPE = "/TYPE=IPV4"; + static final String STRING_IPV6_ADDRESS_TYPE = "/TYPE=IPV6"; + + /** + * Error values. + */ + static private final int PDU_COMPOSE_SUCCESS = 0; + static private final int PDU_COMPOSE_CONTENT_ERROR = 1; + static private final int PDU_COMPOSE_FIELD_NOT_SET = 2; + static private final int PDU_COMPOSE_FIELD_NOT_SUPPORTED = 3; + + /** + * WAP values defined in WSP spec. + */ + static private final int QUOTED_STRING_FLAG = 34; + static private final int END_STRING_FLAG = 0; + static private final int LENGTH_QUOTE = 31; + static private final int TEXT_MAX = 127; + static private final int SHORT_INTEGER_MAX = 127; + static private final int LONG_INTEGER_LENGTH_MAX = 8; + + /** + * Block size when read data from InputStream. + */ + static private final int PDU_COMPOSER_BLOCK_SIZE = 1024; + + /** + * The output message. + */ + protected ByteArrayOutputStream mMessage = null; + + /** + * The PDU. + */ + private GenericPdu mPdu = null; + + /** + * Current visiting position of the mMessage. + */ + protected int mPosition = 0; + + /** + * Message compose buffer stack. + */ + private BufferStack mStack = null; + + /** + * Content resolver. + */ + private final ContentResolver mResolver; + + /** + * Header of this pdu. + */ + private PduHeaders mPduHeader = null; + + /** + * Map of all content type + */ + private static HashMap mContentTypeMap = null; + + static { + mContentTypeMap = new HashMap(); + + int i; + for (i = 0; i < PduContentTypes.contentTypes.length; i++) { + mContentTypeMap.put(PduContentTypes.contentTypes[i], i); + } + } + + /** + * Constructor. + * + * @param context the context + * @param pdu the pdu to be composed + */ + public PduComposer(Context context, GenericPdu pdu) { + mPdu = pdu; + mResolver = context.getContentResolver(); + mPduHeader = pdu.getPduHeaders(); + mStack = new BufferStack(); + mMessage = new ByteArrayOutputStream(); + mPosition = 0; + } + + /** + * Make the message. No need to check whether mandatory fields are set, + * because the constructors of outgoing pdus are taking care of this. + * + * @return OutputStream of maked message. Return null if + * the PDU is invalid. + */ + public byte[] make() { + // Get Message-type. + int type = mPdu.getMessageType(); + + /* make the message */ + switch (type) { + case PduHeaders.MESSAGE_TYPE_SEND_REQ: + if (makeSendReqPdu() != PDU_COMPOSE_SUCCESS) { + return null; + } + break; + case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND: + if (makeNotifyResp() != PDU_COMPOSE_SUCCESS) { + return null; + } + break; + case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND: + if (makeAckInd() != PDU_COMPOSE_SUCCESS) { + return null; + } + break; + case PduHeaders.MESSAGE_TYPE_READ_REC_IND: + if (makeReadRecInd() != PDU_COMPOSE_SUCCESS) { + return null; + } + break; + default: + return null; + } + + return mMessage.toByteArray(); + } + + /** + * Copy buf to mMessage. + */ + protected void arraycopy(byte[] buf, int pos, int length) { + mMessage.write(buf, pos, length); + mPosition = mPosition + length; + } + + /** + * Append a byte to mMessage. + */ + protected void append(int value) { + mMessage.write(value); + mPosition ++; + } + + /** + * Append short integer value to mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendShortInteger(int value) { + /* + * From WAP-230-WSP-20010705-a: + * Short-integer = OCTET + * ; Integers in range 0-127 shall be encoded as a one octet value + * ; with the most significant bit set to one (1xxx xxxx) and with + * ; the value in the remaining least significant bits. + * In our implementation, only low 7 bits are stored and otherwise + * bits are ignored. + */ + append((value | 0x80) & 0xff); + } + + /** + * Append an octet number between 128 and 255 into mMessage. + * NOTE: + * A value between 0 and 127 should be appended by using appendShortInteger. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendOctet(int number) { + append(number); + } + + /** + * Append a short length into mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendShortLength(int value) { + /* + * From WAP-230-WSP-20010705-a: + * Short-length = + */ + append(value); + } + + /** + * Append long integer into mMessage. it's used for really long integers. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendLongInteger(long longInt) { + /* + * From WAP-230-WSP-20010705-a: + * Long-integer = Short-length Multi-octet-integer + * ; The Short-length indicates the length of the Multi-octet-integer + * Multi-octet-integer = 1*30 OCTET + * ; The content octets shall be an unsigned integer value with the + * ; most significant octet encoded first (big-endian representation). + * ; The minimum number of octets must be used to encode the value. + */ + int size; + long temp = longInt; + + // Count the length of the long integer. + for(size = 0; (temp != 0) && (size < LONG_INTEGER_LENGTH_MAX); size++) { + temp = (temp >>> 8); + } + + // Set Length. + appendShortLength(size); + + // Count and set the long integer. + int i; + int shift = (size -1) * 8; + + for (i = 0; i < size; i++) { + append((int)((longInt >>> shift) & 0xff)); + shift = shift - 8; + } + } + + /** + * Append text string into mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendTextString(byte[] text) { + /* + * From WAP-230-WSP-20010705-a: + * Text-string = [Quote] *TEXT End-of-string + * ; If the first character in the TEXT is in the range of 128-255, + * ; a Quote character must precede it. Otherwise the Quote character + * ;must be omitted. The Quote is not part of the contents. + */ + if (((text[0])&0xff) > TEXT_MAX) { // No need to check for <= 255 + append(TEXT_MAX); + } + + arraycopy(text, 0, text.length); + append(0); + } + + /** + * Append text string into mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendTextString(String str) { + /* + * From WAP-230-WSP-20010705-a: + * Text-string = [Quote] *TEXT End-of-string + * ; If the first character in the TEXT is in the range of 128-255, + * ; a Quote character must precede it. Otherwise the Quote character + * ;must be omitted. The Quote is not part of the contents. + */ + appendTextString(str.getBytes()); + } + + /** + * Append encoded string value to mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendEncodedString(EncodedStringValue enStr) { + /* + * From OMA-TS-MMS-ENC-V1_3-20050927-C: + * Encoded-string-value = Text-string | Value-length Char-set Text-string + */ + assert(enStr != null); + + int charset = enStr.getCharacterSet(); + byte[] textString = enStr.getTextString(); + if (null == textString) { + return; + } + + /* + * In the implementation of EncodedStringValue, the charset field will + * never be 0. It will always be composed as + * Encoded-string-value = Value-length Char-set Text-string + */ + mStack.newbuf(); + PositionMarker start = mStack.mark(); + + appendShortInteger(charset); + appendTextString(textString); + + int len = start.getLength(); + mStack.pop(); + appendValueLength(len); + mStack.copy(); + } + + /** + * Append uintvar integer into mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendUintvarInteger(long value) { + /* + * From WAP-230-WSP-20010705-a: + * To encode a large unsigned integer, split it into 7-bit fragments + * and place them in the payloads of multiple octets. The most significant + * bits are placed in the first octets with the least significant bits + * ending up in the last octet. All octets MUST set the Continue bit to 1 + * except the last octet, which MUST set the Continue bit to 0. + */ + int i; + long max = SHORT_INTEGER_MAX; + + for (i = 0; i < 5; i++) { + if (value < max) { + break; + } + + max = (max << 7) | 0x7fl; + } + + while(i > 0) { + long temp = value >>> (i * 7); + temp = temp & 0x7f; + + append((int)((temp | 0x80) & 0xff)); + + i--; + } + + append((int)(value & 0x7f)); + } + + /** + * Append date value into mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendDateValue(long date) { + /* + * From OMA-TS-MMS-ENC-V1_3-20050927-C: + * Date-value = Long-integer + */ + appendLongInteger(date); + } + + /** + * Append value length to mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendValueLength(long value) { + /* + * From WAP-230-WSP-20010705-a: + * Value-length = Short-length | (Length-quote Length) + * ; Value length is used to indicate the length of the value to follow + * Short-length = + * Length-quote = + * Length = Uintvar-integer + */ + if (value < LENGTH_QUOTE) { + appendShortLength((int) value); + return; + } + + append(LENGTH_QUOTE); + appendUintvarInteger(value); + } + + /** + * Append quoted string to mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendQuotedString(byte[] text) { + /* + * From WAP-230-WSP-20010705-a: + * Quoted-string = *TEXT End-of-string + * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing + * ;quotation-marks <"> removed. + */ + append(QUOTED_STRING_FLAG); + arraycopy(text, 0, text.length); + append(END_STRING_FLAG); + } + + /** + * Append quoted string to mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendQuotedString(String str) { + /* + * From WAP-230-WSP-20010705-a: + * Quoted-string = *TEXT End-of-string + * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing + * ;quotation-marks <"> removed. + */ + appendQuotedString(str.getBytes()); + } + + private EncodedStringValue appendAddressType(EncodedStringValue address) { + EncodedStringValue temp = null; + + try { + int addressType = checkAddressType(address.getString()); + temp = EncodedStringValue.copy(address); + if (PDU_PHONE_NUMBER_ADDRESS_TYPE == addressType) { + // Phone number. + temp.appendTextString(STRING_PHONE_NUMBER_ADDRESS_TYPE.getBytes()); + } else if (PDU_IPV4_ADDRESS_TYPE == addressType) { + // Ipv4 address. + temp.appendTextString(STRING_IPV4_ADDRESS_TYPE.getBytes()); + } else if (PDU_IPV6_ADDRESS_TYPE == addressType) { + // Ipv6 address. + temp.appendTextString(STRING_IPV6_ADDRESS_TYPE.getBytes()); + } + } catch (NullPointerException e) { + return null; + } + + return temp; + } + + /** + * Append header to mMessage. + */ + private int appendHeader(int field) { + switch (field) { + case PduHeaders.MMS_VERSION: + appendOctet(field); + + int version = mPduHeader.getOctet(field); + if (0 == version) { + appendShortInteger(PduHeaders.CURRENT_MMS_VERSION); + } else { + appendShortInteger(version); + } + + break; + + case PduHeaders.MESSAGE_ID: + case PduHeaders.TRANSACTION_ID: + byte[] textString = mPduHeader.getTextString(field); + if (null == textString) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + appendOctet(field); + appendTextString(textString); + break; + + case PduHeaders.TO: + case PduHeaders.BCC: + case PduHeaders.CC: + EncodedStringValue[] addr = mPduHeader.getEncodedStringValues(field); + + if (null == addr) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + EncodedStringValue temp; + for (int i = 0; i < addr.length; i++) { + temp = appendAddressType(addr[i]); + if (temp == null) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + appendOctet(field); + appendEncodedString(temp); + } + break; + + case PduHeaders.FROM: + // Value-length (Address-present-token Encoded-string-value | Insert-address-token) + appendOctet(field); + + EncodedStringValue from = mPduHeader.getEncodedStringValue(field); + if ((from == null) + || TextUtils.isEmpty(from.getString()) + || new String(from.getTextString()).equals( + PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR)) { + // Length of from = 1 + append(1); + // Insert-address-token = + append(PduHeaders.FROM_INSERT_ADDRESS_TOKEN); + } else { + mStack.newbuf(); + PositionMarker fstart = mStack.mark(); + + // Address-present-token = + append(PduHeaders.FROM_ADDRESS_PRESENT_TOKEN); + + temp = appendAddressType(from); + if (temp == null) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + appendEncodedString(temp); + + int flen = fstart.getLength(); + mStack.pop(); + appendValueLength(flen); + mStack.copy(); + } + break; + + case PduHeaders.READ_STATUS: + case PduHeaders.STATUS: + case PduHeaders.REPORT_ALLOWED: + case PduHeaders.PRIORITY: + case PduHeaders.DELIVERY_REPORT: + case PduHeaders.READ_REPORT: + int octet = mPduHeader.getOctet(field); + if (0 == octet) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + appendOctet(field); + appendOctet(octet); + break; + + case PduHeaders.DATE: + long date = mPduHeader.getLongInteger(field); + if (-1 == date) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + appendOctet(field); + appendDateValue(date); + break; + + case PduHeaders.SUBJECT: + EncodedStringValue enString = + mPduHeader.getEncodedStringValue(field); + if (null == enString) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + appendOctet(field); + appendEncodedString(enString); + break; + + case PduHeaders.MESSAGE_CLASS: + byte[] messageClass = mPduHeader.getTextString(field); + if (null == messageClass) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + appendOctet(field); + if (Arrays.equals(messageClass, + PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes())) { + appendOctet(PduHeaders.MESSAGE_CLASS_ADVERTISEMENT); + } else if (Arrays.equals(messageClass, + PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes())) { + appendOctet(PduHeaders.MESSAGE_CLASS_AUTO); + } else if (Arrays.equals(messageClass, + PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes())) { + appendOctet(PduHeaders.MESSAGE_CLASS_PERSONAL); + } else if (Arrays.equals(messageClass, + PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes())) { + appendOctet(PduHeaders.MESSAGE_CLASS_INFORMATIONAL); + } else { + appendTextString(messageClass); + } + break; + + case PduHeaders.EXPIRY: + long expiry = mPduHeader.getLongInteger(field); + if (-1 == expiry) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + appendOctet(field); + + mStack.newbuf(); + PositionMarker expiryStart = mStack.mark(); + + append(PduHeaders.VALUE_RELATIVE_TOKEN); + appendLongInteger(expiry); + + int expiryLength = expiryStart.getLength(); + mStack.pop(); + appendValueLength(expiryLength); + mStack.copy(); + break; + + default: + return PDU_COMPOSE_FIELD_NOT_SUPPORTED; + } + + return PDU_COMPOSE_SUCCESS; + } + + /** + * Make ReadRec.Ind. + */ + private int makeReadRecInd() { + if (mMessage == null) { + mMessage = new ByteArrayOutputStream(); + mPosition = 0; + } + + // X-Mms-Message-Type + appendOctet(PduHeaders.MESSAGE_TYPE); + appendOctet(PduHeaders.MESSAGE_TYPE_READ_REC_IND); + + // X-Mms-MMS-Version + if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // Message-ID + if (appendHeader(PduHeaders.MESSAGE_ID) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // To + if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // From + if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // Date Optional + appendHeader(PduHeaders.DATE); + + // X-Mms-Read-Status + if (appendHeader(PduHeaders.READ_STATUS) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // X-Mms-Applic-ID Optional(not support) + // X-Mms-Reply-Applic-ID Optional(not support) + // X-Mms-Aux-Applic-Info Optional(not support) + + return PDU_COMPOSE_SUCCESS; + } + + /** + * Make NotifyResp.Ind. + */ + private int makeNotifyResp() { + if (mMessage == null) { + mMessage = new ByteArrayOutputStream(); + mPosition = 0; + } + + // X-Mms-Message-Type + appendOctet(PduHeaders.MESSAGE_TYPE); + appendOctet(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND); + + // X-Mms-Transaction-ID + if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // X-Mms-MMS-Version + if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // X-Mms-Status + if (appendHeader(PduHeaders.STATUS) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // X-Mms-Report-Allowed Optional (not support) + return PDU_COMPOSE_SUCCESS; + } + + /** + * Make Acknowledge.Ind. + */ + private int makeAckInd() { + if (mMessage == null) { + mMessage = new ByteArrayOutputStream(); + mPosition = 0; + } + + // X-Mms-Message-Type + appendOctet(PduHeaders.MESSAGE_TYPE); + appendOctet(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND); + + // X-Mms-Transaction-ID + if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // X-Mms-MMS-Version + if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // X-Mms-Report-Allowed Optional + appendHeader(PduHeaders.REPORT_ALLOWED); + + return PDU_COMPOSE_SUCCESS; + } + + /** + * Make Send.req. + */ + private int makeSendReqPdu() { + if (mMessage == null) { + mMessage = new ByteArrayOutputStream(); + mPosition = 0; + } + + // X-Mms-Message-Type + appendOctet(PduHeaders.MESSAGE_TYPE); + appendOctet(PduHeaders.MESSAGE_TYPE_SEND_REQ); + + // X-Mms-Transaction-ID + appendOctet(PduHeaders.TRANSACTION_ID); + + byte[] trid = mPduHeader.getTextString(PduHeaders.TRANSACTION_ID); + if (trid == null) { + // Transaction-ID should be set(by Transaction) before make(). + throw new IllegalArgumentException("Transaction-ID is null."); + } + appendTextString(trid); + + // X-Mms-MMS-Version + if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // Date Date-value Optional. + appendHeader(PduHeaders.DATE); + + // From + if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + boolean recipient = false; + + // To + if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_CONTENT_ERROR) { + recipient = true; + } + + // Cc + if (appendHeader(PduHeaders.CC) != PDU_COMPOSE_CONTENT_ERROR) { + recipient = true; + } + + // Bcc + if (appendHeader(PduHeaders.BCC) != PDU_COMPOSE_CONTENT_ERROR) { + recipient = true; + } + + // Need at least one of "cc", "bcc" and "to". + if (false == recipient) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // Subject Optional + appendHeader(PduHeaders.SUBJECT); + + // X-Mms-Message-Class Optional + // Message-class-value = Class-identifier | Token-text + appendHeader(PduHeaders.MESSAGE_CLASS); + + // X-Mms-Expiry Optional + appendHeader(PduHeaders.EXPIRY); + + // X-Mms-Priority Optional + appendHeader(PduHeaders.PRIORITY); + + // X-Mms-Delivery-Report Optional + appendHeader(PduHeaders.DELIVERY_REPORT); + + // X-Mms-Read-Report Optional + appendHeader(PduHeaders.READ_REPORT); + + // Content-Type + appendOctet(PduHeaders.CONTENT_TYPE); + + // Message body + return makeMessageBody(); + } + + /** + * Make message body. + */ + private int makeMessageBody() { + // 1. add body informations + mStack.newbuf(); // Switching buffer because we need to + + PositionMarker ctStart = mStack.mark(); + + // This contentTypeIdentifier should be used for type of attachment... + String contentType = new String(mPduHeader.getTextString(PduHeaders.CONTENT_TYPE)); + Integer contentTypeIdentifier = mContentTypeMap.get(contentType); + if (contentTypeIdentifier == null) { + // content type is mandatory + return PDU_COMPOSE_CONTENT_ERROR; + } + + appendShortInteger(contentTypeIdentifier.intValue()); + + // content-type parameter: start + PduBody body = ((SendReq) mPdu).getBody(); + if (null == body || body.getPartsNum() == 0) { + // empty message + appendUintvarInteger(0); + mStack.pop(); + mStack.copy(); + return PDU_COMPOSE_SUCCESS; + } + + PduPart part; + try { + part = body.getPart(0); + + byte[] start = part.getContentId(); + if (start != null) { + appendOctet(PduPart.P_DEP_START); + if (('<' == start[0]) && ('>' == start[start.length - 1])) { + appendTextString(start); + } else { + appendTextString("<" + new String(start) + ">"); + } + } + + // content-type parameter: type + appendOctet(PduPart.P_CT_MR_TYPE); + appendTextString(part.getContentType()); + } + catch (ArrayIndexOutOfBoundsException e){ + e.printStackTrace(); + } + + int ctLength = ctStart.getLength(); + mStack.pop(); + appendValueLength(ctLength); + mStack.copy(); + + // 3. add content + int partNum = body.getPartsNum(); + appendUintvarInteger(partNum); + for (int i = 0; i < partNum; i++) { + part = body.getPart(i); + mStack.newbuf(); // Leaving space for header lengh and data length + PositionMarker attachment = mStack.mark(); + + mStack.newbuf(); // Leaving space for Content-Type length + PositionMarker contentTypeBegin = mStack.mark(); + + byte[] partContentType = part.getContentType(); + + if (partContentType == null) { + // content type is mandatory + return PDU_COMPOSE_CONTENT_ERROR; + } + + // content-type value + Integer partContentTypeIdentifier = + mContentTypeMap.get(new String(partContentType)); + if (partContentTypeIdentifier == null) { + appendTextString(partContentType); + } else { + appendShortInteger(partContentTypeIdentifier.intValue()); + } + + /* Content-type parameter : name. + * The value of name, filename, content-location is the same. + * Just one of them is enough for this PDU. + */ + byte[] name = part.getName(); + + if (null == name) { + name = part.getFilename(); + + if (null == name) { + name = part.getContentLocation(); + + if (null == name) { + /* at lease one of name, filename, Content-location + * should be available. + */ + return PDU_COMPOSE_CONTENT_ERROR; + } + } + } + appendOctet(PduPart.P_DEP_NAME); + appendTextString(name); + + // content-type parameter : charset + int charset = part.getCharset(); + if (charset != 0) { + appendOctet(PduPart.P_CHARSET); + appendShortInteger(charset); + } + + int contentTypeLength = contentTypeBegin.getLength(); + mStack.pop(); + appendValueLength(contentTypeLength); + mStack.copy(); + + // content id + byte[] contentId = part.getContentId(); + + if (null != contentId) { + appendOctet(PduPart.P_CONTENT_ID); + if (('<' == contentId[0]) && ('>' == contentId[contentId.length - 1])) { + appendQuotedString(contentId); + } else { + appendQuotedString("<" + new String(contentId) + ">"); + } + } + + // content-location + byte[] contentLocation = part.getContentLocation(); + if (null != contentLocation) { + appendOctet(PduPart.P_CONTENT_LOCATION); + appendTextString(contentLocation); + } + + // content + int headerLength = attachment.getLength(); + + int dataLength = 0; // Just for safety... + byte[] partData = part.getData(); + + if (partData != null) { + arraycopy(partData, 0, partData.length); + dataLength = partData.length; + } else { + InputStream cr = null; + try { + byte[] buffer = new byte[PDU_COMPOSER_BLOCK_SIZE]; + cr = mResolver.openInputStream(part.getDataUri()); + int len = 0; + while ((len = cr.read(buffer)) != -1) { + mMessage.write(buffer, 0, len); + mPosition += len; + dataLength += len; + } + } catch (FileNotFoundException e) { + return PDU_COMPOSE_CONTENT_ERROR; + } catch (IOException e) { + return PDU_COMPOSE_CONTENT_ERROR; + } catch (RuntimeException e) { + return PDU_COMPOSE_CONTENT_ERROR; + } finally { + if (cr != null) { + try { + cr.close(); + } catch (IOException e) { + } + } + } + } + + if (dataLength != (attachment.getLength() - headerLength)) { + throw new RuntimeException("BUG: Length sanity check failed"); + } + + mStack.pop(); + appendUintvarInteger(headerLength); + appendUintvarInteger(dataLength); + mStack.copy(); + } + + return PDU_COMPOSE_SUCCESS; + } + + /** + * Record current message informations. + */ + static private class LengthRecordNode { + ByteArrayOutputStream currentMessage = null; + public int currentPosition = 0; + + public LengthRecordNode next = null; + } + + /** + * Mark current message position and stact size. + */ + private class PositionMarker { + private int c_pos; // Current position + private int currentStackSize; // Current stack size + + int getLength() { + // If these assert fails, likely that you are finding the + // size of buffer that is deep in BufferStack you can only + // find the length of the buffer that is on top + if (currentStackSize != mStack.stackSize) { + throw new RuntimeException("BUG: Invalid call to getLength()"); + } + + return mPosition - c_pos; + } + } + + /** + * This implementation can be OPTIMIZED to use only + * 2 buffers. This optimization involves changing BufferStack + * only... Its usage (interface) will not change. + */ + private class BufferStack { + private LengthRecordNode stack = null; + private LengthRecordNode toCopy = null; + + int stackSize = 0; + + /** + * Create a new message buffer and push it into the stack. + */ + void newbuf() { + // You can't create a new buff when toCopy != null + // That is after calling pop() and before calling copy() + // If you do, it is a bug + if (toCopy != null) { + throw new RuntimeException("BUG: Invalid newbuf() before copy()"); + } + + LengthRecordNode temp = new LengthRecordNode(); + + temp.currentMessage = mMessage; + temp.currentPosition = mPosition; + + temp.next = stack; + stack = temp; + + stackSize = stackSize + 1; + + mMessage = new ByteArrayOutputStream(); + mPosition = 0; + } + + /** + * Pop the message before and record current message in the stack. + */ + void pop() { + ByteArrayOutputStream currentMessage = mMessage; + int currentPosition = mPosition; + + mMessage = stack.currentMessage; + mPosition = stack.currentPosition; + + toCopy = stack; + // Re using the top element of the stack to avoid memory allocation + + stack = stack.next; + stackSize = stackSize - 1; + + toCopy.currentMessage = currentMessage; + toCopy.currentPosition = currentPosition; + } + + /** + * Append current message to the message before. + */ + void copy() { + arraycopy(toCopy.currentMessage.toByteArray(), 0, + toCopy.currentPosition); + + toCopy = null; + } + + /** + * Mark current message position + */ + PositionMarker mark() { + PositionMarker m = new PositionMarker(); + + m.c_pos = mPosition; + m.currentStackSize = stackSize; + + return m; + } + } + + /** + * Check address type. + * + * @param address address string without the postfix stinng type, + * such as "/TYPE=PLMN", "/TYPE=IPv6" and "/TYPE=IPv4" + * @return PDU_PHONE_NUMBER_ADDRESS_TYPE if it is phone number, + * PDU_EMAIL_ADDRESS_TYPE if it is email address, + * PDU_IPV4_ADDRESS_TYPE if it is ipv4 address, + * PDU_IPV6_ADDRESS_TYPE if it is ipv6 address, + * PDU_UNKNOWN_ADDRESS_TYPE if it is unknown. + */ + protected static int checkAddressType(String address) { + /** + * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf, section 8. + * address = ( e-mail / device-address / alphanum-shortcode / num-shortcode) + * e-mail = mailbox; to the definition of mailbox as described in + * section 3.4 of [RFC2822], but excluding the + * obsolete definitions as indicated by the "obs-" prefix. + * device-address = ( global-phone-number "/TYPE=PLMN" ) + * / ( ipv4 "/TYPE=IPv4" ) / ( ipv6 "/TYPE=IPv6" ) + * / ( escaped-value "/TYPE=" address-type ) + * + * global-phone-number = ["+"] 1*( DIGIT / written-sep ) + * written-sep =("-"/".") + * + * ipv4 = 1*3DIGIT 3( "." 1*3DIGIT ) ; IPv4 address value + * + * ipv6 = 4HEXDIG 7( ":" 4HEXDIG ) ; IPv6 address per RFC 2373 + */ + + if (null == address) { + return PDU_UNKNOWN_ADDRESS_TYPE; + } + + if (address.matches(REGEXP_IPV4_ADDRESS_TYPE)) { + // Ipv4 address. + return PDU_IPV4_ADDRESS_TYPE; + }else if (address.matches(REGEXP_PHONE_NUMBER_ADDRESS_TYPE)) { + // Phone number. + return PDU_PHONE_NUMBER_ADDRESS_TYPE; + } else if (address.matches(REGEXP_EMAIL_ADDRESS_TYPE)) { + // Email address. + return PDU_EMAIL_ADDRESS_TYPE; + } else if (address.matches(REGEXP_IPV6_ADDRESS_TYPE)) { + // Ipv6 address. + return PDU_IPV6_ADDRESS_TYPE; + } else { + // Unknown address. + return PDU_UNKNOWN_ADDRESS_TYPE; + } + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduContentTypes.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduContentTypes.java new file mode 100644 index 000000000..bdb7ee47f --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduContentTypes.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +public class PduContentTypes { + /** + * All content types. From: + * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.htm + */ + static final String[] contentTypes = { + "*/*", /* 0x00 */ + "text/*", /* 0x01 */ + "text/html", /* 0x02 */ + "text/plain", /* 0x03 */ + "text/x-hdml", /* 0x04 */ + "text/x-ttml", /* 0x05 */ + "text/x-vCalendar", /* 0x06 */ + "text/x-vCard", /* 0x07 */ + "text/vnd.wap.wml", /* 0x08 */ + "text/vnd.wap.wmlscript", /* 0x09 */ + "text/vnd.wap.wta-event", /* 0x0A */ + "multipart/*", /* 0x0B */ + "multipart/mixed", /* 0x0C */ + "multipart/form-data", /* 0x0D */ + "multipart/byterantes", /* 0x0E */ + "multipart/alternative", /* 0x0F */ + "application/*", /* 0x10 */ + "application/java-vm", /* 0x11 */ + "application/x-www-form-urlencoded", /* 0x12 */ + "application/x-hdmlc", /* 0x13 */ + "application/vnd.wap.wmlc", /* 0x14 */ + "application/vnd.wap.wmlscriptc", /* 0x15 */ + "application/vnd.wap.wta-eventc", /* 0x16 */ + "application/vnd.wap.uaprof", /* 0x17 */ + "application/vnd.wap.wtls-ca-certificate", /* 0x18 */ + "application/vnd.wap.wtls-user-certificate", /* 0x19 */ + "application/x-x509-ca-cert", /* 0x1A */ + "application/x-x509-user-cert", /* 0x1B */ + "image/*", /* 0x1C */ + "image/gif", /* 0x1D */ + "image/jpeg", /* 0x1E */ + "image/tiff", /* 0x1F */ + "image/png", /* 0x20 */ + "image/vnd.wap.wbmp", /* 0x21 */ + "application/vnd.wap.multipart.*", /* 0x22 */ + "application/vnd.wap.multipart.mixed", /* 0x23 */ + "application/vnd.wap.multipart.form-data", /* 0x24 */ + "application/vnd.wap.multipart.byteranges", /* 0x25 */ + "application/vnd.wap.multipart.alternative", /* 0x26 */ + "application/xml", /* 0x27 */ + "text/xml", /* 0x28 */ + "application/vnd.wap.wbxml", /* 0x29 */ + "application/x-x968-cross-cert", /* 0x2A */ + "application/x-x968-ca-cert", /* 0x2B */ + "application/x-x968-user-cert", /* 0x2C */ + "text/vnd.wap.si", /* 0x2D */ + "application/vnd.wap.sic", /* 0x2E */ + "text/vnd.wap.sl", /* 0x2F */ + "application/vnd.wap.slc", /* 0x30 */ + "text/vnd.wap.co", /* 0x31 */ + "application/vnd.wap.coc", /* 0x32 */ + "application/vnd.wap.multipart.related", /* 0x33 */ + "application/vnd.wap.sia", /* 0x34 */ + "text/vnd.wap.connectivity-xml", /* 0x35 */ + "application/vnd.wap.connectivity-wbxml", /* 0x36 */ + "application/pkcs7-mime", /* 0x37 */ + "application/vnd.wap.hashed-certificate", /* 0x38 */ + "application/vnd.wap.signed-certificate", /* 0x39 */ + "application/vnd.wap.cert-response", /* 0x3A */ + "application/xhtml+xml", /* 0x3B */ + "application/wml+xml", /* 0x3C */ + "text/css", /* 0x3D */ + "application/vnd.wap.mms-message", /* 0x3E */ + "application/vnd.wap.rollover-certificate", /* 0x3F */ + "application/vnd.wap.locc+wbxml", /* 0x40 */ + "application/vnd.wap.loc+xml", /* 0x41 */ + "application/vnd.syncml.dm+wbxml", /* 0x42 */ + "application/vnd.syncml.dm+xml", /* 0x43 */ + "application/vnd.syncml.notification", /* 0x44 */ + "application/vnd.wap.xhtml+xml", /* 0x45 */ + "application/vnd.wv.csp.cir", /* 0x46 */ + "application/vnd.oma.dd+xml", /* 0x47 */ + "application/vnd.oma.drm.message", /* 0x48 */ + "application/vnd.oma.drm.content", /* 0x49 */ + "application/vnd.oma.drm.rights+xml", /* 0x4A */ + "application/vnd.oma.drm.rights+wbxml", /* 0x4B */ + "application/vnd.wv.csp+xml", /* 0x4C */ + "application/vnd.wv.csp+wbxml", /* 0x4D */ + "application/vnd.syncml.ds.notification", /* 0x4E */ + "audio/*", /* 0x4F */ + "video/*", /* 0x50 */ + "application/vnd.oma.dd2+xml", /* 0x51 */ + "application/mikey" /* 0x52 */ + }; +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduHeaders.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduHeaders.java new file mode 100644 index 000000000..8e546feb3 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduHeaders.java @@ -0,0 +1,721 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import com.example.android.mmslib.InvalidHeaderValueException; + +import java.util.ArrayList; +import java.util.HashMap; + +public class PduHeaders { + /** + * All pdu header fields. + */ + public static final int BCC = 0x81; + public static final int CC = 0x82; + public static final int CONTENT_LOCATION = 0x83; + public static final int CONTENT_TYPE = 0x84; + public static final int DATE = 0x85; + public static final int DELIVERY_REPORT = 0x86; + public static final int DELIVERY_TIME = 0x87; + public static final int EXPIRY = 0x88; + public static final int FROM = 0x89; + public static final int MESSAGE_CLASS = 0x8A; + public static final int MESSAGE_ID = 0x8B; + public static final int MESSAGE_TYPE = 0x8C; + public static final int MMS_VERSION = 0x8D; + public static final int MESSAGE_SIZE = 0x8E; + public static final int PRIORITY = 0x8F; + + public static final int READ_REPLY = 0x90; + public static final int READ_REPORT = 0x90; + public static final int REPORT_ALLOWED = 0x91; + public static final int RESPONSE_STATUS = 0x92; + public static final int RESPONSE_TEXT = 0x93; + public static final int SENDER_VISIBILITY = 0x94; + public static final int STATUS = 0x95; + public static final int SUBJECT = 0x96; + public static final int TO = 0x97; + public static final int TRANSACTION_ID = 0x98; + public static final int RETRIEVE_STATUS = 0x99; + public static final int RETRIEVE_TEXT = 0x9A; + public static final int READ_STATUS = 0x9B; + public static final int REPLY_CHARGING = 0x9C; + public static final int REPLY_CHARGING_DEADLINE = 0x9D; + public static final int REPLY_CHARGING_ID = 0x9E; + public static final int REPLY_CHARGING_SIZE = 0x9F; + + public static final int PREVIOUSLY_SENT_BY = 0xA0; + public static final int PREVIOUSLY_SENT_DATE = 0xA1; + public static final int STORE = 0xA2; + public static final int MM_STATE = 0xA3; + public static final int MM_FLAGS = 0xA4; + public static final int STORE_STATUS = 0xA5; + public static final int STORE_STATUS_TEXT = 0xA6; + public static final int STORED = 0xA7; + public static final int ATTRIBUTES = 0xA8; + public static final int TOTALS = 0xA9; + public static final int MBOX_TOTALS = 0xAA; + public static final int QUOTAS = 0xAB; + public static final int MBOX_QUOTAS = 0xAC; + public static final int MESSAGE_COUNT = 0xAD; + public static final int CONTENT = 0xAE; + public static final int START = 0xAF; + + public static final int ADDITIONAL_HEADERS = 0xB0; + public static final int DISTRIBUTION_INDICATOR = 0xB1; + public static final int ELEMENT_DESCRIPTOR = 0xB2; + public static final int LIMIT = 0xB3; + public static final int RECOMMENDED_RETRIEVAL_MODE = 0xB4; + public static final int RECOMMENDED_RETRIEVAL_MODE_TEXT = 0xB5; + public static final int STATUS_TEXT = 0xB6; + public static final int APPLIC_ID = 0xB7; + public static final int REPLY_APPLIC_ID = 0xB8; + public static final int AUX_APPLIC_ID = 0xB9; + public static final int CONTENT_CLASS = 0xBA; + public static final int DRM_CONTENT = 0xBB; + public static final int ADAPTATION_ALLOWED = 0xBC; + public static final int REPLACE_ID = 0xBD; + public static final int CANCEL_ID = 0xBE; + public static final int CANCEL_STATUS = 0xBF; + + /** + * X-Mms-Message-Type field types. + */ + public static final int MESSAGE_TYPE_SEND_REQ = 0x80; + public static final int MESSAGE_TYPE_SEND_CONF = 0x81; + public static final int MESSAGE_TYPE_NOTIFICATION_IND = 0x82; + public static final int MESSAGE_TYPE_NOTIFYRESP_IND = 0x83; + public static final int MESSAGE_TYPE_RETRIEVE_CONF = 0x84; + public static final int MESSAGE_TYPE_ACKNOWLEDGE_IND = 0x85; + public static final int MESSAGE_TYPE_DELIVERY_IND = 0x86; + public static final int MESSAGE_TYPE_READ_REC_IND = 0x87; + public static final int MESSAGE_TYPE_READ_ORIG_IND = 0x88; + public static final int MESSAGE_TYPE_FORWARD_REQ = 0x89; + public static final int MESSAGE_TYPE_FORWARD_CONF = 0x8A; + public static final int MESSAGE_TYPE_MBOX_STORE_REQ = 0x8B; + public static final int MESSAGE_TYPE_MBOX_STORE_CONF = 0x8C; + public static final int MESSAGE_TYPE_MBOX_VIEW_REQ = 0x8D; + public static final int MESSAGE_TYPE_MBOX_VIEW_CONF = 0x8E; + public static final int MESSAGE_TYPE_MBOX_UPLOAD_REQ = 0x8F; + public static final int MESSAGE_TYPE_MBOX_UPLOAD_CONF = 0x90; + public static final int MESSAGE_TYPE_MBOX_DELETE_REQ = 0x91; + public static final int MESSAGE_TYPE_MBOX_DELETE_CONF = 0x92; + public static final int MESSAGE_TYPE_MBOX_DESCR = 0x93; + public static final int MESSAGE_TYPE_DELETE_REQ = 0x94; + public static final int MESSAGE_TYPE_DELETE_CONF = 0x95; + public static final int MESSAGE_TYPE_CANCEL_REQ = 0x96; + public static final int MESSAGE_TYPE_CANCEL_CONF = 0x97; + + /** + * X-Mms-Delivery-Report | + * X-Mms-Read-Report | + * X-Mms-Report-Allowed | + * X-Mms-Sender-Visibility | + * X-Mms-Store | + * X-Mms-Stored | + * X-Mms-Totals | + * X-Mms-Quotas | + * X-Mms-Distribution-Indicator | + * X-Mms-DRM-Content | + * X-Mms-Adaptation-Allowed | + * field types. + */ + public static final int VALUE_YES = 0x80; + public static final int VALUE_NO = 0x81; + + /** + * Delivery-Time | + * Expiry and Reply-Charging-Deadline | + * field type components. + */ + public static final int VALUE_ABSOLUTE_TOKEN = 0x80; + public static final int VALUE_RELATIVE_TOKEN = 0x81; + + /** + * X-Mms-MMS-Version field types. + */ + public static final int MMS_VERSION_1_3 = ((1 << 4) | 3); + public static final int MMS_VERSION_1_2 = ((1 << 4) | 2); + public static final int MMS_VERSION_1_1 = ((1 << 4) | 1); + public static final int MMS_VERSION_1_0 = ((1 << 4) | 0); + + // Current version is 1.2. + public static final int CURRENT_MMS_VERSION = MMS_VERSION_1_2; + + /** + * From field type components. + */ + public static final int FROM_ADDRESS_PRESENT_TOKEN = 0x80; + public static final int FROM_INSERT_ADDRESS_TOKEN = 0x81; + + public static final String FROM_ADDRESS_PRESENT_TOKEN_STR = "address-present-token"; + public static final String FROM_INSERT_ADDRESS_TOKEN_STR = "insert-address-token"; + + /** + * X-Mms-Status Field. + */ + public static final int STATUS_EXPIRED = 0x80; + public static final int STATUS_RETRIEVED = 0x81; + public static final int STATUS_REJECTED = 0x82; + public static final int STATUS_DEFERRED = 0x83; + public static final int STATUS_UNRECOGNIZED = 0x84; + public static final int STATUS_INDETERMINATE = 0x85; + public static final int STATUS_FORWARDED = 0x86; + public static final int STATUS_UNREACHABLE = 0x87; + + /** + * MM-Flags field type components. + */ + public static final int MM_FLAGS_ADD_TOKEN = 0x80; + public static final int MM_FLAGS_REMOVE_TOKEN = 0x81; + public static final int MM_FLAGS_FILTER_TOKEN = 0x82; + + /** + * X-Mms-Message-Class field types. + */ + public static final int MESSAGE_CLASS_PERSONAL = 0x80; + public static final int MESSAGE_CLASS_ADVERTISEMENT = 0x81; + public static final int MESSAGE_CLASS_INFORMATIONAL = 0x82; + public static final int MESSAGE_CLASS_AUTO = 0x83; + + public static final String MESSAGE_CLASS_PERSONAL_STR = "personal"; + public static final String MESSAGE_CLASS_ADVERTISEMENT_STR = "advertisement"; + public static final String MESSAGE_CLASS_INFORMATIONAL_STR = "informational"; + public static final String MESSAGE_CLASS_AUTO_STR = "auto"; + + /** + * X-Mms-Priority field types. + */ + public static final int PRIORITY_LOW = 0x80; + public static final int PRIORITY_NORMAL = 0x81; + public static final int PRIORITY_HIGH = 0x82; + + /** + * X-Mms-Response-Status field types. + */ + public static final int RESPONSE_STATUS_OK = 0x80; + public static final int RESPONSE_STATUS_ERROR_UNSPECIFIED = 0x81; + public static final int RESPONSE_STATUS_ERROR_SERVICE_DENIED = 0x82; + + public static final int RESPONSE_STATUS_ERROR_MESSAGE_FORMAT_CORRUPT = 0x83; + public static final int RESPONSE_STATUS_ERROR_SENDING_ADDRESS_UNRESOLVED = 0x84; + + public static final int RESPONSE_STATUS_ERROR_MESSAGE_NOT_FOUND = 0x85; + public static final int RESPONSE_STATUS_ERROR_NETWORK_PROBLEM = 0x86; + public static final int RESPONSE_STATUS_ERROR_CONTENT_NOT_ACCEPTED = 0x87; + public static final int RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE = 0x88; + public static final int RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0; + + public static final int RESPONSE_STATUS_ERROR_TRANSIENT_SENDNG_ADDRESS_UNRESOLVED = 0xC1; + public static final int RESPONSE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC2; + public static final int RESPONSE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC3; + public static final int RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS = 0xC4; + + public static final int RESPONSE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_SENDING_ADDRESS_UNRESOLVED = 0xE3; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE4; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_CONTENT_NOT_ACCEPTED = 0xE5; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_LIMITATIONS_NOT_MET = 0xE6; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_REQUEST_NOT_ACCEPTED = 0xE6; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_FORWARDING_DENIED = 0xE8; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_NOT_SUPPORTED = 0xE9; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_ADDRESS_HIDING_NOT_SUPPORTED = 0xEA; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID = 0xEB; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_END = 0xFF; + + /** + * X-Mms-Retrieve-Status field types. + */ + public static final int RETRIEVE_STATUS_OK = 0x80; + public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0; + public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC1; + public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC2; + public static final int RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0; + public static final int RETRIEVE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1; + public static final int RETRIEVE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE2; + public static final int RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED = 0xE3; + public static final int RETRIEVE_STATUS_ERROR_END = 0xFF; + + /** + * X-Mms-Sender-Visibility field types. + */ + public static final int SENDER_VISIBILITY_HIDE = 0x80; + public static final int SENDER_VISIBILITY_SHOW = 0x81; + + /** + * X-Mms-Read-Status field types. + */ + public static final int READ_STATUS_READ = 0x80; + public static final int READ_STATUS__DELETED_WITHOUT_BEING_READ = 0x81; + + /** + * X-Mms-Cancel-Status field types. + */ + public static final int CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED = 0x80; + public static final int CANCEL_STATUS_REQUEST_CORRUPTED = 0x81; + + /** + * X-Mms-Reply-Charging field types. + */ + public static final int REPLY_CHARGING_REQUESTED = 0x80; + public static final int REPLY_CHARGING_REQUESTED_TEXT_ONLY = 0x81; + public static final int REPLY_CHARGING_ACCEPTED = 0x82; + public static final int REPLY_CHARGING_ACCEPTED_TEXT_ONLY = 0x83; + + /** + * X-Mms-MM-State field types. + */ + public static final int MM_STATE_DRAFT = 0x80; + public static final int MM_STATE_SENT = 0x81; + public static final int MM_STATE_NEW = 0x82; + public static final int MM_STATE_RETRIEVED = 0x83; + public static final int MM_STATE_FORWARDED = 0x84; + + /** + * X-Mms-Recommended-Retrieval-Mode field types. + */ + public static final int RECOMMENDED_RETRIEVAL_MODE_MANUAL = 0x80; + + /** + * X-Mms-Content-Class field types. + */ + public static final int CONTENT_CLASS_TEXT = 0x80; + public static final int CONTENT_CLASS_IMAGE_BASIC = 0x81; + public static final int CONTENT_CLASS_IMAGE_RICH = 0x82; + public static final int CONTENT_CLASS_VIDEO_BASIC = 0x83; + public static final int CONTENT_CLASS_VIDEO_RICH = 0x84; + public static final int CONTENT_CLASS_MEGAPIXEL = 0x85; + public static final int CONTENT_CLASS_CONTENT_BASIC = 0x86; + public static final int CONTENT_CLASS_CONTENT_RICH = 0x87; + + /** + * X-Mms-Store-Status field types. + */ + public static final int STORE_STATUS_SUCCESS = 0x80; + public static final int STORE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0; + public static final int STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC1; + public static final int STORE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0; + public static final int STORE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1; + public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2; + public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE3; + public static final int STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL = 0xE4; + public static final int STORE_STATUS_ERROR_END = 0xFF; + + /** + * The map contains the value of all headers. + */ + private HashMap mHeaderMap = null; + + /** + * Constructor of PduHeaders. + */ + public PduHeaders() { + mHeaderMap = new HashMap(); + } + + /** + * Get octet value by header field. + * + * @param field the field + * @return the octet value of the pdu header + * with specified header field. Return 0 if + * the value is not set. + */ + protected int getOctet(int field) { + Integer octet = (Integer) mHeaderMap.get(field); + if (null == octet) { + return 0; + } + + return octet; + } + + /** + * Set octet value to pdu header by header field. + * + * @param value the value + * @param field the field + * @throws InvalidHeaderValueException if the value is invalid. + */ + protected void setOctet(int value, int field) + throws InvalidHeaderValueException{ + /** + * Check whether this field can be set for specific + * header and check validity of the field. + */ + switch (field) { + case REPORT_ALLOWED: + case ADAPTATION_ALLOWED: + case DELIVERY_REPORT: + case DRM_CONTENT: + case DISTRIBUTION_INDICATOR: + case QUOTAS: + case READ_REPORT: + case STORE: + case STORED: + case TOTALS: + case SENDER_VISIBILITY: + if ((VALUE_YES != value) && (VALUE_NO != value)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case READ_STATUS: + if ((READ_STATUS_READ != value) && + (READ_STATUS__DELETED_WITHOUT_BEING_READ != value)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case CANCEL_STATUS: + if ((CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED != value) && + (CANCEL_STATUS_REQUEST_CORRUPTED != value)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case PRIORITY: + if ((value < PRIORITY_LOW) || (value > PRIORITY_HIGH)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case STATUS: + if ((value < STATUS_EXPIRED) || (value > STATUS_UNREACHABLE)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case REPLY_CHARGING: + if ((value < REPLY_CHARGING_REQUESTED) + || (value > REPLY_CHARGING_ACCEPTED_TEXT_ONLY)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case MM_STATE: + if ((value < MM_STATE_DRAFT) || (value > MM_STATE_FORWARDED)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case RECOMMENDED_RETRIEVAL_MODE: + if (RECOMMENDED_RETRIEVAL_MODE_MANUAL != value) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case CONTENT_CLASS: + if ((value < CONTENT_CLASS_TEXT) + || (value > CONTENT_CLASS_CONTENT_RICH)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case RETRIEVE_STATUS: + // According to oma-ts-mms-enc-v1_3, section 7.3.50, we modify the invalid value. + if ((value > RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) && + (value < RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE)) { + value = RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE; + } else if ((value > RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED) && + (value <= RETRIEVE_STATUS_ERROR_END)) { + value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE; + } else if ((value < RETRIEVE_STATUS_OK) || + ((value > RETRIEVE_STATUS_OK) && + (value < RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE)) || + (value > RETRIEVE_STATUS_ERROR_END)) { + value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE; + } + break; + case STORE_STATUS: + // According to oma-ts-mms-enc-v1_3, section 7.3.58, we modify the invalid value. + if ((value > STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) && + (value < STORE_STATUS_ERROR_PERMANENT_FAILURE)) { + value = STORE_STATUS_ERROR_TRANSIENT_FAILURE; + } else if ((value > STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL) && + (value <= STORE_STATUS_ERROR_END)) { + value = STORE_STATUS_ERROR_PERMANENT_FAILURE; + } else if ((value < STORE_STATUS_SUCCESS) || + ((value > STORE_STATUS_SUCCESS) && + (value < STORE_STATUS_ERROR_TRANSIENT_FAILURE)) || + (value > STORE_STATUS_ERROR_END)) { + value = STORE_STATUS_ERROR_PERMANENT_FAILURE; + } + break; + case RESPONSE_STATUS: + // According to oma-ts-mms-enc-v1_3, section 7.3.48, we modify the invalid value. + if ((value > RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS) && + (value < RESPONSE_STATUS_ERROR_PERMANENT_FAILURE)) { + value = RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE; + } else if (((value > RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID) && + (value <= RESPONSE_STATUS_ERROR_PERMANENT_END)) || + (value < RESPONSE_STATUS_OK) || + ((value > RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE) && + (value < RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE)) || + (value > RESPONSE_STATUS_ERROR_PERMANENT_END)) { + value = RESPONSE_STATUS_ERROR_PERMANENT_FAILURE; + } + break; + case MMS_VERSION: + if ((value < MMS_VERSION_1_0)|| (value > MMS_VERSION_1_3)) { + value = CURRENT_MMS_VERSION; // Current version is the default value. + } + break; + case MESSAGE_TYPE: + if ((value < MESSAGE_TYPE_SEND_REQ) || (value > MESSAGE_TYPE_CANCEL_CONF)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + default: + // This header value should not be Octect. + throw new RuntimeException("Invalid header field!"); + } + mHeaderMap.put(field, value); + } + + /** + * Get TextString value by header field. + * + * @param field the field + * @return the TextString value of the pdu header + * with specified header field + */ + protected byte[] getTextString(int field) { + return (byte[]) mHeaderMap.get(field); + } + + /** + * Set TextString value to pdu header by header field. + * + * @param value the value + * @param field the field + * @return the TextString value of the pdu header + * with specified header field + * @throws NullPointerException if the value is null. + */ + protected void setTextString(byte[] value, int field) { + /** + * Check whether this field can be set for specific + * header and check validity of the field. + */ + if (null == value) { + throw new NullPointerException(); + } + + switch (field) { + case TRANSACTION_ID: + case REPLY_CHARGING_ID: + case AUX_APPLIC_ID: + case APPLIC_ID: + case REPLY_APPLIC_ID: + case MESSAGE_ID: + case REPLACE_ID: + case CANCEL_ID: + case CONTENT_LOCATION: + case MESSAGE_CLASS: + case CONTENT_TYPE: + break; + default: + // This header value should not be Text-String. + throw new RuntimeException("Invalid header field!"); + } + mHeaderMap.put(field, value); + } + + /** + * Get EncodedStringValue value by header field. + * + * @param field the field + * @return the EncodedStringValue value of the pdu header + * with specified header field + */ + protected EncodedStringValue getEncodedStringValue(int field) { + return (EncodedStringValue) mHeaderMap.get(field); + } + + /** + * Get TO, CC or BCC header value. + * + * @param field the field + * @return the EncodeStringValue array of the pdu header + * with specified header field + */ + protected EncodedStringValue[] getEncodedStringValues(int field) { + ArrayList list = + (ArrayList) mHeaderMap.get(field); + if (null == list) { + return null; + } + EncodedStringValue[] values = new EncodedStringValue[list.size()]; + return list.toArray(values); + } + + /** + * Set EncodedStringValue value to pdu header by header field. + * + * @param value the value + * @param field the field + * @return the EncodedStringValue value of the pdu header + * with specified header field + * @throws NullPointerException if the value is null. + */ + protected void setEncodedStringValue(EncodedStringValue value, int field) { + /** + * Check whether this field can be set for specific + * header and check validity of the field. + */ + if (null == value) { + throw new NullPointerException(); + } + + switch (field) { + case SUBJECT: + case RECOMMENDED_RETRIEVAL_MODE_TEXT: + case RETRIEVE_TEXT: + case STATUS_TEXT: + case STORE_STATUS_TEXT: + case RESPONSE_TEXT: + case FROM: + case PREVIOUSLY_SENT_BY: + case MM_FLAGS: + break; + default: + // This header value should not be Encoded-String-Value. + throw new RuntimeException("Invalid header field!"); + } + + mHeaderMap.put(field, value); + } + + /** + * Set TO, CC or BCC header value. + * + * @param value the value + * @param field the field + * @return the EncodedStringValue value array of the pdu header + * with specified header field + * @throws NullPointerException if the value is null. + */ + protected void setEncodedStringValues(EncodedStringValue[] value, int field) { + /** + * Check whether this field can be set for specific + * header and check validity of the field. + */ + if (null == value) { + throw new NullPointerException(); + } + + switch (field) { + case BCC: + case CC: + case TO: + break; + default: + // This header value should not be Encoded-String-Value. + throw new RuntimeException("Invalid header field!"); + } + + ArrayList list = new ArrayList(); + for (int i = 0; i < value.length; i++) { + list.add(value[i]); + } + mHeaderMap.put(field, list); + } + + /** + * Append one EncodedStringValue to another. + * + * @param value the EncodedStringValue to append + * @param field the field + * @throws NullPointerException if the value is null. + */ + protected void appendEncodedStringValue(EncodedStringValue value, + int field) { + if (null == value) { + throw new NullPointerException(); + } + + switch (field) { + case BCC: + case CC: + case TO: + break; + default: + throw new RuntimeException("Invalid header field!"); + } + + ArrayList list = + (ArrayList) mHeaderMap.get(field); + if (null == list) { + list = new ArrayList(); + } + list.add(value); + mHeaderMap.put(field, list); + } + + /** + * Get LongInteger value by header field. + * + * @param field the field + * @return the LongInteger value of the pdu header + * with specified header field. if return -1, the + * field is not existed in pdu header. + */ + protected long getLongInteger(int field) { + Long longInteger = (Long) mHeaderMap.get(field); + if (null == longInteger) { + return -1; + } + + return longInteger.longValue(); + } + + /** + * Set LongInteger value to pdu header by header field. + * + * @param value the value + * @param field the field + */ + protected void setLongInteger(long value, int field) { + /** + * Check whether this field can be set for specific + * header and check validity of the field. + */ + switch (field) { + case DATE: + case REPLY_CHARGING_SIZE: + case MESSAGE_SIZE: + case MESSAGE_COUNT: + case START: + case LIMIT: + case DELIVERY_TIME: + case EXPIRY: + case REPLY_CHARGING_DEADLINE: + case PREVIOUSLY_SENT_DATE: + break; + default: + // This header value should not be LongInteger. + throw new RuntimeException("Invalid header field!"); + } + mHeaderMap.put(field, value); + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduParser.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduParser.java new file mode 100755 index 000000000..f1aa4d763 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduParser.java @@ -0,0 +1,2011 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import android.util.Log; + +import com.example.android.mmslib.ContentType; +import com.example.android.mmslib.InvalidHeaderValueException; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.HashMap; + +public class PduParser { + /** + * The next are WAP values defined in WSP specification. + */ + private static final int QUOTE = 127; + private static final int LENGTH_QUOTE = 31; + private static final int TEXT_MIN = 32; + private static final int TEXT_MAX = 127; + private static final int SHORT_INTEGER_MAX = 127; + private static final int SHORT_LENGTH_MAX = 30; + private static final int LONG_INTEGER_LENGTH_MAX = 8; + private static final int QUOTED_STRING_FLAG = 34; + private static final int END_STRING_FLAG = 0x00; + //The next two are used by the interface "parseWapString" to + //distinguish Text-String and Quoted-String. + private static final int TYPE_TEXT_STRING = 0; + private static final int TYPE_QUOTED_STRING = 1; + private static final int TYPE_TOKEN_STRING = 2; + + /** + * Specify the part position. + */ + private static final int THE_FIRST_PART = 0; + private static final int THE_LAST_PART = 1; + + /** + * The pdu data. + */ + private ByteArrayInputStream mPduDataStream = null; + + /** + * Store pdu headers + */ + private PduHeaders mHeaders = null; + + /** + * Store pdu parts. + */ + private PduBody mBody = null; + + /** + * Store the "type" parameter in "Content-Type" header field. + */ + private static byte[] mTypeParam = null; + + /** + * Store the "start" parameter in "Content-Type" header field. + */ + private static byte[] mStartParam = null; + + /** + * The log tag. + */ + private static final String LOG_TAG = "PduParser"; + private static final boolean DEBUG = false; + private static final boolean LOCAL_LOGV = false; + + /** + * Whether to parse content-disposition part header + */ + private final boolean mParseContentDisposition; + + /** + * Constructor. + * + * @param pduDataStream pdu data to be parsed + * @param parseContentDisposition whether to parse the Content-Disposition part header + */ + public PduParser(byte[] pduDataStream, boolean parseContentDisposition) { + mPduDataStream = new ByteArrayInputStream(pduDataStream); + mParseContentDisposition = parseContentDisposition; + } + + /** + * Parse the pdu. + * + * @return the pdu structure if parsing successfully. + * null if parsing error happened or mandatory fields are not set. + */ + public GenericPdu parse(){ + if (mPduDataStream == null) { + return null; + } + + /* parse headers */ + mHeaders = parseHeaders(mPduDataStream); + if (null == mHeaders) { + // Parse headers failed. + return null; + } + + /* get the message type */ + int messageType = mHeaders.getOctet(PduHeaders.MESSAGE_TYPE); + + /* check mandatory header fields */ + if (false == checkMandatoryHeader(mHeaders)) { + log("check mandatory headers failed!"); + return null; + } + + if ((PduHeaders.MESSAGE_TYPE_SEND_REQ == messageType) || + (PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF == messageType)) { + /* need to parse the parts */ + mBody = parseParts(mPduDataStream); + if (null == mBody) { + // Parse parts failed. + return null; + } + } + + switch (messageType) { + case PduHeaders.MESSAGE_TYPE_SEND_REQ: + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parse: MESSAGE_TYPE_SEND_REQ"); + } + SendReq sendReq = new SendReq(mHeaders, mBody); + return sendReq; + case PduHeaders.MESSAGE_TYPE_SEND_CONF: + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parse: MESSAGE_TYPE_SEND_CONF"); + } + SendConf sendConf = new SendConf(mHeaders); + return sendConf; + case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND: + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parse: MESSAGE_TYPE_NOTIFICATION_IND"); + } + NotificationInd notificationInd = + new NotificationInd(mHeaders); + return notificationInd; + case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND: + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parse: MESSAGE_TYPE_NOTIFYRESP_IND"); + } + NotifyRespInd notifyRespInd = + new NotifyRespInd(mHeaders); + return notifyRespInd; + case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF: + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parse: MESSAGE_TYPE_RETRIEVE_CONF"); + } + RetrieveConf retrieveConf = + new RetrieveConf(mHeaders, mBody); + + byte[] contentType = retrieveConf.getContentType(); + if (null == contentType) { + return null; + } + String ctTypeStr = new String(contentType); + if (ctTypeStr.equals(ContentType.MULTIPART_MIXED) + || ctTypeStr.equals(ContentType.MULTIPART_RELATED) + || ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) { + // The MMS content type must be "application/vnd.wap.multipart.mixed" + // or "application/vnd.wap.multipart.related" + // or "application/vnd.wap.multipart.alternative" + return retrieveConf; + } else if (ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) { + // "application/vnd.wap.multipart.alternative" + // should take only the first part. + PduPart firstPart = mBody.getPart(0); + mBody.removeAll(); + mBody.addPart(0, firstPart); + return retrieveConf; + } + return null; + case PduHeaders.MESSAGE_TYPE_DELIVERY_IND: + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parse: MESSAGE_TYPE_DELIVERY_IND"); + } + DeliveryInd deliveryInd = + new DeliveryInd(mHeaders); + return deliveryInd; + case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND: + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parse: MESSAGE_TYPE_ACKNOWLEDGE_IND"); + } + AcknowledgeInd acknowledgeInd = + new AcknowledgeInd(mHeaders); + return acknowledgeInd; + case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND: + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parse: MESSAGE_TYPE_READ_ORIG_IND"); + } + ReadOrigInd readOrigInd = + new ReadOrigInd(mHeaders); + return readOrigInd; + case PduHeaders.MESSAGE_TYPE_READ_REC_IND: + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parse: MESSAGE_TYPE_READ_REC_IND"); + } + ReadRecInd readRecInd = + new ReadRecInd(mHeaders); + return readRecInd; + default: + log("Parser doesn't support this message type in this version!"); + return null; + } + } + + /** + * Parse pdu headers. + * + * @param pduDataStream pdu data input stream + * @return headers in PduHeaders structure, null when parse fail + */ + protected PduHeaders parseHeaders(ByteArrayInputStream pduDataStream){ + if (pduDataStream == null) { + return null; + } + boolean keepParsing = true; + PduHeaders headers = new PduHeaders(); + + while (keepParsing && (pduDataStream.available() > 0)) { + pduDataStream.mark(1); + int headerField = extractByteValue(pduDataStream); + /* parse custom text header */ + if ((headerField >= TEXT_MIN) && (headerField <= TEXT_MAX)) { + pduDataStream.reset(); + byte [] bVal = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "TextHeader: " + new String(bVal)); + } + /* we should ignore it at the moment */ + continue; + } + switch (headerField) { + case PduHeaders.MESSAGE_TYPE: + { + int messageType = extractByteValue(pduDataStream); + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: messageType: " + messageType); + } + switch (messageType) { + // We don't support these kind of messages now. + case PduHeaders.MESSAGE_TYPE_FORWARD_REQ: + case PduHeaders.MESSAGE_TYPE_FORWARD_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ: + case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ: + case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ: + case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ: + case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_DESCR: + case PduHeaders.MESSAGE_TYPE_DELETE_REQ: + case PduHeaders.MESSAGE_TYPE_DELETE_CONF: + case PduHeaders.MESSAGE_TYPE_CANCEL_REQ: + case PduHeaders.MESSAGE_TYPE_CANCEL_CONF: + return null; + } + try { + headers.setOctet(messageType, headerField); + } catch(InvalidHeaderValueException e) { + log("Set invalid Octet value: " + messageType + + " into the header filed: " + headerField); + return null; + } catch(RuntimeException e) { + log(headerField + "is not Octet header field!"); + return null; + } + break; + } + /* Octect value */ + case PduHeaders.REPORT_ALLOWED: + case PduHeaders.ADAPTATION_ALLOWED: + case PduHeaders.DELIVERY_REPORT: + case PduHeaders.DRM_CONTENT: + case PduHeaders.DISTRIBUTION_INDICATOR: + case PduHeaders.QUOTAS: + case PduHeaders.READ_REPORT: + case PduHeaders.STORE: + case PduHeaders.STORED: + case PduHeaders.TOTALS: + case PduHeaders.SENDER_VISIBILITY: + case PduHeaders.READ_STATUS: + case PduHeaders.CANCEL_STATUS: + case PduHeaders.PRIORITY: + case PduHeaders.STATUS: + case PduHeaders.REPLY_CHARGING: + case PduHeaders.MM_STATE: + case PduHeaders.RECOMMENDED_RETRIEVAL_MODE: + case PduHeaders.CONTENT_CLASS: + case PduHeaders.RETRIEVE_STATUS: + case PduHeaders.STORE_STATUS: + /** + * The following field has a different value when + * used in the M-Mbox-Delete.conf and M-Delete.conf PDU. + * For now we ignore this fact, since we do not support these PDUs + */ + case PduHeaders.RESPONSE_STATUS: + { + int value = extractByteValue(pduDataStream); + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: byte: " + headerField + " value: " + + value); + } + + try { + headers.setOctet(value, headerField); + } catch(InvalidHeaderValueException e) { + log("Set invalid Octet value: " + value + + " into the header filed: " + headerField); + return null; + } catch(RuntimeException e) { + log(headerField + "is not Octet header field!"); + return null; + } + break; + } + + /* Long-Integer */ + case PduHeaders.DATE: + case PduHeaders.REPLY_CHARGING_SIZE: + case PduHeaders.MESSAGE_SIZE: + { + try { + long value = parseLongInteger(pduDataStream); + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: longint: " + headerField + " value: " + + value); + } + headers.setLongInteger(value, headerField); + } catch(RuntimeException e) { + log(headerField + "is not Long-Integer header field!"); + return null; + } + break; + } + + /* Integer-Value */ + case PduHeaders.MESSAGE_COUNT: + case PduHeaders.START: + case PduHeaders.LIMIT: + { + try { + long value = parseIntegerValue(pduDataStream); + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: int: " + headerField + " value: " + + value); + } + headers.setLongInteger(value, headerField); + } catch(RuntimeException e) { + log(headerField + "is not Long-Integer header field!"); + return null; + } + break; + } + + /* Text-String */ + case PduHeaders.TRANSACTION_ID: + case PduHeaders.REPLY_CHARGING_ID: + case PduHeaders.AUX_APPLIC_ID: + case PduHeaders.APPLIC_ID: + case PduHeaders.REPLY_APPLIC_ID: + /** + * The next three header fields are email addresses + * as defined in RFC2822, + * not including the characters "<" and ">" + */ + case PduHeaders.MESSAGE_ID: + case PduHeaders.REPLACE_ID: + case PduHeaders.CANCEL_ID: + /** + * The following field has a different value when + * used in the M-Mbox-Delete.conf and M-Delete.conf PDU. + * For now we ignore this fact, since we do not support these PDUs + */ + case PduHeaders.CONTENT_LOCATION: + { + byte[] value = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if (null != value) { + try { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: string: " + headerField + " value: " + + new String(value)); + } + headers.setTextString(value, headerField); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Text-String header field!"); + return null; + } + } + break; + } + + /* Encoded-string-value */ + case PduHeaders.SUBJECT: + case PduHeaders.RECOMMENDED_RETRIEVAL_MODE_TEXT: + case PduHeaders.RETRIEVE_TEXT: + case PduHeaders.STATUS_TEXT: + case PduHeaders.STORE_STATUS_TEXT: + /* the next one is not support + * M-Mbox-Delete.conf and M-Delete.conf now */ + case PduHeaders.RESPONSE_TEXT: + { + EncodedStringValue value = + parseEncodedStringValue(pduDataStream); + if (null != value) { + try { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: encoded string: " + headerField + + " value: " + value.getString()); + } + headers.setEncodedStringValue(value, headerField); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch (RuntimeException e) { + log(headerField + "is not Encoded-String-Value header field!"); + return null; + } + } + break; + } + + /* Addressing model */ + case PduHeaders.BCC: + case PduHeaders.CC: + case PduHeaders.TO: + { + EncodedStringValue value = + parseEncodedStringValue(pduDataStream); + if (null != value) { + byte[] address = value.getTextString(); + if (null != address) { + String str = new String(address); + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: (to/cc/bcc) address: " + headerField + + " value: " + str); + } + int endIndex = str.indexOf("/"); + if (endIndex > 0) { + str = str.substring(0, endIndex); + } + try { + value.setTextString(str.getBytes()); + } catch(NullPointerException e) { + log("null pointer error!"); + return null; + } + } + + try { + headers.appendEncodedStringValue(value, headerField); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Encoded-String-Value header field!"); + return null; + } + } + break; + } + + /* Value-length + * (Absolute-token Date-value | Relative-token Delta-seconds-value) */ + case PduHeaders.DELIVERY_TIME: + case PduHeaders.EXPIRY: + case PduHeaders.REPLY_CHARGING_DEADLINE: + { + /* parse Value-length */ + parseValueLength(pduDataStream); + + /* Absolute-token or Relative-token */ + int token = extractByteValue(pduDataStream); + + /* Date-value or Delta-seconds-value */ + long timeValue; + try { + timeValue = parseLongInteger(pduDataStream); + } catch(RuntimeException e) { + log(headerField + "is not Long-Integer header field!"); + return null; + } + if (PduHeaders.VALUE_RELATIVE_TOKEN == token) { + /* need to convert the Delta-seconds-value + * into Date-value */ + timeValue = System.currentTimeMillis()/1000 + timeValue; + } + + try { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: time value: " + headerField + + " value: " + timeValue); + } + headers.setLongInteger(timeValue, headerField); + } catch(RuntimeException e) { + log(headerField + "is not Long-Integer header field!"); + return null; + } + break; + } + + case PduHeaders.FROM: { + /* From-value = + * Value-length + * (Address-present-token Encoded-string-value | Insert-address-token) + */ + EncodedStringValue from = null; + parseValueLength(pduDataStream); /* parse value-length */ + + /* Address-present-token or Insert-address-token */ + int fromToken = extractByteValue(pduDataStream); + + /* Address-present-token or Insert-address-token */ + if (PduHeaders.FROM_ADDRESS_PRESENT_TOKEN == fromToken) { + /* Encoded-string-value */ + from = parseEncodedStringValue(pduDataStream); + if (null != from) { + byte[] address = from.getTextString(); + if (null != address) { + String str = new String(address); + int endIndex = str.indexOf("/"); + if (endIndex > 0) { + str = str.substring(0, endIndex); + } + try { + from.setTextString(str.getBytes()); + } catch(NullPointerException e) { + log("null pointer error!"); + return null; + } + } + } + } else { + try { + from = new EncodedStringValue( + PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes()); + } catch(NullPointerException e) { + log(headerField + "is not Encoded-String-Value header field!"); + return null; + } + } + + try { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: from address: " + headerField + + " value: " + from.getString()); + } + headers.setEncodedStringValue(from, PduHeaders.FROM); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Encoded-String-Value header field!"); + return null; + } + break; + } + + case PduHeaders.MESSAGE_CLASS: { + /* Message-class-value = Class-identifier | Token-text */ + pduDataStream.mark(1); + int messageClass = extractByteValue(pduDataStream); + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: MESSAGE_CLASS: " + headerField + + " value: " + messageClass); + } + + if (messageClass >= PduHeaders.MESSAGE_CLASS_PERSONAL) { + /* Class-identifier */ + try { + if (PduHeaders.MESSAGE_CLASS_PERSONAL == messageClass) { + headers.setTextString( + PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes(), + PduHeaders.MESSAGE_CLASS); + } else if (PduHeaders.MESSAGE_CLASS_ADVERTISEMENT == messageClass) { + headers.setTextString( + PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes(), + PduHeaders.MESSAGE_CLASS); + } else if (PduHeaders.MESSAGE_CLASS_INFORMATIONAL == messageClass) { + headers.setTextString( + PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes(), + PduHeaders.MESSAGE_CLASS); + } else if (PduHeaders.MESSAGE_CLASS_AUTO == messageClass) { + headers.setTextString( + PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes(), + PduHeaders.MESSAGE_CLASS); + } + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Text-String header field!"); + return null; + } + } else { + /* Token-text */ + pduDataStream.reset(); + byte[] messageClassString = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if (null != messageClassString) { + try { + headers.setTextString(messageClassString, PduHeaders.MESSAGE_CLASS); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Text-String header field!"); + return null; + } + } + } + break; + } + + case PduHeaders.MMS_VERSION: { + int version = parseShortInteger(pduDataStream); + + try { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: MMS_VERSION: " + headerField + + " value: " + version); + } + headers.setOctet(version, PduHeaders.MMS_VERSION); + } catch(InvalidHeaderValueException e) { + log("Set invalid Octet value: " + version + + " into the header filed: " + headerField); + return null; + } catch(RuntimeException e) { + log(headerField + "is not Octet header field!"); + return null; + } + break; + } + + case PduHeaders.PREVIOUSLY_SENT_BY: { + /* Previously-sent-by-value = + * Value-length Forwarded-count-value Encoded-string-value */ + /* parse value-length */ + parseValueLength(pduDataStream); + + /* parse Forwarded-count-value */ + try { + parseIntegerValue(pduDataStream); + } catch(RuntimeException e) { + log(headerField + " is not Integer-Value"); + return null; + } + + /* parse Encoded-string-value */ + EncodedStringValue previouslySentBy = + parseEncodedStringValue(pduDataStream); + if (null != previouslySentBy) { + try { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: PREVIOUSLY_SENT_BY: " + headerField + + " value: " + previouslySentBy.getString()); + } + headers.setEncodedStringValue(previouslySentBy, + PduHeaders.PREVIOUSLY_SENT_BY); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Encoded-String-Value header field!"); + return null; + } + } + break; + } + + case PduHeaders.PREVIOUSLY_SENT_DATE: { + /* Previously-sent-date-value = + * Value-length Forwarded-count-value Date-value */ + /* parse value-length */ + parseValueLength(pduDataStream); + + /* parse Forwarded-count-value */ + try { + parseIntegerValue(pduDataStream); + } catch(RuntimeException e) { + log(headerField + " is not Integer-Value"); + return null; + } + + /* Date-value */ + try { + long perviouslySentDate = parseLongInteger(pduDataStream); + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: PREVIOUSLY_SENT_DATE: " + headerField + + " value: " + perviouslySentDate); + } + headers.setLongInteger(perviouslySentDate, + PduHeaders.PREVIOUSLY_SENT_DATE); + } catch(RuntimeException e) { + log(headerField + "is not Long-Integer header field!"); + return null; + } + break; + } + + case PduHeaders.MM_FLAGS: { + /* MM-flags-value = + * Value-length + * ( Add-token | Remove-token | Filter-token ) + * Encoded-string-value + */ + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: MM_FLAGS: " + headerField + + " NOT REALLY SUPPORTED"); + } + + /* parse Value-length */ + parseValueLength(pduDataStream); + + /* Add-token | Remove-token | Filter-token */ + extractByteValue(pduDataStream); + + /* Encoded-string-value */ + parseEncodedStringValue(pduDataStream); + + /* not store this header filed in "headers", + * because now PduHeaders doesn't support it */ + break; + } + + /* Value-length + * (Message-total-token | Size-total-token) Integer-Value */ + case PduHeaders.MBOX_TOTALS: + case PduHeaders.MBOX_QUOTAS: + { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: MBOX_TOTALS: " + headerField); + } + /* Value-length */ + parseValueLength(pduDataStream); + + /* Message-total-token | Size-total-token */ + extractByteValue(pduDataStream); + + /*Integer-Value*/ + try { + parseIntegerValue(pduDataStream); + } catch(RuntimeException e) { + log(headerField + " is not Integer-Value"); + return null; + } + + /* not store these headers filed in "headers", + because now PduHeaders doesn't support them */ + break; + } + + case PduHeaders.ELEMENT_DESCRIPTOR: { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: ELEMENT_DESCRIPTOR: " + headerField); + } + parseContentType(pduDataStream, null); + + /* not store this header filed in "headers", + because now PduHeaders doesn't support it */ + break; + } + + case PduHeaders.CONTENT_TYPE: { + HashMap map = + new HashMap(); + byte[] contentType = + parseContentType(pduDataStream, map); + + if (null != contentType) { + try { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: CONTENT_TYPE: " + headerField + + contentType.toString()); + } + headers.setTextString(contentType, PduHeaders.CONTENT_TYPE); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Text-String header field!"); + return null; + } + } + + /* get start parameter */ + mStartParam = (byte[]) map.get(PduPart.P_START); + + /* get charset parameter */ + mTypeParam= (byte[]) map.get(PduPart.P_TYPE); + + keepParsing = false; + break; + } + + case PduHeaders.CONTENT: + case PduHeaders.ADDITIONAL_HEADERS: + case PduHeaders.ATTRIBUTES: + default: { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "parseHeaders: Unknown header: " + headerField); + } + log("Unknown header"); + } + } + } + + return headers; + } + + /** + * Parse pdu parts. + * + * @param pduDataStream pdu data input stream + * @return parts in PduBody structure + */ + protected PduBody parseParts(ByteArrayInputStream pduDataStream) { + if (pduDataStream == null) { + return null; + } + + int count = parseUnsignedInt(pduDataStream); // get the number of parts + PduBody body = new PduBody(); + + for (int i = 0 ; i < count ; i++) { + int headerLength = parseUnsignedInt(pduDataStream); + int dataLength = parseUnsignedInt(pduDataStream); + PduPart part = new PduPart(); + int startPos = pduDataStream.available(); + if (startPos <= 0) { + // Invalid part. + return null; + } + + /* parse part's content-type */ + HashMap map = new HashMap(); + byte[] contentType = parseContentType(pduDataStream, map); + if (null != contentType) { + part.setContentType(contentType); + } else { + part.setContentType((PduContentTypes.contentTypes[0]).getBytes()); //"*/*" + } + + /* get name parameter */ + byte[] name = (byte[]) map.get(PduPart.P_NAME); + if (null != name) { + part.setName(name); + } + + /* get charset parameter */ + Integer charset = (Integer) map.get(PduPart.P_CHARSET); + if (null != charset) { + part.setCharset(charset); + } + + /* parse part's headers */ + int endPos = pduDataStream.available(); + int partHeaderLen = headerLength - (startPos - endPos); + if (partHeaderLen > 0) { + if (false == parsePartHeaders(pduDataStream, part, partHeaderLen)) { + // Parse part header faild. + return null; + } + } else if (partHeaderLen < 0) { + // Invalid length of content-type. + return null; + } + + /* FIXME: check content-id, name, filename and content location, + * if not set anyone of them, generate a default content-location + */ + if ((null == part.getContentLocation()) + && (null == part.getName()) + && (null == part.getFilename()) + && (null == part.getContentId())) { + part.setContentLocation(Long.toOctalString( + System.currentTimeMillis()).getBytes()); + } + + /* get part's data */ + if (dataLength > 0) { + byte[] partData = new byte[dataLength]; + String partContentType = new String(part.getContentType()); + pduDataStream.read(partData, 0, dataLength); + if (partContentType.equalsIgnoreCase(ContentType.MULTIPART_ALTERNATIVE)) { + // parse "multipart/vnd.wap.multipart.alternative". + PduBody childBody = parseParts(new ByteArrayInputStream(partData)); + // take the first part of children. + part = childBody.getPart(0); + } else { + // Check Content-Transfer-Encoding. + byte[] partDataEncoding = part.getContentTransferEncoding(); + if (null != partDataEncoding) { + String encoding = new String(partDataEncoding); + if (encoding.equalsIgnoreCase(PduPart.P_BASE64)) { + // Decode "base64" into "binary". + partData = Base64.decodeBase64(partData); + } else if (encoding.equalsIgnoreCase(PduPart.P_QUOTED_PRINTABLE)) { + // Decode "quoted-printable" into "binary". + partData = QuotedPrintable.decodeQuotedPrintable(partData); + } else { + // "binary" is the default encoding. + } + } + if (null == partData) { + log("Decode part data error!"); + return null; + } + part.setData(partData); + } + } + + /* add this part to body */ + if (THE_FIRST_PART == checkPartPosition(part)) { + /* this is the first part */ + body.addPart(0, part); + } else { + /* add the part to the end */ + body.addPart(part); + } + } + + return body; + } + + /** + * Log status. + * + * @param text log information + */ + private static void log(String text) { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, text); + } + } + + /** + * Parse unsigned integer. + * + * @param pduDataStream pdu data input stream + * @return the integer, -1 when failed + */ + protected static int parseUnsignedInt(ByteArrayInputStream pduDataStream) { + /** + * From wap-230-wsp-20010705-a.pdf + * The maximum size of a uintvar is 32 bits. + * So it will be encoded in no more than 5 octets. + */ + assert(null != pduDataStream); + int result = 0; + int temp = pduDataStream.read(); + if (temp == -1) { + return temp; + } + + while((temp & 0x80) != 0) { + result = result << 7; + result |= temp & 0x7F; + temp = pduDataStream.read(); + if (temp == -1) { + return temp; + } + } + + result = result << 7; + result |= temp & 0x7F; + + return result; + } + + /** + * Parse value length. + * + * @param pduDataStream pdu data input stream + * @return the integer + */ + protected static int parseValueLength(ByteArrayInputStream pduDataStream) { + /** + * From wap-230-wsp-20010705-a.pdf + * Value-length = Short-length | (Length-quote Length) + * Short-length = + * Length-quote = + * Length = Uintvar-integer + * Uintvar-integer = 1*5 OCTET + */ + assert(null != pduDataStream); + int temp = pduDataStream.read(); + assert(-1 != temp); + int first = temp & 0xFF; + + if (first <= SHORT_LENGTH_MAX) { + return first; + } else if (first == LENGTH_QUOTE) { + return parseUnsignedInt(pduDataStream); + } + + throw new RuntimeException ("Value length > LENGTH_QUOTE!"); + } + + /** + * Parse encoded string value. + * + * @param pduDataStream pdu data input stream + * @return the EncodedStringValue + */ + protected static EncodedStringValue parseEncodedStringValue(ByteArrayInputStream pduDataStream){ + /** + * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf + * Encoded-string-value = Text-string | Value-length Char-set Text-string + */ + assert(null != pduDataStream); + pduDataStream.mark(1); + EncodedStringValue returnValue = null; + int charset = 0; + int temp = pduDataStream.read(); + assert(-1 != temp); + int first = temp & 0xFF; + if (first == 0) { + return new EncodedStringValue(""); + } + + pduDataStream.reset(); + if (first < TEXT_MIN) { + parseValueLength(pduDataStream); + + charset = parseShortInteger(pduDataStream); //get the "Charset" + } + + byte[] textString = parseWapString(pduDataStream, TYPE_TEXT_STRING); + + try { + if (0 != charset) { + returnValue = new EncodedStringValue(charset, textString); + } else { + returnValue = new EncodedStringValue(textString); + } + } catch(Exception e) { + return null; + } + + return returnValue; + } + + /** + * Parse Text-String or Quoted-String. + * + * @param pduDataStream pdu data input stream + * @param stringType TYPE_TEXT_STRING or TYPE_QUOTED_STRING + * @return the string without End-of-string in byte array + */ + protected static byte[] parseWapString(ByteArrayInputStream pduDataStream, + int stringType) { + assert(null != pduDataStream); + /** + * From wap-230-wsp-20010705-a.pdf + * Text-string = [Quote] *TEXT End-of-string + * If the first character in the TEXT is in the range of 128-255, + * a Quote character must precede it. + * Otherwise the Quote character must be omitted. + * The Quote is not part of the contents. + * Quote = + * End-of-string = + * + * Quoted-string = *TEXT End-of-string + * + * Token-text = Token End-of-string + */ + + // Mark supposed beginning of Text-string + // We will have to mark again if first char is QUOTE or QUOTED_STRING_FLAG + pduDataStream.mark(1); + + // Check first char + int temp = pduDataStream.read(); + assert(-1 != temp); + if ((TYPE_QUOTED_STRING == stringType) && + (QUOTED_STRING_FLAG == temp)) { + // Mark again if QUOTED_STRING_FLAG and ignore it + pduDataStream.mark(1); + } else if ((TYPE_TEXT_STRING == stringType) && + (QUOTE == temp)) { + // Mark again if QUOTE and ignore it + pduDataStream.mark(1); + } else { + // Otherwise go back to origin + pduDataStream.reset(); + } + + // We are now definitely at the beginning of string + /** + * Return *TOKEN or *TEXT (Text-String without QUOTE, + * Quoted-String without QUOTED_STRING_FLAG and without End-of-string) + */ + return getWapString(pduDataStream, stringType); + } + + /** + * Check TOKEN data defined in RFC2616. + * @param ch checking data + * @return true when ch is TOKEN, false when ch is not TOKEN + */ + protected static boolean isTokenCharacter(int ch) { + /** + * Token = 1* + * separators = "("(40) | ")"(41) | "<"(60) | ">"(62) | "@"(64) + * | ","(44) | ";"(59) | ":"(58) | "\"(92) | <">(34) + * | "/"(47) | "["(91) | "]"(93) | "?"(63) | "="(61) + * | "{"(123) | "}"(125) | SP(32) | HT(9) + * CHAR = + * CTL = + * SP = + * HT = + */ + if((ch < 33) || (ch > 126)) { + return false; + } + + switch(ch) { + case '"': /* '"' */ + case '(': /* '(' */ + case ')': /* ')' */ + case ',': /* ',' */ + case '/': /* '/' */ + case ':': /* ':' */ + case ';': /* ';' */ + case '<': /* '<' */ + case '=': /* '=' */ + case '>': /* '>' */ + case '?': /* '?' */ + case '@': /* '@' */ + case '[': /* '[' */ + case '\\': /* '\' */ + case ']': /* ']' */ + case '{': /* '{' */ + case '}': /* '}' */ + return false; + } + + return true; + } + + /** + * Check TEXT data defined in RFC2616. + * @param ch checking data + * @return true when ch is TEXT, false when ch is not TEXT + */ + protected static boolean isText(int ch) { + /** + * TEXT = + * CTL = + * LWS = [CRLF] 1*( SP | HT ) + * CRLF = CR LF + * CR = + * LF = + */ + if(((ch >= 32) && (ch <= 126)) || ((ch >= 128) && (ch <= 255))) { + return true; + } + + switch(ch) { + case '\t': /* '\t' */ + case '\n': /* '\n' */ + case '\r': /* '\r' */ + return true; + } + + return false; + } + + protected static byte[] getWapString(ByteArrayInputStream pduDataStream, + int stringType) { + assert(null != pduDataStream); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int temp = pduDataStream.read(); + assert(-1 != temp); + while((-1 != temp) && ('\0' != temp)) { + // check each of the character + if (stringType == TYPE_TOKEN_STRING) { + if (isTokenCharacter(temp)) { + out.write(temp); + } + } else { + if (isText(temp)) { + out.write(temp); + } + } + + temp = pduDataStream.read(); + assert(-1 != temp); + } + + if (out.size() > 0) { + return out.toByteArray(); + } + + return null; + } + + /** + * Extract a byte value from the input stream. + * + * @param pduDataStream pdu data input stream + * @return the byte + */ + protected static int extractByteValue(ByteArrayInputStream pduDataStream) { + assert(null != pduDataStream); + int temp = pduDataStream.read(); + assert(-1 != temp); + return temp & 0xFF; + } + + /** + * Parse Short-Integer. + * + * @param pduDataStream pdu data input stream + * @return the byte + */ + protected static int parseShortInteger(ByteArrayInputStream pduDataStream) { + /** + * From wap-230-wsp-20010705-a.pdf + * Short-integer = OCTET + * Integers in range 0-127 shall be encoded as a one + * octet value with the most significant bit set to one (1xxx xxxx) + * and with the value in the remaining least significant bits. + */ + assert(null != pduDataStream); + int temp = pduDataStream.read(); + assert(-1 != temp); + return temp & 0x7F; + } + + /** + * Parse Long-Integer. + * + * @param pduDataStream pdu data input stream + * @return long integer + */ + protected static long parseLongInteger(ByteArrayInputStream pduDataStream) { + /** + * From wap-230-wsp-20010705-a.pdf + * Long-integer = Short-length Multi-octet-integer + * The Short-length indicates the length of the Multi-octet-integer + * Multi-octet-integer = 1*30 OCTET + * The content octets shall be an unsigned integer value + * with the most significant octet encoded first (big-endian representation). + * The minimum number of octets must be used to encode the value. + * Short-length = + */ + assert(null != pduDataStream); + int temp = pduDataStream.read(); + assert(-1 != temp); + int count = temp & 0xFF; + + if (count > LONG_INTEGER_LENGTH_MAX) { + throw new RuntimeException("Octet count greater than 8 and I can't represent that!"); + } + + long result = 0; + + for (int i = 0 ; i < count ; i++) { + temp = pduDataStream.read(); + assert(-1 != temp); + result <<= 8; + result += (temp & 0xFF); + } + + return result; + } + + /** + * Parse Integer-Value. + * + * @param pduDataStream pdu data input stream + * @return long integer + */ + protected static long parseIntegerValue(ByteArrayInputStream pduDataStream) { + /** + * From wap-230-wsp-20010705-a.pdf + * Integer-Value = Short-integer | Long-integer + */ + assert(null != pduDataStream); + pduDataStream.mark(1); + int temp = pduDataStream.read(); + assert(-1 != temp); + pduDataStream.reset(); + if (temp > SHORT_INTEGER_MAX) { + return parseShortInteger(pduDataStream); + } else { + return parseLongInteger(pduDataStream); + } + } + + /** + * To skip length of the wap value. + * + * @param pduDataStream pdu data input stream + * @param length area size + * @return the values in this area + */ + protected static int skipWapValue(ByteArrayInputStream pduDataStream, int length) { + assert(null != pduDataStream); + byte[] area = new byte[length]; + int readLen = pduDataStream.read(area, 0, length); + if (readLen < length) { //The actually read length is lower than the length + return -1; + } else { + return readLen; + } + } + + /** + * Parse content type parameters. For now we just support + * four parameters used in mms: "type", "start", "name", "charset". + * + * @param pduDataStream pdu data input stream + * @param map to store parameters of Content-Type field + * @param length length of all the parameters + */ + protected static void parseContentTypeParams(ByteArrayInputStream pduDataStream, + HashMap map, Integer length) { + /** + * From wap-230-wsp-20010705-a.pdf + * Parameter = Typed-parameter | Untyped-parameter + * Typed-parameter = Well-known-parameter-token Typed-value + * the actual expected type of the value is implied by the well-known parameter + * Well-known-parameter-token = Integer-value + * the code values used for parameters are specified in the Assigned Numbers appendix + * Typed-value = Compact-value | Text-value + * In addition to the expected type, there may be no value. + * If the value cannot be encoded using the expected type, it shall be encoded as text. + * Compact-value = Integer-value | + * Date-value | Delta-seconds-value | Q-value | Version-value | + * Uri-value + * Untyped-parameter = Token-text Untyped-value + * the type of the value is unknown, but it shall be encoded as an integer, + * if that is possible. + * Untyped-value = Integer-value | Text-value + */ + assert(null != pduDataStream); + assert(length > 0); + + int startPos = pduDataStream.available(); + int tempPos = 0; + int lastLen = length; + while(0 < lastLen) { + int param = pduDataStream.read(); + assert(-1 != param); + lastLen--; + + switch (param) { + /** + * From rfc2387, chapter 3.1 + * The type parameter must be specified and its value is the MIME media + * type of the "root" body part. It permits a MIME user agent to + * determine the content-type without reference to the enclosed body + * part. If the value of the type parameter and the root body part's + * content-type differ then the User Agent's behavior is undefined. + * + * From wap-230-wsp-20010705-a.pdf + * type = Constrained-encoding + * Constrained-encoding = Extension-Media | Short-integer + * Extension-media = *TEXT End-of-string + */ + case PduPart.P_TYPE: + case PduPart.P_CT_MR_TYPE: + pduDataStream.mark(1); + int first = extractByteValue(pduDataStream); + pduDataStream.reset(); + if (first > TEXT_MAX) { + // Short-integer (well-known type) + int index = parseShortInteger(pduDataStream); + + if (index < PduContentTypes.contentTypes.length) { + byte[] type = (PduContentTypes.contentTypes[index]).getBytes(); + map.put(PduPart.P_TYPE, type); + } else { + //not support this type, ignore it. + } + } else { + // Text-String (extension-media) + byte[] type = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if ((null != type) && (null != map)) { + map.put(PduPart.P_TYPE, type); + } + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + break; + + /** + * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2.3. + * Start Parameter Referring to Presentation + * + * From rfc2387, chapter 3.2 + * The start parameter, if given, is the content-ID of the compound + * object's "root". If not present the "root" is the first body part in + * the Multipart/Related entity. The "root" is the element the + * applications processes first. + * + * From wap-230-wsp-20010705-a.pdf + * start = Text-String + */ + case PduPart.P_START: + case PduPart.P_DEP_START: + byte[] start = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if ((null != start) && (null != map)) { + map.put(PduPart.P_START, start); + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + break; + + /** + * From oma-ts-mms-conf-v1_3.pdf + * In creation, the character set SHALL be either us-ascii + * (IANA MIBenum 3) or utf-8 (IANA MIBenum 106)[Unicode]. + * In retrieval, both us-ascii and utf-8 SHALL be supported. + * + * From wap-230-wsp-20010705-a.pdf + * charset = Well-known-charset|Text-String + * Well-known-charset = Any-charset | Integer-value + * Both are encoded using values from Character Set + * Assignments table in Assigned Numbers + * Any-charset = + * Equivalent to the special RFC2616 charset value "*" + */ + case PduPart.P_CHARSET: + pduDataStream.mark(1); + int firstValue = extractByteValue(pduDataStream); + pduDataStream.reset(); + //Check first char + if (((firstValue > TEXT_MIN) && (firstValue < TEXT_MAX)) || + (END_STRING_FLAG == firstValue)) { + //Text-String (extension-charset) + byte[] charsetStr = parseWapString(pduDataStream, TYPE_TEXT_STRING); + try { + int charsetInt = CharacterSets.getMibEnumValue( + new String(charsetStr)); + map.put(PduPart.P_CHARSET, charsetInt); + } catch (UnsupportedEncodingException e) { + // Not a well-known charset, use "*". + Log.e(LOG_TAG, Arrays.toString(charsetStr), e); + map.put(PduPart.P_CHARSET, CharacterSets.ANY_CHARSET); + } + } else { + //Well-known-charset + int charset = (int) parseIntegerValue(pduDataStream); + if (map != null) { + map.put(PduPart.P_CHARSET, charset); + } + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + break; + + /** + * From oma-ts-mms-conf-v1_3.pdf + * A name for multipart object SHALL be encoded using name-parameter + * for Content-Type header in WSP multipart headers. + * + * From wap-230-wsp-20010705-a.pdf + * name = Text-String + */ + case PduPart.P_DEP_NAME: + case PduPart.P_NAME: + byte[] name = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if ((null != name) && (null != map)) { + map.put(PduPart.P_NAME, name); + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + break; + default: + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "Not supported Content-Type parameter"); + } + if (-1 == skipWapValue(pduDataStream, lastLen)) { + Log.e(LOG_TAG, "Corrupt Content-Type"); + } else { + lastLen = 0; + } + break; + } + } + + if (0 != lastLen) { + Log.e(LOG_TAG, "Corrupt Content-Type"); + } + } + + /** + * Parse content type. + * + * @param pduDataStream pdu data input stream + * @param map to store parameters in Content-Type header field + * @return Content-Type value + */ + protected static byte[] parseContentType(ByteArrayInputStream pduDataStream, + HashMap map) { + /** + * From wap-230-wsp-20010705-a.pdf + * Content-type-value = Constrained-media | Content-general-form + * Content-general-form = Value-length Media-type + * Media-type = (Well-known-media | Extension-Media) *(Parameter) + */ + assert(null != pduDataStream); + + byte[] contentType = null; + pduDataStream.mark(1); + int temp = pduDataStream.read(); + assert(-1 != temp); + pduDataStream.reset(); + + int cur = (temp & 0xFF); + + if (cur < TEXT_MIN) { + int length = parseValueLength(pduDataStream); + int startPos = pduDataStream.available(); + pduDataStream.mark(1); + temp = pduDataStream.read(); + assert(-1 != temp); + pduDataStream.reset(); + int first = (temp & 0xFF); + + if ((first >= TEXT_MIN) && (first <= TEXT_MAX)) { + contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING); + } else if (first > TEXT_MAX) { + int index = parseShortInteger(pduDataStream); + + if (index < PduContentTypes.contentTypes.length) { //well-known type + contentType = (PduContentTypes.contentTypes[index]).getBytes(); + } else { + pduDataStream.reset(); + contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING); + } + } else { + Log.e(LOG_TAG, "Corrupt content-type"); + return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*" + } + + int endPos = pduDataStream.available(); + int parameterLen = length - (startPos - endPos); + if (parameterLen > 0) {//have parameters + parseContentTypeParams(pduDataStream, map, parameterLen); + } + + if (parameterLen < 0) { + Log.e(LOG_TAG, "Corrupt MMS message"); + return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*" + } + } else if (cur <= TEXT_MAX) { + contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING); + } else { + contentType = + (PduContentTypes.contentTypes[parseShortInteger(pduDataStream)]).getBytes(); + } + + return contentType; + } + + /** + * Parse part's headers. + * + * @param pduDataStream pdu data input stream + * @param part to store the header informations of the part + * @param length length of the headers + * @return true if parse successfully, false otherwise + */ + protected boolean parsePartHeaders(ByteArrayInputStream pduDataStream, + PduPart part, int length) { + assert(null != pduDataStream); + assert(null != part); + assert(length > 0); + + /** + * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2. + * A name for multipart object SHALL be encoded using name-parameter + * for Content-Type header in WSP multipart headers. + * In decoding, name-parameter of Content-Type SHALL be used if available. + * If name-parameter of Content-Type is not available, + * filename parameter of Content-Disposition header SHALL be used if available. + * If neither name-parameter of Content-Type header nor filename parameter + * of Content-Disposition header is available, + * Content-Location header SHALL be used if available. + * + * Within SMIL part the reference to the media object parts SHALL use + * either Content-ID or Content-Location mechanism [RFC2557] + * and the corresponding WSP part headers in media object parts + * contain the corresponding definitions. + */ + int startPos = pduDataStream.available(); + int tempPos = 0; + int lastLen = length; + while(0 < lastLen) { + int header = pduDataStream.read(); + assert(-1 != header); + lastLen--; + + if (header > TEXT_MAX) { + // Number assigned headers. + switch (header) { + case PduPart.P_CONTENT_LOCATION: + /** + * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21 + * Content-location-value = Uri-value + */ + byte[] contentLocation = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if (null != contentLocation) { + part.setContentLocation(contentLocation); + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + break; + case PduPart.P_CONTENT_ID: + /** + * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21 + * Content-ID-value = Quoted-string + */ + byte[] contentId = parseWapString(pduDataStream, TYPE_QUOTED_STRING); + if (null != contentId) { + part.setContentId(contentId); + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + break; + case PduPart.P_DEP_CONTENT_DISPOSITION: + case PduPart.P_CONTENT_DISPOSITION: + /** + * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21 + * Content-disposition-value = Value-length Disposition *(Parameter) + * Disposition = Form-data | Attachment | Inline | Token-text + * Form-data = + * Attachment = + * Inline = + */ + + /* + * some carrier mmsc servers do not support content_disposition + * field correctly + */ + if (mParseContentDisposition) { + int len = parseValueLength(pduDataStream); + pduDataStream.mark(1); + int thisStartPos = pduDataStream.available(); + int thisEndPos = 0; + int value = pduDataStream.read(); + + if (value == PduPart.P_DISPOSITION_FROM_DATA ) { + part.setContentDisposition(PduPart.DISPOSITION_FROM_DATA); + } else if (value == PduPart.P_DISPOSITION_ATTACHMENT) { + part.setContentDisposition(PduPart.DISPOSITION_ATTACHMENT); + } else if (value == PduPart.P_DISPOSITION_INLINE) { + part.setContentDisposition(PduPart.DISPOSITION_INLINE); + } else { + pduDataStream.reset(); + /* Token-text */ + part.setContentDisposition(parseWapString(pduDataStream + , TYPE_TEXT_STRING)); + } + + /* get filename parameter and skip other parameters */ + thisEndPos = pduDataStream.available(); + if (thisStartPos - thisEndPos < len) { + value = pduDataStream.read(); + if (value == PduPart.P_FILENAME) { //filename is text-string + part.setFilename(parseWapString(pduDataStream + , TYPE_TEXT_STRING)); + } + + /* skip other parameters */ + thisEndPos = pduDataStream.available(); + if (thisStartPos - thisEndPos < len) { + int last = len - (thisStartPos - thisEndPos); + byte[] temp = new byte[last]; + pduDataStream.read(temp, 0, last); + } + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + } + break; + default: + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "Not supported Part headers: " + header); + } + if (-1 == skipWapValue(pduDataStream, lastLen)) { + Log.e(LOG_TAG, "Corrupt Part headers"); + return false; + } + lastLen = 0; + break; + } + } else if ((header >= TEXT_MIN) && (header <= TEXT_MAX)) { + // Not assigned header. + byte[] tempHeader = parseWapString(pduDataStream, TYPE_TEXT_STRING); + byte[] tempValue = parseWapString(pduDataStream, TYPE_TEXT_STRING); + + // Check the header whether it is "Content-Transfer-Encoding". + if (true == + PduPart.CONTENT_TRANSFER_ENCODING.equalsIgnoreCase(new String(tempHeader))) { + part.setContentTransferEncoding(tempValue); + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + } else { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "Not supported Part headers: " + header); + } + // Skip all headers of this part. + if (-1 == skipWapValue(pduDataStream, lastLen)) { + Log.e(LOG_TAG, "Corrupt Part headers"); + return false; + } + lastLen = 0; + } + } + + if (0 != lastLen) { + Log.e(LOG_TAG, "Corrupt Part headers"); + return false; + } + + return true; + } + + /** + * Check the position of a specified part. + * + * @param part the part to be checked + * @return part position, THE_FIRST_PART when it's the + * first one, THE_LAST_PART when it's the last one. + */ + private static int checkPartPosition(PduPart part) { + assert(null != part); + if ((null == mTypeParam) && + (null == mStartParam)) { + return THE_LAST_PART; + } + + /* check part's content-id */ + if (null != mStartParam) { + byte[] contentId = part.getContentId(); + if (null != contentId) { + if (true == Arrays.equals(mStartParam, contentId)) { + return THE_FIRST_PART; + } + } + // This is not the first part, so append to end (keeping the original order) + // Check b/19607294 for details of this change + return THE_LAST_PART; + } + + /* check part's content-type */ + if (null != mTypeParam) { + byte[] contentType = part.getContentType(); + if (null != contentType) { + if (true == Arrays.equals(mTypeParam, contentType)) { + return THE_FIRST_PART; + } + } + } + + return THE_LAST_PART; + } + + /** + * Check mandatory headers of a pdu. + * + * @param headers pdu headers + * @return true if the pdu has all of the mandatory headers, false otherwise. + */ + protected static boolean checkMandatoryHeader(PduHeaders headers) { + if (null == headers) { + return false; + } + + /* get message type */ + int messageType = headers.getOctet(PduHeaders.MESSAGE_TYPE); + + /* check Mms-Version field */ + int mmsVersion = headers.getOctet(PduHeaders.MMS_VERSION); + if (0 == mmsVersion) { + // Every message should have Mms-Version field. + return false; + } + + /* check mandatory header fields */ + switch (messageType) { + case PduHeaders.MESSAGE_TYPE_SEND_REQ: + // Content-Type field. + byte[] srContentType = headers.getTextString(PduHeaders.CONTENT_TYPE); + if (null == srContentType) { + return false; + } + + // From field. + EncodedStringValue srFrom = headers.getEncodedStringValue(PduHeaders.FROM); + if (null == srFrom) { + return false; + } + + // Transaction-Id field. + byte[] srTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); + if (null == srTransactionId) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_SEND_CONF: + // Response-Status field. + int scResponseStatus = headers.getOctet(PduHeaders.RESPONSE_STATUS); + if (0 == scResponseStatus) { + return false; + } + + // Transaction-Id field. + byte[] scTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); + if (null == scTransactionId) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND: + // Content-Location field. + byte[] niContentLocation = headers.getTextString(PduHeaders.CONTENT_LOCATION); + if (null == niContentLocation) { + return false; + } + + // Expiry field. + long niExpiry = headers.getLongInteger(PduHeaders.EXPIRY); + if (-1 == niExpiry) { + return false; + } + + // Message-Class field. + byte[] niMessageClass = headers.getTextString(PduHeaders.MESSAGE_CLASS); + if (null == niMessageClass) { + return false; + } + + // Message-Size field. + long niMessageSize = headers.getLongInteger(PduHeaders.MESSAGE_SIZE); + if (-1 == niMessageSize) { + return false; + } + + // Transaction-Id field. + byte[] niTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); + if (null == niTransactionId) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND: + // Status field. + int nriStatus = headers.getOctet(PduHeaders.STATUS); + if (0 == nriStatus) { + return false; + } + + // Transaction-Id field. + byte[] nriTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); + if (null == nriTransactionId) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF: + // Content-Type field. + byte[] rcContentType = headers.getTextString(PduHeaders.CONTENT_TYPE); + if (null == rcContentType) { + return false; + } + + // Date field. + long rcDate = headers.getLongInteger(PduHeaders.DATE); + if (-1 == rcDate) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_DELIVERY_IND: + // Date field. + long diDate = headers.getLongInteger(PduHeaders.DATE); + if (-1 == diDate) { + return false; + } + + // Message-Id field. + byte[] diMessageId = headers.getTextString(PduHeaders.MESSAGE_ID); + if (null == diMessageId) { + return false; + } + + // Status field. + int diStatus = headers.getOctet(PduHeaders.STATUS); + if (0 == diStatus) { + return false; + } + + // To field. + EncodedStringValue[] diTo = headers.getEncodedStringValues(PduHeaders.TO); + if (null == diTo) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND: + // Transaction-Id field. + byte[] aiTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); + if (null == aiTransactionId) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND: + // Date field. + long roDate = headers.getLongInteger(PduHeaders.DATE); + if (-1 == roDate) { + return false; + } + + // From field. + EncodedStringValue roFrom = headers.getEncodedStringValue(PduHeaders.FROM); + if (null == roFrom) { + return false; + } + + // Message-Id field. + byte[] roMessageId = headers.getTextString(PduHeaders.MESSAGE_ID); + if (null == roMessageId) { + return false; + } + + // Read-Status field. + int roReadStatus = headers.getOctet(PduHeaders.READ_STATUS); + if (0 == roReadStatus) { + return false; + } + + // To field. + EncodedStringValue[] roTo = headers.getEncodedStringValues(PduHeaders.TO); + if (null == roTo) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_READ_REC_IND: + // From field. + EncodedStringValue rrFrom = headers.getEncodedStringValue(PduHeaders.FROM); + if (null == rrFrom) { + return false; + } + + // Message-Id field. + byte[] rrMessageId = headers.getTextString(PduHeaders.MESSAGE_ID); + if (null == rrMessageId) { + return false; + } + + // Read-Status field. + int rrReadStatus = headers.getOctet(PduHeaders.READ_STATUS); + if (0 == rrReadStatus) { + return false; + } + + // To field. + EncodedStringValue[] rrTo = headers.getEncodedStringValues(PduHeaders.TO); + if (null == rrTo) { + return false; + } + + break; + default: + // Parser doesn't support this message type in this version. + return false; + } + + return true; + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduPart.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduPart.java new file mode 100644 index 000000000..dd71efe58 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduPart.java @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import android.net.Uri; + +import java.util.HashMap; +import java.util.Map; + +/** + * The pdu part. + */ +public class PduPart { + /** + * Well-Known Parameters. + */ + public static final int P_Q = 0x80; + public static final int P_CHARSET = 0x81; + public static final int P_LEVEL = 0x82; + public static final int P_TYPE = 0x83; + public static final int P_DEP_NAME = 0x85; + public static final int P_DEP_FILENAME = 0x86; + public static final int P_DIFFERENCES = 0x87; + public static final int P_PADDING = 0x88; + // This value of "TYPE" s used with Content-Type: multipart/related + public static final int P_CT_MR_TYPE = 0x89; + public static final int P_DEP_START = 0x8A; + public static final int P_DEP_START_INFO = 0x8B; + public static final int P_DEP_COMMENT = 0x8C; + public static final int P_DEP_DOMAIN = 0x8D; + public static final int P_MAX_AGE = 0x8E; + public static final int P_DEP_PATH = 0x8F; + public static final int P_SECURE = 0x90; + public static final int P_SEC = 0x91; + public static final int P_MAC = 0x92; + public static final int P_CREATION_DATE = 0x93; + public static final int P_MODIFICATION_DATE = 0x94; + public static final int P_READ_DATE = 0x95; + public static final int P_SIZE = 0x96; + public static final int P_NAME = 0x97; + public static final int P_FILENAME = 0x98; + public static final int P_START = 0x99; + public static final int P_START_INFO = 0x9A; + public static final int P_COMMENT = 0x9B; + public static final int P_DOMAIN = 0x9C; + public static final int P_PATH = 0x9D; + + /** + * Header field names. + */ + public static final int P_CONTENT_TYPE = 0x91; + public static final int P_CONTENT_LOCATION = 0x8E; + public static final int P_CONTENT_ID = 0xC0; + public static final int P_DEP_CONTENT_DISPOSITION = 0xAE; + public static final int P_CONTENT_DISPOSITION = 0xC5; + // The next header is unassigned header, use reserved header(0x48) value. + public static final int P_CONTENT_TRANSFER_ENCODING = 0xC8; + + /** + * Content=Transfer-Encoding string. + */ + public static final String CONTENT_TRANSFER_ENCODING = + "Content-Transfer-Encoding"; + + /** + * Value of Content-Transfer-Encoding. + */ + public static final String P_BINARY = "binary"; + public static final String P_7BIT = "7bit"; + public static final String P_8BIT = "8bit"; + public static final String P_BASE64 = "base64"; + public static final String P_QUOTED_PRINTABLE = "quoted-printable"; + + /** + * Value of disposition can be set to PduPart when the value is octet in + * the PDU. + * "from-data" instead of Form-data. + * "attachment" instead of Attachment. + * "inline" instead of Inline. + */ + static final byte[] DISPOSITION_FROM_DATA = "from-data".getBytes(); + static final byte[] DISPOSITION_ATTACHMENT = "attachment".getBytes(); + static final byte[] DISPOSITION_INLINE = "inline".getBytes(); + + /** + * Content-Disposition value. + */ + public static final int P_DISPOSITION_FROM_DATA = 0x80; + public static final int P_DISPOSITION_ATTACHMENT = 0x81; + public static final int P_DISPOSITION_INLINE = 0x82; + + /** + * Header of part. + */ + private Map mPartHeader = null; + + /** + * Data uri. + */ + private Uri mUri = null; + + /** + * Part data. + */ + private byte[] mPartData = null; + + private static final String TAG = "PduPart"; + + /** + * Empty Constructor. + */ + public PduPart() { + mPartHeader = new HashMap(); + } + + /** + * Set part data. The data are stored as byte array. + * + * @param data the data + */ + public void setData(byte[] data) { + if(data == null) { + return; + } + + mPartData = new byte[data.length]; + System.arraycopy(data, 0, mPartData, 0, data.length); + } + + /** + * @return A copy of the part data or null if the data wasn't set or + * the data is stored as Uri. + * @see #getDataUri + */ + public byte[] getData() { + if(mPartData == null) { + return null; + } + + byte[] byteArray = new byte[mPartData.length]; + System.arraycopy(mPartData, 0, byteArray, 0, mPartData.length); + return byteArray; + } + + /** + * @return The length of the data, if this object have data, else 0. + */ + public int getDataLength() { + if(mPartData != null){ + return mPartData.length; + } else { + return 0; + } + } + + + /** + * Set data uri. The data are stored as Uri. + * + * @param uri the uri + */ + public void setDataUri(Uri uri) { + mUri = uri; + } + + /** + * @return The Uri of the part data or null if the data wasn't set or + * the data is stored as byte array. + * @see #getData + */ + public Uri getDataUri() { + return mUri; + } + + /** + * Set Content-id value + * + * @param contentId the content-id value + * @throws NullPointerException if the value is null. + */ + public void setContentId(byte[] contentId) { + if((contentId == null) || (contentId.length == 0)) { + throw new IllegalArgumentException( + "Content-Id may not be null or empty."); + } + + if ((contentId.length > 1) + && ((char) contentId[0] == '<') + && ((char) contentId[contentId.length - 1] == '>')) { + mPartHeader.put(P_CONTENT_ID, contentId); + return; + } + + // Insert beginning '<' and trailing '>' for Content-Id. + byte[] buffer = new byte[contentId.length + 2]; + buffer[0] = (byte) (0xff & '<'); + buffer[buffer.length - 1] = (byte) (0xff & '>'); + System.arraycopy(contentId, 0, buffer, 1, contentId.length); + mPartHeader.put(P_CONTENT_ID, buffer); + } + + /** + * Get Content-id value. + * + * @return the value + */ + public byte[] getContentId() { + return (byte[]) mPartHeader.get(P_CONTENT_ID); + } + + /** + * Set Char-set value. + * + * @param charset the value + */ + public void setCharset(int charset) { + mPartHeader.put(P_CHARSET, charset); + } + + /** + * Get Char-set value + * + * @return the charset value. Return 0 if charset was not set. + */ + public int getCharset() { + Integer charset = (Integer) mPartHeader.get(P_CHARSET); + if(charset == null) { + return 0; + } else { + return charset.intValue(); + } + } + + /** + * Set Content-Location value. + * + * @param contentLocation the value + * @throws NullPointerException if the value is null. + */ + public void setContentLocation(byte[] contentLocation) { + if(contentLocation == null) { + throw new NullPointerException("null content-location"); + } + + mPartHeader.put(P_CONTENT_LOCATION, contentLocation); + } + + /** + * Get Content-Location value. + * + * @return the value + * return PduPart.disposition[0] instead of (Form-data). + * return PduPart.disposition[1] instead of (Attachment). + * return PduPart.disposition[2] instead of (Inline). + */ + public byte[] getContentLocation() { + return (byte[]) mPartHeader.get(P_CONTENT_LOCATION); + } + + /** + * Set Content-Disposition value. + * Use PduPart.disposition[0] instead of (Form-data). + * Use PduPart.disposition[1] instead of (Attachment). + * Use PduPart.disposition[2] instead of (Inline). + * + * @param contentDisposition the value + * @throws NullPointerException if the value is null. + */ + public void setContentDisposition(byte[] contentDisposition) { + if(contentDisposition == null) { + throw new NullPointerException("null content-disposition"); + } + + mPartHeader.put(P_CONTENT_DISPOSITION, contentDisposition); + } + + /** + * Get Content-Disposition value. + * + * @return the value + */ + public byte[] getContentDisposition() { + return (byte[]) mPartHeader.get(P_CONTENT_DISPOSITION); + } + + /** + * Set Content-Type value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setContentType(byte[] contentType) { + if(contentType == null) { + throw new NullPointerException("null content-type"); + } + + mPartHeader.put(P_CONTENT_TYPE, contentType); + } + + /** + * Get Content-Type value of part. + * + * @return the value + */ + public byte[] getContentType() { + return (byte[]) mPartHeader.get(P_CONTENT_TYPE); + } + + /** + * Set Content-Transfer-Encoding value + * + * @param contentId the content-id value + * @throws NullPointerException if the value is null. + */ + public void setContentTransferEncoding(byte[] contentTransferEncoding) { + if(contentTransferEncoding == null) { + throw new NullPointerException("null content-transfer-encoding"); + } + + mPartHeader.put(P_CONTENT_TRANSFER_ENCODING, contentTransferEncoding); + } + + /** + * Get Content-Transfer-Encoding value. + * + * @return the value + */ + public byte[] getContentTransferEncoding() { + return (byte[]) mPartHeader.get(P_CONTENT_TRANSFER_ENCODING); + } + + /** + * Set Content-type parameter: name. + * + * @param name the name value + * @throws NullPointerException if the value is null. + */ + public void setName(byte[] name) { + if(null == name) { + throw new NullPointerException("null content-id"); + } + + mPartHeader.put(P_NAME, name); + } + + /** + * Get content-type parameter: name. + * + * @return the name + */ + public byte[] getName() { + return (byte[]) mPartHeader.get(P_NAME); + } + + /** + * Get Content-disposition parameter: filename + * + * @param fileName the filename value + * @throws NullPointerException if the value is null. + */ + public void setFilename(byte[] fileName) { + if(null == fileName) { + throw new NullPointerException("null content-id"); + } + + mPartHeader.put(P_FILENAME, fileName); + } + + /** + * Set Content-disposition parameter: filename + * + * @return the filename + */ + public byte[] getFilename() { + return (byte[]) mPartHeader.get(P_FILENAME); + } + + public String generateLocation() { + // Assumption: At least one of the content-location / name / filename + // or content-id should be set. This is guaranteed by the PduParser + // for incoming messages and by MM composer for outgoing messages. + byte[] location = (byte[]) mPartHeader.get(P_NAME); + if(null == location) { + location = (byte[]) mPartHeader.get(P_FILENAME); + + if (null == location) { + location = (byte[]) mPartHeader.get(P_CONTENT_LOCATION); + } + } + + if (null == location) { + byte[] contentId = (byte[]) mPartHeader.get(P_CONTENT_ID); + return "cid:" + new String(contentId); + } else { + return new String(location); + } + } +} + diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/QuotedPrintable.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/QuotedPrintable.java new file mode 100644 index 000000000..51c5968d0 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/QuotedPrintable.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import java.io.ByteArrayOutputStream; + +public class QuotedPrintable { + private static byte ESCAPE_CHAR = '='; + + /** + * Decodes an array quoted-printable characters into an array of original bytes. + * Escaped characters are converted back to their original representation. + * + *

+ * This function implements a subset of + * quoted-printable encoding specification (rule #1 and rule #2) + * as defined in RFC 1521. + *

+ * + * @param bytes array of quoted-printable characters + * @return array of original bytes, + * null if quoted-printable decoding is unsuccessful. + */ + public static final byte[] decodeQuotedPrintable(byte[] bytes) { + if (bytes == null) { + return null; + } + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + for (int i = 0; i < bytes.length; i++) { + int b = bytes[i]; + if (b == ESCAPE_CHAR) { + try { + if('\r' == (char)bytes[i + 1] && + '\n' == (char)bytes[i + 2]) { + i += 2; + continue; + } + int u = Character.digit((char) bytes[++i], 16); + int l = Character.digit((char) bytes[++i], 16); + if (u == -1 || l == -1) { + return null; + } + buffer.write((char) ((u << 4) + l)); + } catch (ArrayIndexOutOfBoundsException e) { + return null; + } + } else { + buffer.write(b); + } + } + return buffer.toByteArray(); + } +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/ReadOrigInd.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/ReadOrigInd.java new file mode 100644 index 000000000..65f615369 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/ReadOrigInd.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import com.example.android.mmslib.InvalidHeaderValueException; + +public class ReadOrigInd extends GenericPdu { + /** + * Empty constructor. + * Since the Pdu corresponding to this class is constructed + * by the Proxy-Relay server, this class is only instantiated + * by the Pdu Parser. + * + * @throws InvalidHeaderValueException if error occurs. + */ + public ReadOrigInd() throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_READ_ORIG_IND); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + ReadOrigInd(PduHeaders headers) { + super(headers); + } + + /** + * Get Date value. + * + * @return the value + */ + public long getDate() { + return mPduHeaders.getLongInteger(PduHeaders.DATE); + } + + /** + * Set Date value. + * + * @param value the value + */ + public void setDate(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.DATE); + } + + /** + * Get From value. + * From-value = Value-length + * (Address-present-token Encoded-string-value | Insert-address-token) + * + * @return the value + */ + public EncodedStringValue getFrom() { + return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); + } + + /** + * Set From value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setFrom(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); + } + + /** + * Get Message-ID value. + * + * @return the value + */ + public byte[] getMessageId() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); + } + + /** + * Set Message-ID value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setMessageId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); + } + + /** + * Get X-MMS-Read-status value. + * + * @return the value + */ + public int getReadStatus() { + return mPduHeaders.getOctet(PduHeaders.READ_STATUS); + } + + /** + * Set X-MMS-Read-status value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setReadStatus(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.READ_STATUS); + } + + /** + * Get To value. + * + * @return the value + */ + public EncodedStringValue[] getTo() { + return mPduHeaders.getEncodedStringValues(PduHeaders.TO); + } + + /** + * Set To value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTo(EncodedStringValue[] value) { + mPduHeaders.setEncodedStringValues(value, PduHeaders.TO); + } + + /* + * Optional, not supported header fields: + * + * public byte[] getApplicId() {return null;} + * public void setApplicId(byte[] value) {} + * + * public byte[] getAuxApplicId() {return null;} + * public void getAuxApplicId(byte[] value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + */ +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/ReadRecInd.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/ReadRecInd.java new file mode 100644 index 000000000..0088c42e4 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/ReadRecInd.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import com.example.android.mmslib.InvalidHeaderValueException; + +public class ReadRecInd extends GenericPdu { + /** + * Constructor, used when composing a M-ReadRec.ind pdu. + * + * @param from the from value + * @param messageId the message ID value + * @param mmsVersion current viersion of mms + * @param readStatus the read status value + * @param to the to value + * @throws InvalidHeaderValueException if parameters are invalid. + * NullPointerException if messageId or to is null. + */ + public ReadRecInd(EncodedStringValue from, + byte[] messageId, + int mmsVersion, + int readStatus, + EncodedStringValue[] to) throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_READ_REC_IND); + setFrom(from); + setMessageId(messageId); + setMmsVersion(mmsVersion); + setTo(to); + setReadStatus(readStatus); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + ReadRecInd(PduHeaders headers) { + super(headers); + } + + /** + * Get Date value. + * + * @return the value + */ + public long getDate() { + return mPduHeaders.getLongInteger(PduHeaders.DATE); + } + + /** + * Set Date value. + * + * @param value the value + */ + public void setDate(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.DATE); + } + + /** + * Get Message-ID value. + * + * @return the value + */ + public byte[] getMessageId() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); + } + + /** + * Set Message-ID value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setMessageId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); + } + + /** + * Get To value. + * + * @return the value + */ + public EncodedStringValue[] getTo() { + return mPduHeaders.getEncodedStringValues(PduHeaders.TO); + } + + /** + * Set To value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTo(EncodedStringValue[] value) { + mPduHeaders.setEncodedStringValues(value, PduHeaders.TO); + } + + /** + * Get X-MMS-Read-status value. + * + * @return the value + */ + public int getReadStatus() { + return mPduHeaders.getOctet(PduHeaders.READ_STATUS); + } + + /** + * Set X-MMS-Read-status value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setReadStatus(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.READ_STATUS); + } + + /* + * Optional, not supported header fields: + * + * public byte[] getApplicId() {return null;} + * public void setApplicId(byte[] value) {} + * + * public byte[] getAuxApplicId() {return null;} + * public void getAuxApplicId(byte[] value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + */ +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/RetrieveConf.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/RetrieveConf.java new file mode 100644 index 000000000..a1198051d --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/RetrieveConf.java @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import com.example.android.mmslib.InvalidHeaderValueException; + +/** + * M-Retrive.conf Pdu. + */ +public class RetrieveConf extends MultimediaMessagePdu { + /** + * Empty constructor. + * Since the Pdu corresponding to this class is constructed + * by the Proxy-Relay server, this class is only instantiated + * by the Pdu Parser. + * + * @throws InvalidHeaderValueException if error occurs. + */ + public RetrieveConf() throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + RetrieveConf(PduHeaders headers) { + super(headers); + } + + /** + * Constructor with given headers and body + * + * @param headers Headers for this PDU. + * @param body Body of this PDu. + */ + RetrieveConf(PduHeaders headers, PduBody body) { + super(headers, body); + } + + /** + * Get CC value. + * + * @return the value + */ + public EncodedStringValue[] getCc() { + return mPduHeaders.getEncodedStringValues(PduHeaders.CC); + } + + /** + * Add a "CC" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void addCc(EncodedStringValue value) { + mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC); + } + + /** + * Get Content-type value. + * + * @return the value + */ + public byte[] getContentType() { + return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE); + } + + /** + * Set Content-type value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setContentType(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE); + } + + /** + * Get X-Mms-Delivery-Report value. + * + * @return the value + */ + public int getDeliveryReport() { + return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT); + } + + /** + * Set X-Mms-Delivery-Report value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setDeliveryReport(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT); + } + + /** + * Get From value. + * From-value = Value-length + * (Address-present-token Encoded-string-value | Insert-address-token) + * + * @return the value + */ + public EncodedStringValue getFrom() { + return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); + } + + /** + * Set From value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setFrom(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); + } + + /** + * Get X-Mms-Message-Class value. + * Message-class-value = Class-identifier | Token-text + * Class-identifier = Personal | Advertisement | Informational | Auto + * + * @return the value + */ + public byte[] getMessageClass() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS); + } + + /** + * Set X-Mms-Message-Class value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setMessageClass(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS); + } + + /** + * Get Message-ID value. + * + * @return the value + */ + public byte[] getMessageId() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); + } + + /** + * Set Message-ID value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setMessageId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); + } + + /** + * Get X-Mms-Read-Report value. + * + * @return the value + */ + public int getReadReport() { + return mPduHeaders.getOctet(PduHeaders.READ_REPORT); + } + + /** + * Set X-Mms-Read-Report value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setReadReport(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.READ_REPORT); + } + + /** + * Get X-Mms-Retrieve-Status value. + * + * @return the value + */ + public int getRetrieveStatus() { + return mPduHeaders.getOctet(PduHeaders.RETRIEVE_STATUS); + } + + /** + * Set X-Mms-Retrieve-Status value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setRetrieveStatus(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.RETRIEVE_STATUS); + } + + /** + * Get X-Mms-Retrieve-Text value. + * + * @return the value + */ + public EncodedStringValue getRetrieveText() { + return mPduHeaders.getEncodedStringValue(PduHeaders.RETRIEVE_TEXT); + } + + /** + * Set X-Mms-Retrieve-Text value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setRetrieveText(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.RETRIEVE_TEXT); + } + + /** + * Get X-Mms-Transaction-Id. + * + * @return the value + */ + public byte[] getTransactionId() { + return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); + } + + /** + * Set X-Mms-Transaction-Id. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTransactionId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); + } + + /* + * Optional, not supported header fields: + * + * public byte[] getApplicId() {return null;} + * public void setApplicId(byte[] value) {} + * + * public byte[] getAuxApplicId() {return null;} + * public void getAuxApplicId(byte[] value) {} + * + * public byte getContentClass() {return 0x00;} + * public void setApplicId(byte value) {} + * + * public byte getDrmContent() {return 0x00;} + * public void setDrmContent(byte value) {} + * + * public byte getDistributionIndicator() {return 0x00;} + * public void setDistributionIndicator(byte value) {} + * + * public PreviouslySentByValue getPreviouslySentBy() {return null;} + * public void setPreviouslySentBy(PreviouslySentByValue value) {} + * + * public PreviouslySentDateValue getPreviouslySentDate() {} + * public void setPreviouslySentDate(PreviouslySentDateValue value) {} + * + * public MmFlagsValue getMmFlags() {return null;} + * public void setMmFlags(MmFlagsValue value) {} + * + * public MmStateValue getMmState() {return null;} + * public void getMmState(MmStateValue value) {} + * + * public byte[] getReplaceId() {return 0x00;} + * public void setReplaceId(byte[] value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + * + * public byte getReplyCharging() {return 0x00;} + * public void setReplyCharging(byte value) {} + * + * public byte getReplyChargingDeadline() {return 0x00;} + * public void setReplyChargingDeadline(byte value) {} + * + * public byte[] getReplyChargingId() {return 0x00;} + * public void setReplyChargingId(byte[] value) {} + * + * public long getReplyChargingSize() {return 0;} + * public void setReplyChargingSize(long value) {} + */ +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/SendConf.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/SendConf.java new file mode 100644 index 000000000..2109c9885 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/SendConf.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import com.example.android.mmslib.InvalidHeaderValueException; + +public class SendConf extends GenericPdu { + /** + * Empty constructor. + * Since the Pdu corresponding to this class is constructed + * by the Proxy-Relay server, this class is only instantiated + * by the Pdu Parser. + * + * @throws InvalidHeaderValueException if error occurs. + */ + public SendConf() throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_SEND_CONF); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + SendConf(PduHeaders headers) { + super(headers); + } + + /** + * Get Message-ID value. + * + * @return the value + */ + public byte[] getMessageId() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); + } + + /** + * Set Message-ID value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setMessageId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); + } + + /** + * Get X-Mms-Response-Status. + * + * @return the value + */ + public int getResponseStatus() { + return mPduHeaders.getOctet(PduHeaders.RESPONSE_STATUS); + } + + /** + * Set X-Mms-Response-Status. + * + * @param value the values + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setResponseStatus(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.RESPONSE_STATUS); + } + + /** + * Get X-Mms-Transaction-Id field value. + * + * @return the X-Mms-Report-Allowed value + */ + public byte[] getTransactionId() { + return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); + } + + /** + * Set X-Mms-Transaction-Id field value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTransactionId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); + } + + /* + * Optional, not supported header fields: + * + * public byte[] getContentLocation() {return null;} + * public void setContentLocation(byte[] value) {} + * + * public EncodedStringValue getResponseText() {return null;} + * public void setResponseText(EncodedStringValue value) {} + * + * public byte getStoreStatus() {return 0x00;} + * public void setStoreStatus(byte value) {} + * + * public byte[] getStoreStatusText() {return null;} + * public void setStoreStatusText(byte[] value) {} + */ +} diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/SendReq.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/SendReq.java new file mode 100644 index 000000000..5f9699801 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/SendReq.java @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.mmslib.pdu; + +import android.util.Log; + +import com.example.android.mmslib.InvalidHeaderValueException; + +public class SendReq extends MultimediaMessagePdu { + private static final String TAG = "SendReq"; + + public SendReq() { + super(); + + try { + setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ); + setMmsVersion(PduHeaders.CURRENT_MMS_VERSION); + // FIXME: Content-type must be decided according to whether + // SMIL part present. + setContentType("application/vnd.wap.multipart.related".getBytes()); + setFrom(new EncodedStringValue(PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes())); + setTransactionId(generateTransactionId()); + } catch (InvalidHeaderValueException e) { + // Impossible to reach here since all headers we set above are valid. + Log.e(TAG, "Unexpected InvalidHeaderValueException.", e); + throw new RuntimeException(e); + } + } + + private byte[] generateTransactionId() { + String transactionId = "T" + Long.toHexString(System.currentTimeMillis()); + return transactionId.getBytes(); + } + + /** + * Constructor, used when composing a M-Send.req pdu. + * + * @param contentType the content type value + * @param from the from value + * @param mmsVersion current viersion of mms + * @param transactionId the transaction-id value + * @throws InvalidHeaderValueException if parameters are invalid. + * NullPointerException if contentType, form or transactionId is null. + */ + public SendReq(byte[] contentType, + EncodedStringValue from, + int mmsVersion, + byte[] transactionId) throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ); + setContentType(contentType); + setFrom(from); + setMmsVersion(mmsVersion); + setTransactionId(transactionId); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + SendReq(PduHeaders headers) { + super(headers); + } + + /** + * Constructor with given headers and body + * + * @param headers Headers for this PDU. + * @param body Body of this PDu. + */ + SendReq(PduHeaders headers, PduBody body) { + super(headers, body); + } + + /** + * Get Bcc value. + * + * @return the value + */ + public EncodedStringValue[] getBcc() { + return mPduHeaders.getEncodedStringValues(PduHeaders.BCC); + } + + /** + * Add a "BCC" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void addBcc(EncodedStringValue value) { + mPduHeaders.appendEncodedStringValue(value, PduHeaders.BCC); + } + + /** + * Set "BCC" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setBcc(EncodedStringValue[] value) { + mPduHeaders.setEncodedStringValues(value, PduHeaders.BCC); + } + + /** + * Get CC value. + * + * @return the value + */ + public EncodedStringValue[] getCc() { + return mPduHeaders.getEncodedStringValues(PduHeaders.CC); + } + + /** + * Add a "CC" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void addCc(EncodedStringValue value) { + mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC); + } + + /** + * Set "CC" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setCc(EncodedStringValue[] value) { + mPduHeaders.setEncodedStringValues(value, PduHeaders.CC); + } + + /** + * Get Content-type value. + * + * @return the value + */ + public byte[] getContentType() { + return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE); + } + + /** + * Set Content-type value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setContentType(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE); + } + + /** + * Get X-Mms-Delivery-Report value. + * + * @return the value + */ + public int getDeliveryReport() { + return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT); + } + + /** + * Set X-Mms-Delivery-Report value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setDeliveryReport(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT); + } + + /** + * Get X-Mms-Expiry value. + * + * Expiry-value = Value-length + * (Absolute-token Date-value | Relative-token Delta-seconds-value) + * + * @return the value + */ + public long getExpiry() { + return mPduHeaders.getLongInteger(PduHeaders.EXPIRY); + } + + /** + * Set X-Mms-Expiry value. + * + * @param value the value + */ + public void setExpiry(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY); + } + + /** + * Get X-Mms-MessageSize value. + * + * Expiry-value = size of message + * + * @return the value + */ + public long getMessageSize() { + return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE); + } + + /** + * Set X-Mms-MessageSize value. + * + * @param value the value + */ + public void setMessageSize(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE); + } + + /** + * Get X-Mms-Message-Class value. + * Message-class-value = Class-identifier | Token-text + * Class-identifier = Personal | Advertisement | Informational | Auto + * + * @return the value + */ + public byte[] getMessageClass() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS); + } + + /** + * Set X-Mms-Message-Class value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setMessageClass(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS); + } + + /** + * Get X-Mms-Read-Report value. + * + * @return the value + */ + public int getReadReport() { + return mPduHeaders.getOctet(PduHeaders.READ_REPORT); + } + + /** + * Set X-Mms-Read-Report value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setReadReport(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.READ_REPORT); + } + + /** + * Set "To" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTo(EncodedStringValue[] value) { + mPduHeaders.setEncodedStringValues(value, PduHeaders.TO); + } + + /** + * Get X-Mms-Transaction-Id field value. + * + * @return the X-Mms-Report-Allowed value + */ + public byte[] getTransactionId() { + return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); + } + + /** + * Set X-Mms-Transaction-Id field value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTransactionId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); + } + + /* + * Optional, not supported header fields: + * + * public byte getAdaptationAllowed() {return 0}; + * public void setAdaptationAllowed(btye value) {}; + * + * public byte[] getApplicId() {return null;} + * public void setApplicId(byte[] value) {} + * + * public byte[] getAuxApplicId() {return null;} + * public void getAuxApplicId(byte[] value) {} + * + * public byte getContentClass() {return 0x00;} + * public void setApplicId(byte value) {} + * + * public long getDeliveryTime() {return 0}; + * public void setDeliveryTime(long value) {}; + * + * public byte getDrmContent() {return 0x00;} + * public void setDrmContent(byte value) {} + * + * public MmFlagsValue getMmFlags() {return null;} + * public void setMmFlags(MmFlagsValue value) {} + * + * public MmStateValue getMmState() {return null;} + * public void getMmState(MmStateValue value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + * + * public byte getReplyCharging() {return 0x00;} + * public void setReplyCharging(byte value) {} + * + * public byte getReplyChargingDeadline() {return 0x00;} + * public void setReplyChargingDeadline(byte value) {} + * + * public byte[] getReplyChargingId() {return 0x00;} + * public void setReplyChargingId(byte[] value) {} + * + * public long getReplyChargingSize() {return 0;} + * public void setReplyChargingSize(long value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + * + * public byte getStore() {return 0x00;} + * public void setStore(byte value) {} + */ +}