Merge "frameworks/base/opengl: Add NULL check"
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index d271e93..fa5cd8b 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -562,15 +562,24 @@
              * values:</p>
              *
              * <ul>
-             *   <li><em>transactionId (Integer)</em> - The WAP transaction
-             *    ID</li>
+             *   <li><em>transactionId (Integer)</em> - The WAP transaction ID</li>
              *   <li><em>pduType (Integer)</em> - The WAP PDU type</li>
              *   <li><em>header (byte[])</em> - The header of the message</li>
              *   <li><em>data (byte[])</em> - The data payload of the message</li>
+             *   <li><em>contentTypeParameters (HashMap&lt;String,String&gt;)</em>
+             *   - Any parameters associated with the content type
+             *   (decoded from the WSP Content-Type header)</li>
              * </ul>
              *
              * <p>If a BroadcastReceiver encounters an error while processing
              * this intent it should set the result code appropriately.</p>
+             *
+             * <p>The contentTypeParameters extra value is map of content parameters keyed by
+             * their names.</p>
+             *
+             * <p>If any unassigned well-known parameters are encountered, the key of the map will
+             * be 'unassigned/0x...', where '...' is the hex value of the unassigned parameter.  If
+             * a parameter has No-Value the value in the map will be null.</p>
              */
             @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
             public static final String WAP_PUSH_RECEIVED_ACTION =
@@ -583,7 +592,7 @@
              */
             @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
             public static final String SIM_FULL_ACTION =
-                "android.provider.Telephony.SIM_FULL";
+                    "android.provider.Telephony.SIM_FULL";
 
             /**
              * Broadcast Action: An incoming SMS has been rejected by the
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 5bf66e4..bc8607e 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -78,9 +78,12 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.text.Collator;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
 
 /**
  * This class provides a system service that manages input methods.
@@ -466,6 +469,11 @@
         screenOnOffFilt.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt);
 
+        mStatusBar = statusBar;
+        mInputMethodData = IconData.makeIcon("ime", null, 0, 0, 0);
+        mInputMethodIcon = statusBar.addIcon(mInputMethodData, null);
+        statusBar.setIconVisibility(mInputMethodIcon, false);
+
         buildInputMethodListLocked(mMethodList, mMethodMap);
 
         final String enabledStr = Settings.Secure.getString(
@@ -507,11 +515,6 @@
             }
         }
 
-        mStatusBar = statusBar;
-        mInputMethodData = IconData.makeIcon("ime", null, 0, 0, 0);
-        mInputMethodIcon = statusBar.addIcon(mInputMethodData, null);
-        statusBar.setIconVisibility(mInputMethodIcon, false);
-
         mSettingsObserver = new SettingsObserver(mHandler);
         updateFromSettingsLocked();
     }
@@ -1222,6 +1225,7 @@
             if (mCurClient == null || client == null
                     || mCurClient.client.asBinder() != client.asBinder()) {
                 Slog.w(TAG, "Ignoring showInputMethodDialogFromClient of: " + client);
+                return;
             }
 
             mHandler.sendEmptyMessage(MSG_SHOW_IM_PICKER);
@@ -1513,21 +1517,22 @@
             hideInputMethodMenuLocked();
 
             int N = immis.size();
-    
-            mItems = new CharSequence[N];
-            mIms = new InputMethodInfo[N];
-    
-            int j = 0;
+
+            final Map<CharSequence, InputMethodInfo> imMap =
+                new TreeMap<CharSequence, InputMethodInfo>(Collator.getInstance());
+
             for (int i = 0; i < N; ++i) {
                 InputMethodInfo property = immis.get(i);
                 if (property == null) {
                     continue;
                 }
-                mItems[j] = property.loadLabel(pm);
-                mIms[j] = property;
-                j++;
+                imMap.put(property.loadLabel(pm), property);
             }
-    
+
+            N = imMap.size();
+            mItems = imMap.keySet().toArray(new CharSequence[N]);
+            mIms = imMap.values().toArray(new InputMethodInfo[N]);
+
             int checkedItem = 0;
             for (int i = 0; i < N; ++i) {
                 if (mIms[i].getId().equals(lastInputMethodId)) {
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 6ceeb95..cb55808 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -969,7 +969,7 @@
 
     private void setUmsEnabling(boolean enable) {
         synchronized (mListeners) {
-            mUmsEnabling = true;
+            mUmsEnabling = enable;
         }
     }
 
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index 5c21593..0869283 100644
--- a/services/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -540,7 +540,7 @@
     const ISurface::BufferHeap& buffers(mBufferHeap);
     uint32_t w = mLayer.mTransformedBounds.width();
     uint32_t h = mLayer.mTransformedBounds.height();
-    if (buffers.w * h != buffers.h * w) {
+    if (mLayer.getOrientation() & (Transform::ROT_90 | Transform::ROT_270)) {
         int t = w; w = h; h = t;
     }
 
diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
index a636a4b..2f5d3ec 100644
--- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
@@ -24,7 +24,6 @@
 import android.util.Config;
 import android.util.Log;
 
-
 /**
  * WAP push handler class.
  *
@@ -59,7 +58,7 @@
      */
     public int dispatchWapPdu(byte[] pdu) {
 
-        if (Config.LOGD) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
+        if (Config.DEBUG) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
 
         int index = 0;
         int transactionId = pdu[index++] & 0xFF;
@@ -68,7 +67,7 @@
 
         if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
                 (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
-            if (Config.LOGD) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
+            if (Config.DEBUG) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
             return Intents.RESULT_SMS_HANDLED;
         }
 
@@ -81,7 +80,7 @@
          * So it will be encoded in no more than 5 octets.
          */
         if (pduDecoder.decodeUintvarInteger(index) == false) {
-            if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Length error.");
+            if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Length error.");
             return Intents.RESULT_SMS_GENERIC_ERROR;
         }
         headerLength = (int)pduDecoder.getValue32();
@@ -102,136 +101,44 @@
          * Length = Uintvar-integer
          */
         if (pduDecoder.decodeContentType(index) == false) {
-            if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
+            if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
             return Intents.RESULT_SMS_GENERIC_ERROR;
         }
-        int binaryContentType;
+
         String mimeType = pduDecoder.getValueString();
-        if (mimeType == null) {
-            binaryContentType = (int)pduDecoder.getValue32();
-            // TODO we should have more generic way to map binaryContentType code to mimeType.
-            switch (binaryContentType) {
-                case WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_XML:
-                    mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML;
-                    break;
-                case WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_WBXML:
-                    mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML;
-                    break;
-                case WspTypeDecoder.CONTENT_TYPE_B_PUSH_SI:
-                    mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SI;
-                    break;
-                case WspTypeDecoder.CONTENT_TYPE_B_PUSH_SL:
-                    mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SL;
-                    break;
-                case WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO:
-                    mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO;
-                    break;
-                case WspTypeDecoder.CONTENT_TYPE_B_MMS:
-                    mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS;
-                    break;
-                case WspTypeDecoder.CONTENT_TYPE_B_VND_DOCOMO_PF:
-                    mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_VND_DOCOMO_PF;
-                    break;
-                default:
-                    if (Config.LOGD) {
-                        Log.w(LOG_TAG,
-                                "Received PDU. Unsupported Content-Type = " + binaryContentType);
-                    }
-                return Intents.RESULT_SMS_HANDLED;
-            }
-        } else {
-            if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML)) {
-                binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_XML;
-            } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML)) {
-                binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_WBXML;
-            } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SI)) {
-                binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_SI;
-            } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SL)) {
-                binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_SL;
-            } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO)) {
-                binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO;
-            } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS)) {
-                binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_MMS;
-            } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_VND_DOCOMO_PF)) {
-                binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_VND_DOCOMO_PF;
-            } else {
-                if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Unknown Content-Type = " + mimeType);
-                return Intents.RESULT_SMS_HANDLED;
-            }
-        }
+
         index += pduDecoder.getDecodedDataLength();
 
-        boolean dispatchedByApplication = false;
-        switch (binaryContentType) {
-            case WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO:
-                dispatchWapPdu_PushCO(pdu, transactionId, pduType, headerStartIndex, headerLength);
-                dispatchedByApplication = true;
-                break;
-            case WspTypeDecoder.CONTENT_TYPE_B_MMS:
-                dispatchWapPdu_MMS(pdu, transactionId, pduType, headerStartIndex, headerLength);
-                dispatchedByApplication = true;
-                break;
-            default:
-                break;
-        }
-        if (dispatchedByApplication == false) {
-            dispatchWapPdu_default(pdu, transactionId, pduType, mimeType,
-                                   headerStartIndex, headerLength);
-        }
-        return Activity.RESULT_OK;
-    }
-
-    private void dispatchWapPdu_default(byte[] pdu, int transactionId, int pduType,
-                                        String mimeType, int headerStartIndex, int headerLength) {
         byte[] header = new byte[headerLength];
         System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
-        int dataIndex = headerStartIndex + headerLength;
-        byte[] data;
 
-        data = new byte[pdu.length - dataIndex];
-        System.arraycopy(pdu, dataIndex, data, 0, data.length);
+        byte[] intentData;
+        String permission;
+
+        if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+            intentData = pdu;
+        } else {
+            int dataIndex = headerStartIndex + headerLength;
+            intentData = new byte[pdu.length - dataIndex];
+            System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
+        }
+
+        if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_MMS)) {
+            permission = "android.permission.RECEIVE_MMS";
+        } else {
+            permission = "android.permission.RECEIVE_WAP_PUSH";
+        }
 
         Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION);
         intent.setType(mimeType);
         intent.putExtra("transactionId", transactionId);
         intent.putExtra("pduType", pduType);
         intent.putExtra("header", header);
-        intent.putExtra("data", data);
+        intent.putExtra("data", intentData);
+        intent.putExtra("contentTypeParameters", pduDecoder.getContentParameters());
 
-        mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH");
+        mSmsDispatcher.dispatch(intent, permission);
+
+        return Activity.RESULT_OK;
     }
-
-    private void dispatchWapPdu_PushCO(byte[] pdu, int transactionId, int pduType,
-                                       int headerStartIndex, int headerLength) {
-        byte[] header = new byte[headerLength];
-        System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
-
-        Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION);
-        intent.setType(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO);
-        intent.putExtra("transactionId", transactionId);
-        intent.putExtra("pduType", pduType);
-        intent.putExtra("header", header);
-        intent.putExtra("data", pdu);
-
-        mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH");
-    }
-
-    private void dispatchWapPdu_MMS(byte[] pdu, int transactionId, int pduType,
-                                    int headerStartIndex, int headerLength) {
-        byte[] header = new byte[headerLength];
-        System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
-        int dataIndex = headerStartIndex + headerLength;
-        byte[] data = new byte[pdu.length - dataIndex];
-        System.arraycopy(pdu, dataIndex, data, 0, data.length);
-
-        Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION);
-        intent.setType(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS);
-        intent.putExtra("transactionId", transactionId);
-        intent.putExtra("pduType", pduType);
-        intent.putExtra("header", header);
-        intent.putExtra("data", data);
-
-        mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_MMS");
-    }
-}
-
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
index 336bc82..6bf6b13 100644
--- a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
+++ b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
@@ -16,11 +16,12 @@
 
 package com.android.internal.telephony;
 
+import java.util.HashMap;
 
 /**
- *  Implement the WSP data type decoder.
+ * Implement the WSP data type decoder.
  *
- *  @hide
+ * @hide
  */
 public class WspTypeDecoder {
 
@@ -30,35 +31,176 @@
     public static final int PDU_TYPE_PUSH = 0x06;
     public static final int PDU_TYPE_CONFIRMED_PUSH = 0x07;
 
-    // TODO we should have mapping between those binary code and mime type string.
-    //  see http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
+    private final static HashMap<Integer, String> WELL_KNOWN_MIME_TYPES =
+            new HashMap<Integer, String>();
 
-    public static final int CONTENT_TYPE_B_DRM_RIGHTS_XML = 0x4a;
-    public static final int CONTENT_TYPE_B_DRM_RIGHTS_WBXML = 0x4b;
-    public static final int CONTENT_TYPE_B_PUSH_SI = 0x2e;
-    public static final int CONTENT_TYPE_B_PUSH_SL = 0x30;
-    public static final int CONTENT_TYPE_B_PUSH_CO = 0x32;
-    public static final int CONTENT_TYPE_B_MMS = 0x3e;
-    public static final int CONTENT_TYPE_B_VND_DOCOMO_PF = 0x0310;
+    private final static HashMap<Integer, String> WELL_KNOWN_PARAMETERS =
+            new HashMap<Integer, String>();
 
-    public static final String CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML =
-            "application/vnd.oma.drm.rights+xml";
-    public static final String CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML =
-            "application/vnd.oma.drm.rights+wbxml";
-    public static final String CONTENT_MIME_TYPE_B_PUSH_SI = "application/vnd.wap.sic";
-    public static final String CONTENT_MIME_TYPE_B_PUSH_SL = "application/vnd.wap.slc";
-    public static final String CONTENT_MIME_TYPE_B_PUSH_CO = "application/vnd.wap.coc";
-    public static final String CONTENT_MIME_TYPE_B_MMS = "application/vnd.wap.mms-message";
-    public static final String CONTENT_MIME_TYPE_B_VND_DOCOMO_PF = "application/vnd.docomo.pf";
+    private static final int Q_VALUE = 0x00;
 
-    public static final int PARAMETER_ID_X_WAP_APPLICATION_ID = 0x2f;
+    static {
+        WELL_KNOWN_MIME_TYPES.put(0x00, "*/*");
+        WELL_KNOWN_MIME_TYPES.put(0x01, "text/*");
+        WELL_KNOWN_MIME_TYPES.put(0x02, "text/html");
+        WELL_KNOWN_MIME_TYPES.put(0x03, "text/plain");
+        WELL_KNOWN_MIME_TYPES.put(0x04, "text/x-hdml");
+        WELL_KNOWN_MIME_TYPES.put(0x05, "text/x-ttml");
+        WELL_KNOWN_MIME_TYPES.put(0x06, "text/x-vCalendar");
+        WELL_KNOWN_MIME_TYPES.put(0x07, "text/x-vCard");
+        WELL_KNOWN_MIME_TYPES.put(0x08, "text/vnd.wap.wml");
+        WELL_KNOWN_MIME_TYPES.put(0x09, "text/vnd.wap.wmlscript");
+        WELL_KNOWN_MIME_TYPES.put(0x0A, "text/vnd.wap.wta-event");
+        WELL_KNOWN_MIME_TYPES.put(0x0B, "multipart/*");
+        WELL_KNOWN_MIME_TYPES.put(0x0C, "multipart/mixed");
+        WELL_KNOWN_MIME_TYPES.put(0x0D, "multipart/form-data");
+        WELL_KNOWN_MIME_TYPES.put(0x0E, "multipart/byterantes");
+        WELL_KNOWN_MIME_TYPES.put(0x0F, "multipart/alternative");
+        WELL_KNOWN_MIME_TYPES.put(0x10, "application/*");
+        WELL_KNOWN_MIME_TYPES.put(0x11, "application/java-vm");
+        WELL_KNOWN_MIME_TYPES.put(0x12, "application/x-www-form-urlencoded");
+        WELL_KNOWN_MIME_TYPES.put(0x13, "application/x-hdmlc");
+        WELL_KNOWN_MIME_TYPES.put(0x14, "application/vnd.wap.wmlc");
+        WELL_KNOWN_MIME_TYPES.put(0x15, "application/vnd.wap.wmlscriptc");
+        WELL_KNOWN_MIME_TYPES.put(0x16, "application/vnd.wap.wta-eventc");
+        WELL_KNOWN_MIME_TYPES.put(0x17, "application/vnd.wap.uaprof");
+        WELL_KNOWN_MIME_TYPES.put(0x18, "application/vnd.wap.wtls-ca-certificate");
+        WELL_KNOWN_MIME_TYPES.put(0x19, "application/vnd.wap.wtls-user-certificate");
+        WELL_KNOWN_MIME_TYPES.put(0x1A, "application/x-x509-ca-cert");
+        WELL_KNOWN_MIME_TYPES.put(0x1B, "application/x-x509-user-cert");
+        WELL_KNOWN_MIME_TYPES.put(0x1C, "image/*");
+        WELL_KNOWN_MIME_TYPES.put(0x1D, "image/gif");
+        WELL_KNOWN_MIME_TYPES.put(0x1E, "image/jpeg");
+        WELL_KNOWN_MIME_TYPES.put(0x1F, "image/tiff");
+        WELL_KNOWN_MIME_TYPES.put(0x20, "image/png");
+        WELL_KNOWN_MIME_TYPES.put(0x21, "image/vnd.wap.wbmp");
+        WELL_KNOWN_MIME_TYPES.put(0x22, "application/vnd.wap.multipart.*");
+        WELL_KNOWN_MIME_TYPES.put(0x23, "application/vnd.wap.multipart.mixed");
+        WELL_KNOWN_MIME_TYPES.put(0x24, "application/vnd.wap.multipart.form-data");
+        WELL_KNOWN_MIME_TYPES.put(0x25, "application/vnd.wap.multipart.byteranges");
+        WELL_KNOWN_MIME_TYPES.put(0x26, "application/vnd.wap.multipart.alternative");
+        WELL_KNOWN_MIME_TYPES.put(0x27, "application/xml");
+        WELL_KNOWN_MIME_TYPES.put(0x28, "text/xml");
+        WELL_KNOWN_MIME_TYPES.put(0x29, "application/vnd.wap.wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x2A, "application/x-x968-cross-cert");
+        WELL_KNOWN_MIME_TYPES.put(0x2B, "application/x-x968-ca-cert");
+        WELL_KNOWN_MIME_TYPES.put(0x2C, "application/x-x968-user-cert");
+        WELL_KNOWN_MIME_TYPES.put(0x2D, "text/vnd.wap.si");
+        WELL_KNOWN_MIME_TYPES.put(0x2E, "application/vnd.wap.sic");
+        WELL_KNOWN_MIME_TYPES.put(0x2F, "text/vnd.wap.sl");
+        WELL_KNOWN_MIME_TYPES.put(0x30, "application/vnd.wap.slc");
+        WELL_KNOWN_MIME_TYPES.put(0x31, "text/vnd.wap.co");
+        WELL_KNOWN_MIME_TYPES.put(0x32, "application/vnd.wap.coc");
+        WELL_KNOWN_MIME_TYPES.put(0x33, "application/vnd.wap.multipart.related");
+        WELL_KNOWN_MIME_TYPES.put(0x34, "application/vnd.wap.sia");
+        WELL_KNOWN_MIME_TYPES.put(0x35, "text/vnd.wap.connectivity-xml");
+        WELL_KNOWN_MIME_TYPES.put(0x36, "application/vnd.wap.connectivity-wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x37, "application/pkcs7-mime");
+        WELL_KNOWN_MIME_TYPES.put(0x38, "application/vnd.wap.hashed-certificate");
+        WELL_KNOWN_MIME_TYPES.put(0x39, "application/vnd.wap.signed-certificate");
+        WELL_KNOWN_MIME_TYPES.put(0x3A, "application/vnd.wap.cert-response");
+        WELL_KNOWN_MIME_TYPES.put(0x3B, "application/xhtml+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x3C, "application/wml+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x3D, "text/css");
+        WELL_KNOWN_MIME_TYPES.put(0x3E, "application/vnd.wap.mms-message");
+        WELL_KNOWN_MIME_TYPES.put(0x3F, "application/vnd.wap.rollover-certificate");
+        WELL_KNOWN_MIME_TYPES.put(0x40, "application/vnd.wap.locc+wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x41, "application/vnd.wap.loc+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x42, "application/vnd.syncml.dm+wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x43, "application/vnd.syncml.dm+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x44, "application/vnd.syncml.notification");
+        WELL_KNOWN_MIME_TYPES.put(0x45, "application/vnd.wap.xhtml+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x46, "application/vnd.wv.csp.cir");
+        WELL_KNOWN_MIME_TYPES.put(0x47, "application/vnd.oma.dd+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x48, "application/vnd.oma.drm.message");
+        WELL_KNOWN_MIME_TYPES.put(0x49, "application/vnd.oma.drm.content");
+        WELL_KNOWN_MIME_TYPES.put(0x4A, "application/vnd.oma.drm.rights+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x4B, "application/vnd.oma.drm.rights+wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x4C, "application/vnd.wv.csp+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x4D, "application/vnd.wv.csp+wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x4E, "application/vnd.syncml.ds.notification");
+        WELL_KNOWN_MIME_TYPES.put(0x4F, "audio/*");
+        WELL_KNOWN_MIME_TYPES.put(0x50, "video/*");
+        WELL_KNOWN_MIME_TYPES.put(0x51, "application/vnd.oma.dd2+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x52, "application/mikey");
+        WELL_KNOWN_MIME_TYPES.put(0x53, "application/vnd.oma.dcd");
+        WELL_KNOWN_MIME_TYPES.put(0x54, "application/vnd.oma.dcdc");
 
+        WELL_KNOWN_MIME_TYPES.put(0x0201, "application/vnd.uplanet.cacheop-wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x0202, "application/vnd.uplanet.signal");
+        WELL_KNOWN_MIME_TYPES.put(0x0203, "application/vnd.uplanet.alert-wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x0204, "application/vnd.uplanet.list-wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x0205, "application/vnd.uplanet.listcmd-wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x0206, "application/vnd.uplanet.channel-wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x0207, "application/vnd.uplanet.provisioning-status-uri");
+        WELL_KNOWN_MIME_TYPES.put(0x0208, "x-wap.multipart/vnd.uplanet.header-set");
+        WELL_KNOWN_MIME_TYPES.put(0x0209, "application/vnd.uplanet.bearer-choice-wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x020A, "application/vnd.phonecom.mmc-wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x020B, "application/vnd.nokia.syncset+wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x020C, "image/x-up-wpng");
+        WELL_KNOWN_MIME_TYPES.put(0x0300, "application/iota.mmc-wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x0301, "application/iota.mmc-xml");
+        WELL_KNOWN_MIME_TYPES.put(0x0302, "application/vnd.syncml+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x0303, "application/vnd.syncml+wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x0304, "text/vnd.wap.emn+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x0305, "text/calendar");
+        WELL_KNOWN_MIME_TYPES.put(0x0306, "application/vnd.omads-email+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x0307, "application/vnd.omads-file+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x0308, "application/vnd.omads-folder+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x0309, "text/directory;profile=vCard");
+        WELL_KNOWN_MIME_TYPES.put(0x030A, "application/vnd.wap.emn+wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x030B, "application/vnd.nokia.ipdc-purchase-response");
+        WELL_KNOWN_MIME_TYPES.put(0x030C, "application/vnd.motorola.screen3+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x030D, "application/vnd.motorola.screen3+gzip");
+        WELL_KNOWN_MIME_TYPES.put(0x030E, "application/vnd.cmcc.setting+wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x030F, "application/vnd.cmcc.bombing+wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x0310, "application/vnd.docomo.pf");
+        WELL_KNOWN_MIME_TYPES.put(0x0311, "application/vnd.docomo.ub");
+        WELL_KNOWN_MIME_TYPES.put(0x0312, "application/vnd.omaloc-supl-init");
+        WELL_KNOWN_MIME_TYPES.put(0x0313, "application/vnd.oma.group-usage-list+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x0314, "application/oma-directory+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x0315, "application/vnd.docomo.pf2");
+        WELL_KNOWN_MIME_TYPES.put(0x0316, "application/vnd.oma.drm.roap-trigger+wbxml");
+        WELL_KNOWN_MIME_TYPES.put(0x0317, "application/vnd.sbm.mid2");
+        WELL_KNOWN_MIME_TYPES.put(0x0318, "application/vnd.wmf.bootstrap");
+        WELL_KNOWN_MIME_TYPES.put(0x0319, "application/vnc.cmcc.dcd+xml");
+        WELL_KNOWN_MIME_TYPES.put(0x031A, "application/vnd.sbm.cid");
+        WELL_KNOWN_MIME_TYPES.put(0x031B, "application/vnd.oma.bcast.provisioningtrigger");
+
+        WELL_KNOWN_PARAMETERS.put(0x00, "Q");
+        WELL_KNOWN_PARAMETERS.put(0x01, "Charset");
+        WELL_KNOWN_PARAMETERS.put(0x02, "Level");
+        WELL_KNOWN_PARAMETERS.put(0x03, "Type");
+        WELL_KNOWN_PARAMETERS.put(0x07, "Differences");
+        WELL_KNOWN_PARAMETERS.put(0x08, "Padding");
+        WELL_KNOWN_PARAMETERS.put(0x09, "Type");
+        WELL_KNOWN_PARAMETERS.put(0x0E, "Max-Age");
+        WELL_KNOWN_PARAMETERS.put(0x10, "Secure");
+        WELL_KNOWN_PARAMETERS.put(0x11, "SEC");
+        WELL_KNOWN_PARAMETERS.put(0x12, "MAC");
+        WELL_KNOWN_PARAMETERS.put(0x13, "Creation-date");
+        WELL_KNOWN_PARAMETERS.put(0x14, "Modification-date");
+        WELL_KNOWN_PARAMETERS.put(0x15, "Read-date");
+        WELL_KNOWN_PARAMETERS.put(0x16, "Size");
+        WELL_KNOWN_PARAMETERS.put(0x17, "Name");
+        WELL_KNOWN_PARAMETERS.put(0x18, "Filename");
+        WELL_KNOWN_PARAMETERS.put(0x19, "Start");
+        WELL_KNOWN_PARAMETERS.put(0x1A, "Start-info");
+        WELL_KNOWN_PARAMETERS.put(0x1B, "Comment");
+        WELL_KNOWN_PARAMETERS.put(0x1C, "Domain");
+        WELL_KNOWN_PARAMETERS.put(0x1D, "Path");
+    }
+
+    public static final String CONTENT_TYPE_B_PUSH_CO = "application/vnd.wap.coc";
+    public static final String CONTENT_TYPE_B_MMS = "application/vnd.wap.mms-message";
 
     byte[] wspData;
     int    dataLength;
     long   unsigned32bit;
     String stringValue;
 
+    HashMap<String, String> contentParameters;
+
     public WspTypeDecoder(byte[] pdu) {
         wspData = pdu;
     }
@@ -69,17 +211,17 @@
      * @param startIndex The starting position of the "Text-string" in this pdu
      *
      * @return false when error(not a Text-string) occur
-     *         return value can be retrieved by getValueString() method
-     *         length of data in pdu can be retrieved by getValue32() method
+     *         return value can be retrieved by getValueString() method length of data in pdu can be
+     *         retrieved by getDecodedDataLength() method
      */
     public boolean decodeTextString(int startIndex) {
         int index = startIndex;
         while (wspData[index] != 0) {
             index++;
         }
-        dataLength  = index - startIndex + 1;
+        dataLength = index - startIndex + 1;
         if (wspData[startIndex] == 127) {
-            stringValue = new String(wspData, startIndex+1, dataLength - 2);
+            stringValue = new String(wspData, startIndex + 1, dataLength - 2);
         } else {
             stringValue = new String(wspData, startIndex, dataLength - 1);
         }
@@ -87,13 +229,33 @@
     }
 
     /**
+     * Decode the "Token-text" type for WSP pdu
+     *
+     * @param startIndex The starting position of the "Token-text" in this pdu
+     *
+     * @return always true
+     *         return value can be retrieved by getValueString() method
+     *         length of data in pdu can be retrieved by getDecodedDataLength() method
+     */
+    public boolean decodeTokenText(int startIndex) {
+        int index = startIndex;
+        while (wspData[index] != 0) {
+            index++;
+        }
+        dataLength = index - startIndex + 1;
+        stringValue = new String(wspData, startIndex, dataLength - 1);
+
+        return true;
+    }
+
+    /**
      * Decode the "Short-integer" type for WSP pdu
      *
      * @param startIndex The starting position of the "Short-integer" in this pdu
      *
      * @return false when error(not a Short-integer) occur
      *         return value can be retrieved by getValue32() method
-     *         length of data in pdu can be retrieved by getValue32() method
+     *         length of data in pdu can be retrieved by getDecodedDataLength() method
      */
     public boolean decodeShortInteger(int startIndex) {
         if ((wspData[startIndex] & 0x80) == 0) {
@@ -111,7 +273,7 @@
      *
      * @return false when error(not a Long-integer) occur
      *         return value can be retrieved by getValue32() method
-     *         length of data in pdu can be retrieved by getValue32() method
+     *         length of data in pdu can be retrieved by getDecodedDataLength() method
      */
     public boolean decodeLongInteger(int startIndex) {
         int lengthMultiOctet = wspData[startIndex] & 0xff;
@@ -120,10 +282,10 @@
             return false;
         }
         unsigned32bit = 0;
-        for (int i=1; i<=lengthMultiOctet; i++) {
-            unsigned32bit = (unsigned32bit << 8) | (wspData[startIndex+i] & 0xff);
+        for (int i = 1; i <= lengthMultiOctet; i++) {
+            unsigned32bit = (unsigned32bit << 8) | (wspData[startIndex + i] & 0xff);
         }
-        dataLength = 1+lengthMultiOctet;
+        dataLength = 1 + lengthMultiOctet;
         return true;
     }
 
@@ -134,7 +296,7 @@
      *
      * @return false when error(not a Integer-Value) occur
      *         return value can be retrieved by getValue32() method
-     *         length of data in pdu can be retrieved by getValue32() method
+     *         length of data in pdu can be retrieved by getDecodedDataLength() method
      */
     public boolean decodeIntegerValue(int startIndex) {
         if (decodeShortInteger(startIndex) == true) {
@@ -150,10 +312,10 @@
      *
      * @return false when error(not a Uintvar-integer) occur
      *         return value can be retrieved by getValue32() method
-     *         length of data in pdu can be retrieved by getValue32() method
+     *         length of data in pdu can be retrieved by getDecodedDataLength() method
      */
     public boolean decodeUintvarInteger(int startIndex) {
-        int  index = startIndex;
+        int index = startIndex;
 
         unsigned32bit = 0;
         while ((wspData[index] & 0x80) != 0) {
@@ -175,7 +337,7 @@
      *
      * @return false when error(not a Value-length) occur
      *         return value can be retrieved by getValue32() method
-     *         length of data in pdu can be retrieved by getValue32() method
+     *         length of data in pdu can be retrieved by getDecodedDataLength() method
      */
     public boolean decodeValueLength(int startIndex) {
         if ((wspData[startIndex] & 0xff) > WAP_PDU_LENGTH_QUOTE) {
@@ -185,22 +347,22 @@
             unsigned32bit = wspData[startIndex];
             dataLength = 1;
         } else {
-            decodeUintvarInteger(startIndex+1);
-            dataLength ++;
+            decodeUintvarInteger(startIndex + 1);
+            dataLength++;
         }
         return true;
     }
 
     /**
-    * Decode the "Extension-media" type for WSP PDU.
-    *
-    * @param startIndex The starting position of the "Extension-media" in this PDU.
-    *
-    * @return false on error, such as if there is no Extension-media at startIndex.
-    * Side-effects: updates stringValue (available with getValueString()), which will be
-    * null on error. The length of the data in the PDU is available with getValue32(), 0
-    * on error.
-    */
+     * Decode the "Extension-media" type for WSP PDU.
+     *
+     * @param startIndex The starting position of the "Extension-media" in this PDU.
+     *
+     * @return false on error, such as if there is no Extension-media at startIndex.
+     *         Side-effects: updates stringValue (available with
+     *         getValueString()), which will be null on error. The length of the
+     *         data in the PDU is available with getValue32(), 0 on error.
+     */
     public boolean decodeExtensionMedia(int startIndex) {
         int index = startIndex;
         dataLength = 0;
@@ -212,7 +374,7 @@
             index++;
         }
 
-        dataLength  = index - startIndex + 1;
+        dataLength = index - startIndex + 1;
         stringValue = new String(wspData, startIndex, dataLength - 1);
 
         return rtrn;
@@ -225,7 +387,7 @@
      *
      * @return false when error(not a Constrained-encoding) occur
      *         return value can be retrieved first by getValueString() and second by getValue32() method
-     *         length of data in pdu can be retrieved by getValue32() method
+     *         length of data in pdu can be retrieved by getDecodedDataLength() method
      */
     public boolean decodeConstrainedEncoding(int startIndex) {
         if (decodeShortInteger(startIndex) == true) {
@@ -240,31 +402,162 @@
      *
      * @param startIndex The starting position of the "Content-type" in this pdu
      *
-     * @return false when error(not a Content-type) occur
-     *         return value can be retrieved first by getValueString() and second by getValue32()
-     *         method length of data in pdu can be retrieved by getValue32() method
+     * @return false when error(not a Content-type) occurs
+     *         If a content type exists in the headers (either as inline string, or as well-known
+     *         value), getValueString() will return it. If a 'well known value' is encountered that
+     *         cannot be mapped to a string mime type, getValueString() will return null, and
+     *         getValue32() will return the unknown content type value.
+     *         length of data in pdu can be retrieved by getDecodedDataLength() method
+     *         Any content type parameters will be accessible via getContentParameters()
      */
     public boolean decodeContentType(int startIndex) {
         int mediaPrefixLength;
-        long mediaFieldLength;
+        contentParameters = new HashMap<String, String>();
 
-        if (decodeValueLength(startIndex) == false) {
-            return decodeConstrainedEncoding(startIndex);
-        }
-        mediaPrefixLength = getDecodedDataLength();
-        mediaFieldLength = getValue32();
-        if (decodeIntegerValue(startIndex + mediaPrefixLength) == true) {
-            dataLength += mediaPrefixLength;
-            stringValue = null;
-            return true;
-        }
-        if (decodeExtensionMedia(startIndex + mediaPrefixLength) == true) {
-            dataLength += mediaPrefixLength;
-            return true;
+        try {
+            if (decodeValueLength(startIndex) == false) {
+                boolean found = decodeConstrainedEncoding(startIndex);
+                if (found) {
+                    expandWellKnownMimeType();
+                }
+                return found;
+            }
+            int headersLength = (int) unsigned32bit;
+            mediaPrefixLength = getDecodedDataLength();
+            if (decodeIntegerValue(startIndex + mediaPrefixLength) == true) {
+                dataLength += mediaPrefixLength;
+                int readLength = dataLength;
+                stringValue = null;
+                expandWellKnownMimeType();
+                long wellKnownValue = unsigned32bit;
+                String mimeType = stringValue;
+                if (readContentParameters(startIndex + dataLength,
+                        (headersLength - (dataLength - mediaPrefixLength)), 0)) {
+                    dataLength += readLength;
+                    unsigned32bit = wellKnownValue;
+                    stringValue = mimeType;
+                    return true;
+                }
+                return false;
+            }
+            if (decodeExtensionMedia(startIndex + mediaPrefixLength) == true) {
+                dataLength += mediaPrefixLength;
+                int readLength = dataLength;
+                expandWellKnownMimeType();
+                long wellKnownValue = unsigned32bit;
+                String mimeType = stringValue;
+                if (readContentParameters(startIndex + dataLength,
+                        (headersLength - (dataLength - mediaPrefixLength)), 0)) {
+                    dataLength += readLength;
+                    unsigned32bit = wellKnownValue;
+                    stringValue = mimeType;
+                    return true;
+                }
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            //something doesn't add up
+            return false;
         }
         return false;
     }
 
+    private boolean readContentParameters(int startIndex, int leftToRead, int accumulator) {
+
+        int totalRead = 0;
+
+        if (leftToRead > 0) {
+            byte nextByte = wspData[startIndex];
+            String value = null;
+            String param = null;
+            if ((nextByte & 0x80) == 0x00 && nextByte > 31) { // untyped
+                decodeTokenText(startIndex);
+                param = stringValue;
+                totalRead += dataLength;
+            } else { // typed
+                if (decodeIntegerValue(startIndex)) {
+                    totalRead += dataLength;
+                    int wellKnownParameterValue = (int) unsigned32bit;
+                    param = WELL_KNOWN_PARAMETERS.get(wellKnownParameterValue);
+                    if (param == null) {
+                        param = "unassigned/0x" + Long.toHexString(wellKnownParameterValue);
+                    }
+                    // special case for the "Q" parameter, value is a uintvar
+                    if (wellKnownParameterValue == Q_VALUE) {
+                        if (decodeUintvarInteger(startIndex + totalRead)) {
+                            totalRead += dataLength;
+                            value = String.valueOf(unsigned32bit);
+                            contentParameters.put(param, value);
+                            return readContentParameters(startIndex + totalRead, leftToRead
+                                                            - totalRead, accumulator + totalRead);
+                        } else {
+                            return false;
+                        }
+                    }
+                } else {
+                    return false;
+                }
+            }
+
+            if (decodeNoValue(startIndex + totalRead)) {
+                totalRead += dataLength;
+                value = null;
+            } else if (decodeIntegerValue(startIndex + totalRead)) {
+                totalRead += dataLength;
+                int intValue = (int) unsigned32bit;
+                if (intValue == 0) {
+                    value = "";
+                } else {
+                    value = String.valueOf(intValue);
+                }
+            } else {
+                decodeTokenText(startIndex + totalRead);
+                totalRead += dataLength;
+                value = stringValue;
+                if (value.startsWith("\"")) {
+                    // quoted string, so remove the quote
+                    value = value.substring(1);
+                }
+            }
+            contentParameters.put(param, value);
+            return readContentParameters(startIndex + totalRead, leftToRead - totalRead,
+                                            accumulator + totalRead);
+
+        } else {
+            dataLength = accumulator;
+            return true;
+        }
+    }
+
+    /**
+     * Check if the next byte is No-Value
+     *
+     * @param startIndex The starting position of the "Content length" in this pdu
+     *
+     * @return true if and only if the next byte is 0x00
+     */
+    private boolean decodeNoValue(int startIndex) {
+        if (wspData[startIndex] == 0) {
+            dataLength = 1;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Populate stringValue with the mime type corresponding to the value in unsigned32bit
+     *
+     * Sets unsigned32bit to -1 if stringValue is already populated
+     */
+    private void expandWellKnownMimeType() {
+        if (stringValue == null) {
+            int binaryContentType = (int) unsigned32bit;
+            stringValue = WELL_KNOWN_MIME_TYPES.get(binaryContentType);
+        } else {
+            unsigned32bit = -1;
+        }
+    }
+
     /**
      * Decode the "Content length" type for WSP pdu
      *
@@ -272,7 +565,7 @@
      *
      * @return false when error(not a Content length) occur
      *         return value can be retrieved by getValue32() method
-     *         length of data in pdu can be retrieved by getValue32() method
+     *         length of data in pdu can be retrieved by getDecodedDataLength() method
      */
     public boolean decodeContentLength(int startIndex) {
         return decodeIntegerValue(startIndex);
@@ -285,7 +578,7 @@
      *
      * @return false when error(not a Content location) occur
      *         return value can be retrieved by getValueString() method
-     *         length of data in pdu can be retrieved by getValue32() method
+     *         length of data in pdu can be retrieved by getDecodedDataLength() method
      */
     public boolean decodeContentLocation(int startIndex) {
         return decodeTextString(startIndex);
@@ -298,7 +591,8 @@
      *
      * @return false when error(not a X-Wap-Application-Id) occur
      *         return value can be retrieved first by getValueString() and second by getValue32()
-     *         method length of data in pdu can be retrieved by getValue32() method
+     *         method
+     *         length of data in pdu can be retrieved by getDecodedDataLength() method
      */
     public boolean decodeXWapApplicationId(int startIndex) {
         if (decodeIntegerValue(startIndex) == true) {
@@ -315,7 +609,7 @@
      *
      * @return false when error(not a X-Wap-Content-URI) occur
      *         return value can be retrieved by getValueString() method
-     *         length of data in pdu can be retrieved by getValue32() method
+     *         length of data in pdu can be retrieved by getDecodedDataLength() method
      */
     public boolean decodeXWapContentURI(int startIndex) {
         return decodeTextString(startIndex);
@@ -328,7 +622,7 @@
      *
      * @return false when error(not a X-Wap-Initiator-URI) occur
      *         return value can be retrieved by getValueString() method
-     *         length of data in pdu can be retrieved by getValue32() method
+     *         length of data in pdu can be retrieved by getDecodedDataLength() method
      */
     public boolean decodeXWapInitiatorURI(int startIndex) {
         return decodeTextString(startIndex);
@@ -354,4 +648,18 @@
     public String getValueString() {
         return stringValue;
     }
+
+    /**
+     * Any parameters encountered as part of a decodeContentType() invocation.
+     *
+     * @return a map of content parameters keyed by their names, or null if
+     *         decodeContentType() has not been called If any unassigned
+     *         well-known parameters are encountered, the key of the map will be
+     *         'unassigned/0x...', where '...' is the hex value of the
+     *         unassigned parameter.  If a parameter has No-Value the value will be null.
+     *
+     */
+    public HashMap<String, String> getContentParameters() {
+        return contentParameters;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cat/AppInterface.java b/telephony/java/com/android/internal/telephony/cat/AppInterface.java
index 0ba3e11..2eb6ccb 100644
--- a/telephony/java/com/android/internal/telephony/cat/AppInterface.java
+++ b/telephony/java/com/android/internal/telephony/cat/AppInterface.java
@@ -58,7 +58,8 @@
         SET_UP_EVENT_LIST(0x05),
         SET_UP_IDLE_MODE_TEXT(0x28),
         SET_UP_MENU(0x25),
-        SET_UP_CALL(0x10);
+        SET_UP_CALL(0x10),
+        PROVIDE_LOCAL_INFORMATION(0x26);
 
         private int mValue;
 
diff --git a/telephony/java/com/android/internal/telephony/cat/CatService.java b/telephony/java/com/android/internal/telephony/cat/CatService.java
index b916713..1e23e34 100644
--- a/telephony/java/com/android/internal/telephony/cat/CatService.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatService.java
@@ -22,6 +22,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.os.SystemProperties;
 
 import com.android.internal.telephony.IccUtils;
 import com.android.internal.telephony.CommandsInterface;
@@ -245,48 +246,46 @@
 
         CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams);
         switch (cmdParams.getCommandType()) {
-        case SET_UP_MENU:
-            if (removeMenu(cmdMsg.getMenu())) {
-                mMenuCmd = null;
-            } else {
-                mMenuCmd = cmdMsg;
-            }
-            sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0,
-                    null);
-            break;
-        case DISPLAY_TEXT:
-            // when application is not required to respond, send an immediate
-            // response.
-            if (!cmdMsg.geTextMessage().responseNeeded) {
-                sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false,
-                        0, null);
-            }
-            break;
-        case REFRESH:
-            // ME side only handles refresh commands which meant to remove IDLE
-            // MODE TEXT.
-            cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT
-                    .value();
-            break;
-        case SET_UP_IDLE_MODE_TEXT:
-            sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false,
-                    0, null);
-            break;
-        case LAUNCH_BROWSER:
-        case SELECT_ITEM:
-        case GET_INPUT:
-        case GET_INKEY:
-        case SEND_DTMF:
-        case SEND_SMS:
-        case SEND_SS:
-        case SEND_USSD:
-        case PLAY_TONE:
-        case SET_UP_CALL:
-            // nothing to do on telephony!
-            break;
-        default:
-            CatLog.d(this, "Unsupported command");
-            return;
+            case SET_UP_MENU:
+                if (removeMenu(cmdMsg.getMenu())) {
+                    mMenuCmd = null;
+                } else {
+                    mMenuCmd = cmdMsg;
+                }
+                sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+                break;
+            case DISPLAY_TEXT:
+                // when application is not required to respond, send an immediate response.
+                if (!cmdMsg.geTextMessage().responseNeeded) {
+                    sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+                }
+                break;
+            case REFRESH:
+                // ME side only handles refresh commands which meant to remove IDLE
+                // MODE TEXT.
+                cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value();
+                break;
+            case SET_UP_IDLE_MODE_TEXT:
+                sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+                break;
+            case PROVIDE_LOCAL_INFORMATION:
+                sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+                return;
+            case LAUNCH_BROWSER:
+            case SELECT_ITEM:
+            case GET_INPUT:
+            case GET_INKEY:
+            case SEND_DTMF:
+            case SEND_SMS:
+            case SEND_SS:
+            case SEND_USSD:
+            case PLAY_TONE:
+            case SET_UP_CALL:
+                // nothing to do on telephony!
+                break;
+            default:
+                CatLog.d(this, "Unsupported command");
+                return;
         }
         mCurrntCmd = cmdMsg;
         Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
@@ -315,6 +314,11 @@
         }
         ByteArrayOutputStream buf = new ByteArrayOutputStream();
 
+        Input cmdInput = null;
+        if (mCurrntCmd != null) {
+            cmdInput = mCurrntCmd.geInput();
+        }
+
         // command details
         int tag = ComprehensionTlvTag.COMMAND_DETAILS.value();
         if (cmdDet.compRequired) {
@@ -327,7 +331,13 @@
         buf.write(cmdDet.commandQualifier);
 
         // device identities
-        tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value();
+        // According to TS102.223/TS31.111 section 6.8 Structure of
+        // TERMINAL RESPONSE, "For all SIMPLE-TLV objects with Min=N,
+        // the ME should set the CR(comprehension required) flag to
+        // comprehension not required.(CR=0)"
+        // Since DEVICE_IDENTITIES and DURATION TLVs have Min=N,
+        // the CR flag is not set.
+        tag = ComprehensionTlvTag.DEVICE_IDENTITIES.value();
         buf.write(tag);
         buf.write(0x02); // length
         buf.write(DEV_ID_TERMINAL); // source device id
@@ -348,6 +358,8 @@
         // Fill optional data for each corresponding command
         if (resp != null) {
             resp.format(buf);
+        } else {
+            encodeOptionalTags(cmdDet, resultCode, cmdInput, buf);
         }
 
         byte[] rawData = buf.toByteArray();
@@ -359,6 +371,52 @@
         mCmdIf.sendTerminalResponse(hexString, null);
     }
 
+    private void encodeOptionalTags(CommandDetails cmdDet,
+            ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) {
+        switch (AppInterface.CommandType.fromInt(cmdDet.typeOfCommand)) {
+            case GET_INKEY:
+                // ETSI TS 102 384,27.22.4.2.8.4.2.
+                // If it is a response for GET_INKEY command and the response timeout
+                // occured, then add DURATION TLV for variable timeout case.
+                if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) &&
+                    (cmdInput != null) && (cmdInput.duration != null)) {
+                    getInKeyResponse(buf, cmdInput);
+                }
+                break;
+            case PROVIDE_LOCAL_INFORMATION:
+                if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) &&
+                    (resultCode.value() == ResultCode.OK.value())) {
+                    getPliResponse(buf);
+                }
+                break;
+            default:
+                CatLog.d(this, "encodeOptionalTags() Unsupported Cmd:" + cmdDet.typeOfCommand);
+                break;
+        }
+    }
+
+    private void getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput) {
+        int tag = ComprehensionTlvTag.DURATION.value();
+
+        buf.write(tag);
+        buf.write(0x02); // length
+        buf.write(cmdInput.duration.timeUnit.SECOND.value()); // Time (Unit,Seconds)
+        buf.write(cmdInput.duration.timeInterval); // Time Duration
+    }
+
+    private void getPliResponse(ByteArrayOutputStream buf) {
+
+        // Locale Language Setting
+        String lang = SystemProperties.get("persist.sys.language");
+
+        if (lang != null) {
+            // tag
+            int tag = ComprehensionTlvTag.LANGUAGE.value();
+            buf.write(tag);
+            ResponseData.writeLength(buf, lang.length());
+            buf.write(lang.getBytes(), 0, lang.length());
+        }
+    }
 
     private void sendMenuSelection(int menuId, boolean helpRequired) {
 
diff --git a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index edb2dc8..12204a0 100644
--- a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -52,6 +52,9 @@
     static final int REFRESH_NAA_INIT                       = 0x03;
     static final int REFRESH_UICC_RESET                     = 0x04;
 
+    // Command Qualifier values for PLI command
+    static final int LANGUAGE_SETTING                       = 0x04;
+
     static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
             IccFileHandler fh) {
         if (sInstance != null) {
@@ -112,7 +115,10 @@
         AppInterface.CommandType cmdType = AppInterface.CommandType
                 .fromInt(cmdDet.typeOfCommand);
         if (cmdType == null) {
-            sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD);
+            // This PROACTIVE COMMAND is presently not handled. Hence set
+            // result code as BEYOND_TERMINAL_CAPABILITY in TR.
+            mCmdParams = new CommandParams(cmdDet);
+            sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY);
             return;
         }
 
@@ -155,10 +161,13 @@
              case PLAY_TONE:
                 cmdPending = processPlayTone(cmdDet, ctlvs);
                 break;
+             case PROVIDE_LOCAL_INFORMATION:
+                cmdPending = processProvideLocalInfo(cmdDet, ctlvs);
+                break;
             default:
                 // unsupported proactive commands
                 mCmdParams = new CommandParams(cmdDet);
-                sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD);
+                sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY);
                 return;
             }
         } catch (ResultException e) {
@@ -380,6 +389,12 @@
             iconId = ValueParser.retrieveIconId(ctlv);
         }
 
+        // parse duration
+        ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
+        if (ctlv != null) {
+            input.duration = ValueParser.retrieveDuration(ctlv);
+        }
+
         input.minLen = 1;
         input.maxLen = 1;
 
@@ -863,4 +878,20 @@
         }
         return false;
     }
+
+    private boolean processProvideLocalInfo(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)
+            throws ResultException {
+        CatLog.d(this, "process ProvideLocalInfo");
+        switch (cmdDet.commandQualifier) {
+            case LANGUAGE_SETTING:
+                CatLog.d(this, "PLI [LANGUAGE_SETTING]");
+                mCmdParams = new CommandParams(cmdDet);
+                break;
+            default:
+                CatLog.d(this, "PLI[" + cmdDet.commandQualifier + "] Command Not Supported");
+                mCmdParams = new CommandParams(cmdDet);
+                throw new ResultException(ResultCode.BEYOND_TERMINAL_CAPABILITY);
+        }
+        return false;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cat/Input.java b/telephony/java/com/android/internal/telephony/cat/Input.java
index 8bcaab9..13a5ad4 100644
--- a/telephony/java/com/android/internal/telephony/cat/Input.java
+++ b/telephony/java/com/android/internal/telephony/cat/Input.java
@@ -36,6 +36,7 @@
     public boolean echo;
     public boolean yesNo;
     public boolean helpAvailable;
+    public Duration duration;
 
     Input() {
         text = "";
@@ -49,6 +50,7 @@
         echo = false;
         yesNo = false;
         helpAvailable = false;
+        duration = null;
     }
 
     private Input(Parcel in) {
@@ -63,6 +65,7 @@
         echo = in.readInt() == 1 ? true : false;
         yesNo = in.readInt() == 1 ? true : false;
         helpAvailable = in.readInt() == 1 ? true : false;
+        duration = in.readParcelable(null);
     }
 
     public int describeContents() {
@@ -81,6 +84,7 @@
         dest.writeInt(echo ? 1 : 0);
         dest.writeInt(yesNo ? 1 : 0);
         dest.writeInt(helpAvailable ? 1 : 0);
+        dest.writeParcelable(duration, 0);
     }
 
     public static final Parcelable.Creator<Input> CREATOR = new Parcelable.Creator<Input>() {
diff --git a/telephony/java/com/android/internal/telephony/cat/ResponseData.java b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
index 84c08f8..677d66b 100644
--- a/telephony/java/com/android/internal/telephony/cat/ResponseData.java
+++ b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
@@ -28,6 +28,16 @@
      * the ByteArrayOutputStream object.
      */
     public abstract void format(ByteArrayOutputStream buf);
+
+    public static void writeLength(ByteArrayOutputStream buf, int length) {
+        // As per ETSI 102.220 Sec7.1.2, if the total length is greater
+        // than 0x7F, it should be coded in two bytes and the first byte
+        // should be 0x81.
+        if (length > 0x7F) {
+            buf.write(0x81);
+        }
+        buf.write(length);
+    }
 }
 
 class SelectItemResponseData extends ResponseData {
@@ -120,7 +130,7 @@
         }
 
         // length - one more for data coding scheme.
-        buf.write(data.length + 1);
+        writeLength(buf, data.length + 1);
 
         // data coding scheme
         if (mIsUcs2) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 5f093d4..69a7a57 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -972,7 +972,9 @@
     }
 
     public void getCallWaiting(Message onComplete) {
-        mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
+        //As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service
+        //class parameter in call waiting interrogation  to network
+        mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_NONE, onComplete);
     }
 
     public void setCallWaiting(boolean enable, Message onComplete) {
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/Wap230WspContentTypeTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/Wap230WspContentTypeTest.java
new file mode 100644
index 0000000..d31b294
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/Wap230WspContentTypeTest.java
@@ -0,0 +1,853 @@
+/*
+ * Copyright (C) 2010 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.android.internal.telephony;
+
+import com.android.internal.telephony.WspTypeDecoder;
+import com.android.internal.util.HexDump;
+
+import java.io.ByteArrayOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class Wap230WspContentTypeTest extends TestCase {
+
+    public static final Map<Integer, String> WELL_KNOWN_SHORT_MIME_TYPES
+            = new HashMap<Integer, String>();
+    public static final Map<Integer, String> WELL_KNOWN_LONG_MIME_TYPES
+            = new HashMap<Integer, String>();
+    public static final Map<Integer, String> WELL_KNOWN_PARAMETERS
+            = new HashMap<Integer, String>();
+
+    static {
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x00, "*/*");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x01, "text/*");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x02, "text/html");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x03, "text/plain");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x04, "text/x-hdml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x05, "text/x-ttml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x06, "text/x-vCalendar");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x07, "text/x-vCard");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x08, "text/vnd.wap.wml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x09, "text/vnd.wap.wmlscript");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x0A, "text/vnd.wap.wta-event");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x0B, "multipart/*");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x0C, "multipart/mixed");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x0D, "multipart/form-data");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x0E, "multipart/byterantes");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x0F, "multipart/alternative");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x10, "application/*");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x11, "application/java-vm");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x12, "application/x-www-form-urlencoded");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x13, "application/x-hdmlc");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x14, "application/vnd.wap.wmlc");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x15, "application/vnd.wap.wmlscriptc");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x16, "application/vnd.wap.wta-eventc");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x17, "application/vnd.wap.uaprof");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x18, "application/vnd.wap.wtls-ca-certificate");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x19, "application/vnd.wap.wtls-user-certificate");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x1A, "application/x-x509-ca-cert");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x1B, "application/x-x509-user-cert");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x1C, "image/*");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x1D, "image/gif");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x1E, "image/jpeg");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x1F, "image/tiff");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x20, "image/png");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x21, "image/vnd.wap.wbmp");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x22, "application/vnd.wap.multipart.*");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x23, "application/vnd.wap.multipart.mixed");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x24, "application/vnd.wap.multipart.form-data");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x25, "application/vnd.wap.multipart.byteranges");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x26, "application/vnd.wap.multipart.alternative");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x27, "application/xml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x28, "text/xml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x29, "application/vnd.wap.wbxml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x2A, "application/x-x968-cross-cert");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x2B, "application/x-x968-ca-cert");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x2C, "application/x-x968-user-cert");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x2D, "text/vnd.wap.si");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x2E, "application/vnd.wap.sic");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x2F, "text/vnd.wap.sl");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x30, "application/vnd.wap.slc");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x31, "text/vnd.wap.co");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x32, "application/vnd.wap.coc");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x33, "application/vnd.wap.multipart.related");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x34, "application/vnd.wap.sia");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x35, "text/vnd.wap.connectivity-xml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x36, "application/vnd.wap.connectivity-wbxml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x37, "application/pkcs7-mime");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x38, "application/vnd.wap.hashed-certificate");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x39, "application/vnd.wap.signed-certificate");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x3A, "application/vnd.wap.cert-response");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x3B, "application/xhtml+xml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x3C, "application/wml+xml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x3D, "text/css");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x3E, "application/vnd.wap.mms-message");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x3F, "application/vnd.wap.rollover-certificate");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x40, "application/vnd.wap.locc+wbxml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x41, "application/vnd.wap.loc+xml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x42, "application/vnd.syncml.dm+wbxml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x43, "application/vnd.syncml.dm+xml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x44, "application/vnd.syncml.notification");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x45, "application/vnd.wap.xhtml+xml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x46, "application/vnd.wv.csp.cir");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x47, "application/vnd.oma.dd+xml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x48, "application/vnd.oma.drm.message");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x49, "application/vnd.oma.drm.content");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x4A, "application/vnd.oma.drm.rights+xml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x4B, "application/vnd.oma.drm.rights+wbxml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x4C, "application/vnd.wv.csp+xml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x4D, "application/vnd.wv.csp+wbxml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x4E, "application/vnd.syncml.ds.notification");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x4F, "audio/*");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x50, "video/*");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x51, "application/vnd.oma.dd2+xml");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x52, "application/mikey");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x53, "application/vnd.oma.dcd");
+        WELL_KNOWN_SHORT_MIME_TYPES.put(0x54, "application/vnd.oma.dcdc");
+
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0201, "application/vnd.uplanet.cacheop-wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0202, "application/vnd.uplanet.signal");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0203, "application/vnd.uplanet.alert-wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0204, "application/vnd.uplanet.list-wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0205, "application/vnd.uplanet.listcmd-wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0206, "application/vnd.uplanet.channel-wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0207, "application/vnd.uplanet.provisioning-status-uri");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0208, "x-wap.multipart/vnd.uplanet.header-set");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0209, "application/vnd.uplanet.bearer-choice-wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x020A, "application/vnd.phonecom.mmc-wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x020B, "application/vnd.nokia.syncset+wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x020C, "image/x-up-wpng");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0300, "application/iota.mmc-wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0301, "application/iota.mmc-xml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0302, "application/vnd.syncml+xml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0303, "application/vnd.syncml+wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0304, "text/vnd.wap.emn+xml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0305, "text/calendar");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0306, "application/vnd.omads-email+xml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0307, "application/vnd.omads-file+xml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0308, "application/vnd.omads-folder+xml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0309, "text/directory;profile=vCard");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x030A, "application/vnd.wap.emn+wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x030B, "application/vnd.nokia.ipdc-purchase-response");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x030C, "application/vnd.motorola.screen3+xml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x030D, "application/vnd.motorola.screen3+gzip");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x030E, "application/vnd.cmcc.setting+wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x030F, "application/vnd.cmcc.bombing+wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0310, "application/vnd.docomo.pf");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0311, "application/vnd.docomo.ub");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0312, "application/vnd.omaloc-supl-init");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0313, "application/vnd.oma.group-usage-list+xml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0314, "application/oma-directory+xml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0315, "application/vnd.docomo.pf2");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0316, "application/vnd.oma.drm.roap-trigger+wbxml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0317, "application/vnd.sbm.mid2");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0318, "application/vnd.wmf.bootstrap");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x0319, "application/vnc.cmcc.dcd+xml");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x031A, "application/vnd.sbm.cid");
+        WELL_KNOWN_LONG_MIME_TYPES.put(0x031B, "application/vnd.oma.bcast.provisioningtrigger");
+
+        WELL_KNOWN_PARAMETERS.put(0x00, "Q");
+        WELL_KNOWN_PARAMETERS.put(0x01, "Charset");
+        WELL_KNOWN_PARAMETERS.put(0x02, "Level");
+        WELL_KNOWN_PARAMETERS.put(0x03, "Type");
+        WELL_KNOWN_PARAMETERS.put(0x07, "Differences");
+        WELL_KNOWN_PARAMETERS.put(0x08, "Padding");
+        WELL_KNOWN_PARAMETERS.put(0x09, "Type");
+        WELL_KNOWN_PARAMETERS.put(0x0E, "Max-Age");
+        WELL_KNOWN_PARAMETERS.put(0x10, "Secure");
+        WELL_KNOWN_PARAMETERS.put(0x11, "SEC");
+        WELL_KNOWN_PARAMETERS.put(0x12, "MAC");
+        WELL_KNOWN_PARAMETERS.put(0x13, "Creation-date");
+        WELL_KNOWN_PARAMETERS.put(0x14, "Modification-date");
+        WELL_KNOWN_PARAMETERS.put(0x15, "Read-date");
+        WELL_KNOWN_PARAMETERS.put(0x16, "Size");
+        WELL_KNOWN_PARAMETERS.put(0x17, "Name");
+        WELL_KNOWN_PARAMETERS.put(0x18, "Filename");
+        WELL_KNOWN_PARAMETERS.put(0x19, "Start");
+        WELL_KNOWN_PARAMETERS.put(0x1A, "Start-info");
+        WELL_KNOWN_PARAMETERS.put(0x1B, "Comment");
+        WELL_KNOWN_PARAMETERS.put(0x1C, "Domain");
+        WELL_KNOWN_PARAMETERS.put(0x1D, "Path");
+
+    }
+
+    final int WSP_DEFINED_SHORT_MIME_TYPE_COUNT = 85;
+    final int WSP_DEFINED_LONG_MIME_TYPE_COUNT = 85;
+
+    private static final byte WSP_STRING_TERMINATOR = 0x00;
+    private static final byte WSP_SHORT_INTEGER_MASK = (byte) 0x80;
+    private static final byte WSP_LENGTH_QUOTE = 0x1F;
+    private static final byte WSP_QUOTE = 0x22;
+
+    private static final short LONG_MIME_TYPE_OMA_DIRECTORY_XML = 0x0314;
+    private static final short LONG_MIME_TYPE_UNASSIGNED = 0x052C;
+
+    private static final byte SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE = 0x3F;
+    private static final byte SHORT_MIME_TYPE_UNASSIGNED = 0x60;
+
+    private static final String STRING_MIME_TYPE_ROLLOVER_CERTIFICATE
+            = "application/vnd.wap.rollover-certificate";
+
+    private static final byte TYPED_PARAM_Q = 0x00;
+    private static final byte TYPED_PARAM_DOMAIN = 0x1C;
+    private static final byte PARAM_UNASSIGNED = 0x42;
+    private static final byte PARAM_NO_VALUE = 0x00;
+    private static final byte TYPED_PARAM_SEC = 0x11;
+    private static final byte TYPED_PARAM_MAC = 0x12;
+
+    public void testHasExpectedNumberOfShortMimeTypes() {
+        assertEquals(WSP_DEFINED_SHORT_MIME_TYPE_COUNT, WELL_KNOWN_SHORT_MIME_TYPES.size());
+    }
+
+    public void testHasExpectedNumberOfLongMimeTypes() {
+        assertEquals(WSP_DEFINED_LONG_MIME_TYPE_COUNT, WELL_KNOWN_LONG_MIME_TYPES.size());
+    }
+
+    public void testWellKnownShortIntegerMimeTypeValues() {
+
+        for (int value : Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.keySet()) {
+            WspTypeDecoder unit = new WspTypeDecoder(
+                    HexDump.toByteArray((byte) (value | WSP_SHORT_INTEGER_MASK)));
+            assertTrue(unit.decodeContentType(0));
+            String mimeType = unit.getValueString();
+            int wellKnownValue = (int) unit.getValue32();
+            assertEquals(Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.get(value), mimeType);
+            assertEquals(value, wellKnownValue);
+            assertEquals(1, unit.getDecodedDataLength());
+        }
+    }
+
+    public void testWellKnownLongIntegerMimeTypeValues() {
+        byte headerLength = 3;
+        byte typeLength = 2;
+        for (int value : Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.keySet()) {
+            byte[] data = new byte[10];
+            data[0] = headerLength;
+            data[1] = typeLength;
+            data[2] = (byte) (value >> 8);
+            data[3] = (byte) (value & 0xFF);
+            WspTypeDecoder unit = new WspTypeDecoder(data);
+            assertTrue(unit.decodeContentType(0));
+            String mimeType = unit.getValueString();
+            int wellKnownValue = (int) unit.getValue32();
+            assertEquals(Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.get(value), mimeType);
+            assertEquals(value, wellKnownValue);
+            assertEquals(4, unit.getDecodedDataLength());
+        }
+    }
+
+    public void testDecodeReturnsFalse_WhenOnlyAZeroBytePresent() {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x00);
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertFalse(unit.decodeContentType(0));
+    }
+
+    public void testConstrainedMediaExtensionMedia() throws Exception {
+
+        String testType = "application/wibble";
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(testType.getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+        String mimeType = unit.getValueString();
+        assertEquals(testType, mimeType);
+        assertEquals(-1, unit.getValue32());
+        assertEquals(19, unit.getDecodedDataLength());
+    }
+
+    public void testGeneralFormShortLengthExtensionMedia() throws Exception {
+
+        String testType = "12345678901234567890123456789";
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(testType.length() + 1);
+        out.write(testType.getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+        assertEquals(testType, mimeType);
+        assertEquals(-1, unit.getValue32());
+        assertEquals(31, unit.getDecodedDataLength());
+    }
+
+    public void testGeneralFormShortLengthWellKnownShortInteger()  {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x01);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+        assertEquals(2, unit.getDecodedDataLength());
+
+    }
+
+    public void testGeneralFormShortLengthWellKnownShortIntegerWithUnknownValue()  {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x01);
+        out.write(SHORT_MIME_TYPE_UNASSIGNED | WSP_SHORT_INTEGER_MASK);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+        assertNull(mimeType);
+        assertEquals(SHORT_MIME_TYPE_UNASSIGNED, unit.getValue32());
+        assertEquals(2, unit.getDecodedDataLength());
+
+    }
+
+    public void testGeneralFormShortLengthWellKnownLongInteger()  {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        out.write(0x03); // header length
+        out.write(0x02); // type length (2 octets)
+        out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML >> 8);
+        out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML & 0xFF);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals("application/oma-directory+xml", mimeType);
+        assertEquals(LONG_MIME_TYPE_OMA_DIRECTORY_XML, unit.getValue32());
+        assertEquals(4, unit.getDecodedDataLength());
+    }
+
+    public void testGeneralFormShortLengthWellKnownLongIntegerWithUnknownValue()  {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        out.write(0x03); // Value-length, short-length
+        out.write(0x02); // long-integer length (2 octets)
+        out.write(LONG_MIME_TYPE_UNASSIGNED >> 8);
+        out.write(LONG_MIME_TYPE_UNASSIGNED & 0xFF);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertNull(mimeType);
+        assertEquals(LONG_MIME_TYPE_UNASSIGNED, unit.getValue32());
+        assertEquals(4, unit.getDecodedDataLength());
+
+    }
+
+    public void testGeneralFormLengthQuoteWellKnownShortInteger()  {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        out.write(WSP_LENGTH_QUOTE);
+        out.write(0x01); // Length as UINTVAR
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+        assertEquals(3, unit.getDecodedDataLength());
+
+    }
+
+    public void testGeneralFormLengthQuoteWellKnownShortIntegerWithUnknownValue()  {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        out.write(WSP_LENGTH_QUOTE);
+        out.write(0x01); // Length as UINTVAR
+        out.write(SHORT_MIME_TYPE_UNASSIGNED | WSP_SHORT_INTEGER_MASK);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+        assertNull(mimeType);
+        assertEquals(SHORT_MIME_TYPE_UNASSIGNED, unit.getValue32());
+        assertEquals(3, unit.getDecodedDataLength());
+    }
+
+    public void testGeneralFormLengthQuoteWellKnownLongInteger()  {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        out.write(WSP_LENGTH_QUOTE);
+        out.write(0x03); // Length as UINTVAR
+        out.write(0x02); // long-integer length (2 octets)
+        out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML >> 8);
+        out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML & 0xFF);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals("application/oma-directory+xml", mimeType);
+        assertEquals(LONG_MIME_TYPE_OMA_DIRECTORY_XML, unit.getValue32());
+        assertEquals(5, unit.getDecodedDataLength());
+
+    }
+
+    public void testGeneralFormLengthQuoteWellKnownLongIntegerWithUnknownValue()  {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        out.write(WSP_LENGTH_QUOTE);
+        out.write(0x03); // Length as UINTVAR
+        out.write(0x02); // long-integer length (2 octets)
+        out.write(LONG_MIME_TYPE_UNASSIGNED >> 8);
+        out.write(LONG_MIME_TYPE_UNASSIGNED & 0xFF);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertNull(mimeType);
+        assertEquals(LONG_MIME_TYPE_UNASSIGNED, unit.getValue32());
+        assertEquals(5, unit.getDecodedDataLength());
+
+    }
+
+    public void testGeneralFormLengthQuoteExtensionMedia() throws Exception {
+
+        String testType = "application/wibble";
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        out.write(WSP_LENGTH_QUOTE);
+        out.write(testType.length() + 1); // Length as UINTVAR
+
+        out.write(testType.getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals(testType, mimeType);
+        assertEquals(-1, unit.getValue32());
+        assertEquals(21, unit.getDecodedDataLength());
+
+    }
+
+    public void testGeneralFormLengthQuoteExtensionMediaWithNiceLongMimeType() throws Exception {
+
+        String testType =
+                "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
+                +"01234567890123456789012345678901234567890123456789012345678901234567890123456789";
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        out.write(WSP_LENGTH_QUOTE);
+        out.write(0x81); // Length as UINTVAR (161 decimal, 0xA1), 2 bytes
+        out.write(0x21);
+
+        out.write(testType.getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals(testType, mimeType);
+        assertEquals(-1, unit.getValue32());
+        assertEquals(164, unit.getDecodedDataLength());
+
+    }
+
+    public void testConstrainedMediaExtensionMediaWithSpace() throws Exception {
+
+        String testType = " application/wibble";
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(testType.getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals(testType, mimeType);
+        assertEquals(-1, unit.getValue32());
+        assertEquals(20, unit.getDecodedDataLength());
+
+    }
+
+    public void testTypedParamWellKnownShortIntegerNoValue()  {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x03); // Value-length, short-length
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write(TYPED_PARAM_DOMAIN | WSP_SHORT_INTEGER_MASK);
+        out.write(PARAM_NO_VALUE);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+        assertEquals(4, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals(null, params.get("Domain"));
+
+    }
+
+    public void testTypedParamWellKnownShortIntegerTokenText() throws Exception {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x14); // Value-length, short-length
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write(TYPED_PARAM_DOMAIN | WSP_SHORT_INTEGER_MASK);
+        out.write("wdstechnology.com".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+        assertEquals(out.toByteArray().length, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals("wdstechnology.com", params.get("Domain"));
+
+    }
+
+    public void testTypedParamWellKnownLongIntegerTokenText() throws Exception {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x15);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write(0x01);
+        out.write(TYPED_PARAM_DOMAIN);
+        out.write("wdstechnology.com".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+        assertEquals(22, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals("wdstechnology.com", params.get("Domain"));
+
+    }
+
+    public void testTypedParamWellKnownShortIntegerQuotedText() throws Exception {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x15);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write(TYPED_PARAM_DOMAIN | WSP_SHORT_INTEGER_MASK);
+        out.write(WSP_QUOTE);
+        out.write("wdstechnology.com".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(0x3F, unit.getValue32());
+        assertEquals(22, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals("wdstechnology.com", params.get("Domain"));
+
+    }
+
+    public void testTypedParamWellKnownShortIntegerCompactIntegerValue()  {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x3);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write(TYPED_PARAM_SEC | WSP_SHORT_INTEGER_MASK);
+        out.write(0x01 | WSP_SHORT_INTEGER_MASK);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(0x3F, unit.getValue32());
+        assertEquals(4, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals("1", params.get("SEC"));
+
+    }
+
+    public void testTypedParamWellKnownShortIntegerMultipleParameters() throws Exception {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x0B);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write(TYPED_PARAM_SEC | WSP_SHORT_INTEGER_MASK);
+        out.write(0x01 | WSP_SHORT_INTEGER_MASK);
+        out.write(TYPED_PARAM_MAC | WSP_SHORT_INTEGER_MASK);
+        out.write(WSP_QUOTE);
+        out.write("imapc".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+        assertEquals(12, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals("1", params.get("SEC"));
+        assertEquals("imapc", params.get("MAC"));
+    }
+
+    public void testUntypedParamIntegerValueShortInteger() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x0A);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write("MYPARAM".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR); // EOS
+        out.write(0x45 | WSP_SHORT_INTEGER_MASK);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+        assertEquals(11, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals("69", params.get("MYPARAM"));
+    }
+
+    public void testUntypedParamIntegerValueLongInteger() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x0C);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write("MYPARAM".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+        out.write(0x02); // Short Length
+        out.write(0x42); // Long Integer byte 1
+        out.write(0x69); // Long Integer byte 2
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(0x3F, unit.getValue32());
+        assertEquals(13, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals("17001", params.get("MYPARAM"));
+    }
+
+    public void testUntypedParamTextNoValue() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x0A);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write("MYPARAM".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+        out.write(PARAM_NO_VALUE);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+        assertEquals(11, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals(null, params.get("MYPARAM"));
+
+    }
+
+    public void testUntypedParamTextTokenText() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x11);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write("MYPARAM".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+        out.write("myvalue".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+        assertEquals(18, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals("myvalue", params.get("MYPARAM"));
+    }
+
+    public void testUntypedParamTextQuotedString() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x11);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write("MYPARAM".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+        out.write(WSP_QUOTE);
+        out.write("myvalue".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+        assertEquals(19, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals("myvalue", params.get("MYPARAM"));
+
+    }
+
+    public void testDecodesReturnsFalse_ForParamWithMissingValue() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x09);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write("MYPARAM".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertFalse(unit.decodeContentType(0));
+    }
+
+    public void testTypedParamTextQValue()  {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x04);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write(TYPED_PARAM_Q);
+        out.write(0x83); // Q value byte 1
+        out.write(0x31); // Q value byte 2
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(0x3F, unit.getValue32());
+        assertEquals(5, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals("433", params.get("Q"));
+
+    }
+
+    public void testTypedParamUnassignedWellKnownShortIntegerTokenText() throws Exception {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x14);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write(PARAM_UNASSIGNED | WSP_SHORT_INTEGER_MASK);
+        out.write("wdstechnology.com".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+        assertEquals(21, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals("wdstechnology.com", params.get("unassigned/0x42"));
+
+    }
+
+    public void testTypedParamUnassignedWellKnownLongIntegerTokenText() throws Exception {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x15);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write(0x01); // Short-length of well-known parameter token
+        out.write(PARAM_UNASSIGNED);
+        out.write("wdstechnology.com".getBytes("US-ASCII"));
+        out.write(WSP_STRING_TERMINATOR);
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertTrue(unit.decodeContentType(0));
+
+        String mimeType = unit.getValueString();
+
+        assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+        assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+        assertEquals(22, unit.getDecodedDataLength());
+
+        Map<String, String> params = unit.getContentParameters();
+        assertEquals("wdstechnology.com", params.get("unassigned/0x42"));
+    }
+
+    public void testDecodesReturnsFalse_WhenParamValueNotTerminated() throws Exception {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(0x15);
+        out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+        out.write(0x01);
+        out.write(PARAM_UNASSIGNED);
+        out.write("wdstechnology.com".getBytes("US-ASCII"));
+
+        WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+        assertFalse(unit.decodeContentType(0));
+    }
+}
\ No newline at end of file
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index b339a2c..094b7db 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -41,7 +41,7 @@
 	libpng
 
 ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -lrt
+LOCAL_LDLIBS += -lrt -lpthread
 endif
 
 # Statically link libz for MinGW (Win SDK under Linux),
diff --git a/tools/localize/Android.mk b/tools/localize/Android.mk
index ab79f8d..f284e86 100644
--- a/tools/localize/Android.mk
+++ b/tools/localize/Android.mk
@@ -34,7 +34,7 @@
 	libcutils
     
 ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -lrt
+LOCAL_LDLIBS += -lrt -lpthread
 endif