am b732b7b5: am ce57a7f3: am 6504490c: am dff6b8e7: Merge "Add --non-constant-id to aapt."

* commit 'b732b7b5e8192501360edc15fb8c6399d11fb97d':
  GpsLocationProvider: Clean up HAL initialization/cleanup sequence
  Fixed GSM encoded network initiated position request
  Ensuring thread-safe usage of DateFormat.
  Fixing infinite loop for zero duration.
  Fix for an infinite loop while scrolling lists.
  WAPPushManager, WAP Push over SMS message handler
  Add --non-constant-id to aapt.
diff --git a/Android.mk b/Android.mk
index 08ee65e..6e41f96 100644
--- a/Android.mk
+++ b/Android.mk
@@ -188,6 +188,7 @@
 	telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
 	telephony/java/com/android/internal/telephony/IIccPhoneBook.aidl \
 	telephony/java/com/android/internal/telephony/ISms.aidl \
+	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
 	wifi/java/android/net/wifi/IWifiManager.aidl \
 	telephony/java/com/android/internal/telephony/IExtendedNetworkService.aidl \
 	vpn/java/android/net/vpn/IVpnService.aidl \
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index a95dad7..353b628 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -642,14 +642,18 @@
 
     private static void initFormatStrings() {
         synchronized (sLock) {
-            Resources r = Resources.getSystem();
-            Configuration cfg = r.getConfiguration();
-            if (sLastConfig == null || !sLastConfig.equals(cfg)) {
-                sLastConfig = cfg;
-                sStatusTimeFormat = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT);
-                sElapsedFormatMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_mm_ss);
-                sElapsedFormatHMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_h_mm_ss);
-            }
+            initFormatStringsLocked();
+        }
+    }
+
+    private static void initFormatStringsLocked() {
+        Resources r = Resources.getSystem();
+        Configuration cfg = r.getConfiguration();
+        if (sLastConfig == null || !sLastConfig.equals(cfg)) {
+            sLastConfig = cfg;
+            sStatusTimeFormat = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT);
+            sElapsedFormatMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_mm_ss);
+            sElapsedFormatHMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_h_mm_ss);
         }
     }
 
@@ -659,8 +663,10 @@
      * @hide
      */
     public static final CharSequence timeString(long millis) {
-        initFormatStrings();
-        return sStatusTimeFormat.format(millis);
+        synchronized (sLock) {
+            initFormatStringsLocked();
+            return sStatusTimeFormat.format(millis);
+        }
     }
 
     /**
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index d7b0dc1..9042505 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -191,7 +191,7 @@
             int pos = 0;
             fieldLen -= 1;
             while (pos < fieldLen) {
-                formatStr[pos] = ' ';
+                formatStr[pos++] = ' ';
             }
             formatStr[pos] = '0';
             return pos+1;
diff --git a/core/java/android/widget/EdgeGlow.java b/core/java/android/widget/EdgeGlow.java
index 2a0e849..c2cb0a0 100644
--- a/core/java/android/widget/EdgeGlow.java
+++ b/core/java/android/widget/EdgeGlow.java
@@ -339,6 +339,7 @@
                     mEdgeScaleY = mEdgeScaleYStart +
                         (mEdgeScaleYFinish - mEdgeScaleYStart) *
                             interp * factor;
+                    mState = STATE_RECEDE;
                     break;
                 case STATE_RECEDE:
                     mState = STATE_IDLE;
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index d539833..ffc3346 100755
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -29,6 +29,7 @@
 import android.util.Log;
 
 import com.android.internal.R;
+import com.android.internal.telephony.GsmAlphabet;
 
 /**
  * A GPS Network-initiated Handler class used by LocationManager.
@@ -288,58 +289,32 @@
      */
     static String decodeGSMPackedString(byte[] input)
     {
-        final char CHAR_CR = 0x0D;
-        int nStridx = 0;
-        int nPckidx = 0;
-        int num_bytes = input.length;
-        int cPrev = 0;
-        int cCurr = 0;
-        byte nShift;
-        byte nextChar;
-        byte[] stringBuf = new byte[input.length * 2];
-        String result = "";
+        final char PADDING_CHAR = 0x00;
+        int lengthBytes = input.length;
+        int lengthSeptets = (lengthBytes * 8) / 7;
+        String decoded;
 
-        while(nPckidx < num_bytes)
-        {
-            nShift = (byte) (nStridx & 0x07);
-            cCurr = input[nPckidx++];
-            if (cCurr < 0) cCurr += 256;
-
-            /* A 7-bit character can be split at the most between two bytes of packed
-             ** data.
-             */
-            nextChar = (byte) (( (cCurr << nShift) | (cPrev >> (8-nShift)) ) & 0x7F);
-            stringBuf[nStridx++] = nextChar;
-
-            /* Special case where the whole of the next 7-bit character fits inside
-             ** the current byte of packed data.
-             */
-            if(nShift == 6)
-            {
-                /* If the next 7-bit character is a CR (0x0D) and it is the last
-                 ** character, then it indicates a padding character. Drop it.
-                 */
-                if (nPckidx == num_bytes || (cCurr >> 1) == CHAR_CR)
-                {
-                    break;
+        /* Special case where the last 7 bits in the last byte could hold a valid
+         * 7-bit character or a padding character. Drop the last 7-bit character
+         * if it is a padding character.
+         */
+        if (lengthBytes % 7 == 0) {
+            if (lengthBytes > 0) {
+                if ((input[lengthBytes - 1] >> 1) == PADDING_CHAR) {
+                    lengthSeptets = lengthSeptets - 1;
                 }
-
-                nextChar = (byte) (cCurr >> 1);
-                stringBuf[nStridx++] = nextChar;
             }
-
-            cPrev = cCurr;
         }
 
-        try {
-            result = new String(stringBuf, 0, nStridx, "US-ASCII");
-        }
-        catch (UnsupportedEncodingException e)
-        {
-            Log.e(TAG, e.getMessage());
+        decoded = GsmAlphabet.gsm7BitPackedToString(input, 0, lengthSeptets);
+
+        // Return "" if decoding of GSM packed string fails
+        if (null == decoded) {
+            Log.e(TAG, "Decoding of GSM packed string failed");
+            decoded = "";
         }
 
-        return result;
+        return decoded;
     }
 
     static String decodeUTF8String(byte[] input)
diff --git a/packages/WAPPushManager/Android.mk b/packages/WAPPushManager/Android.mk
new file mode 100644
index 0000000..c1d8f4b
--- /dev/null
+++ b/packages/WAPPushManager/Android.mk
@@ -0,0 +1,20 @@
+# Copyright 2007-2008 The Android Open Source Project
+
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := WAPPushManager
+
+LOCAL_STATIC_JAVA_LIBRARIES += android-common
+
+LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags
+
+include $(BUILD_PACKAGE)
+
+# This finds and builds the test apk as well, so a single make does both.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/WAPPushManager/AndroidManifest.xml b/packages/WAPPushManager/AndroidManifest.xml
new file mode 100644
index 0000000..89e9d6a
--- /dev/null
+++ b/packages/WAPPushManager/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.smspush">
+
+    <permission android:name="com.android.smspush.WAPPUSH_MANAGER_BIND"
+        android:protectionLevel="signatureOrSystem" />
+
+    <original-package android:name="com.android.smspush" />
+    <application>
+        <service android:name=".WapPushManager"
+            android:permission="com.android.smspush.WAPPUSH_MANAGER_BIND"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.internal.telephony.IWapPushManager"></action>
+            </intent-filter>
+        </service>
+    </application>
+
+
+</manifest>
diff --git a/packages/WAPPushManager/CleanSpec.mk b/packages/WAPPushManager/CleanSpec.mk
new file mode 100644
index 0000000..b84e1b6
--- /dev/null
+++ b/packages/WAPPushManager/CleanSpec.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/packages/WAPPushManager/MODULE_LICENSE_APACHE2 b/packages/WAPPushManager/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/WAPPushManager/MODULE_LICENSE_APACHE2
diff --git a/packages/WAPPushManager/NOTICE b/packages/WAPPushManager/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/packages/WAPPushManager/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/packages/WAPPushManager/proguard.flags b/packages/WAPPushManager/proguard.flags
new file mode 100644
index 0000000..d09887b
--- /dev/null
+++ b/packages/WAPPushManager/proguard.flags
@@ -0,0 +1,18 @@
+
+#apply method is dynamically referenced by the reflection class.
+-keep class android.app.ContextImpl$SharedPreferencesImpl$EditorImpl {
+    void apply();
+}
+-keep class android.content.SharedPreferences$Editor {
+    void apply();
+}
+
+#WapPushManager is referenced only by AndroidManifest.xml
+-keep class com.android.smspush.WapPushManager {
+#CTS module method
+    public boolean isDataExist(java.lang.String, java.lang.String,
+            java.lang.String, java.lang.String);
+    public boolean verifyData(java.lang.String, java.lang.String,
+            java.lang.String, java.lang.String, int, boolean, boolean);
+}
+
diff --git a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
new file mode 100644
index 0000000..96e0377
--- /dev/null
+++ b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
@@ -0,0 +1,424 @@
+/*
+ * 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.smspush;
+
+import android.app.Service;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.telephony.WapPushManagerParams;
+
+/**
+ * The WapPushManager service is implemented to process incoming
+ * WAP Push messages and to maintain the Receiver Application/Application
+ * ID mapping. The WapPushManager runs as a system service, and only the
+ * WapPushManager can update the WAP Push message and Receiver Application
+ * mapping (Application ID Table). When the device receives an SMS WAP Push
+ * message, the WapPushManager looks up the Receiver Application name in
+ * Application ID Table. If an application is found, the application is
+ * launched using its full component name instead of broadcasting an implicit
+ * Intent. If a Receiver Application is not found in the Application ID
+ * Table or the WapPushManager returns a process-further value, the
+ * telephony stack will process the message using existing message processing
+ * flow, and broadcast an implicit Intent.
+ */
+public class WapPushManager extends Service {
+
+    private static final String LOG_TAG = "WAP PUSH";
+    private static final String DATABASE_NAME = "wappush.db";
+    private static final String APPID_TABLE_NAME = "appid_tbl";
+
+    /**
+     * Version number must be incremented when table structure is changed.
+     */
+    private static final int WAP_PUSH_MANAGER_VERSION = 1;
+    private static final boolean DEBUG_SQL = false;
+    private static final boolean LOCAL_LOGV = false;
+
+    /**
+     * Inner class that deals with application ID table
+     */
+    private class WapPushManDBHelper extends SQLiteOpenHelper {
+        WapPushManDBHelper(Context context) {
+            super(context, DATABASE_NAME, null, WAP_PUSH_MANAGER_VERSION);
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "helper instance created.");
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "db onCreate.");
+            String sql = "CREATE TABLE " + APPID_TABLE_NAME + " ("
+                    + "id INTEGER PRIMARY KEY, "
+                    + "x_wap_application TEXT, "
+                    + "content_type TEXT, "
+                    + "package_name TEXT, "
+                    + "class_name TEXT, "
+                    + "app_type INTEGER, "
+                    + "need_signature INTEGER, "
+                    + "further_processing INTEGER, "
+                    + "install_order INTEGER "
+                    + ")";
+
+            if (DEBUG_SQL) Log.v(LOG_TAG, "sql: " + sql);
+            db.execSQL(sql);
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db,
+                    int oldVersion, int newVersion) {
+            // TODO: when table structure is changed, need to dump and restore data.
+            /*
+              db.execSQL(
+              "drop table if exists "+APPID_TABLE_NAME);
+              onCreate(db);
+            */
+            Log.w(LOG_TAG, "onUpgrade is not implemented yet. do nothing.");
+        }
+
+        protected class queryData {
+            public String packageName;
+            public String className;
+            int appType;
+            int needSignature;
+            int furtherProcessing;
+            int installOrder;
+        }
+
+        /**
+         * Query the latest receiver application info with supplied application ID and
+         * content type.
+         * @param app_id    application ID to look up
+         * @param content_type    content type to look up
+         */
+        protected queryData queryLastApp(SQLiteDatabase db,
+                String app_id, String content_type) {
+            String sql = "select install_order, package_name, class_name, "
+                    + " app_type, need_signature, further_processing"
+                    + " from " + APPID_TABLE_NAME
+                    + " where x_wap_application=\'" + app_id + "\'"
+                    + " and content_type=\'" + content_type + "\'"
+                    + " order by install_order desc";
+            if (DEBUG_SQL) Log.v(LOG_TAG, "sql: " + sql);
+            Cursor cur = db.rawQuery(sql, null);
+            queryData ret = null;
+
+            if (cur.moveToNext()) {
+                ret = new queryData();
+                ret.installOrder = cur.getInt(cur.getColumnIndex("install_order"));
+                ret.packageName = cur.getString(cur.getColumnIndex("package_name"));
+                ret.className = cur.getString(cur.getColumnIndex("class_name"));
+                ret.appType = cur.getInt(cur.getColumnIndex("app_type"));
+                ret.needSignature = cur.getInt(cur.getColumnIndex("need_signature"));
+                ret.furtherProcessing = cur.getInt(cur.getColumnIndex("further_processing"));
+            }
+            cur.close();
+            return ret;
+        }
+
+    }
+
+    /**
+     * The exported API implementations class
+     */
+    private class IWapPushManagerStub extends IWapPushManager.Stub {
+        public Context mContext;
+
+        public IWapPushManagerStub() {
+
+        }
+
+        /**
+         * Compare the package signature with WapPushManager package
+         */
+        protected boolean signatureCheck(String package_name) {
+            PackageManager pm = mContext.getPackageManager();
+            int match = pm.checkSignatures(mContext.getPackageName(), package_name);
+
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "compare signature " + mContext.getPackageName()
+                    + " and " +  package_name + ", match=" + match);
+
+            return match == PackageManager.SIGNATURE_MATCH;
+        }
+
+        /**
+         * Returns the status value of the message processing.
+         * The message will be processed as follows:
+         * 1.Look up Application ID Table with x-wap-application-id + content type
+         * 2.Check the signature of package name that is found in the
+         *   Application ID Table by using PackageManager.checkSignature
+         * 3.Trigger the Application
+         * 4.Returns the process status value.
+         */
+        public int processMessage(String app_id, String content_type, Intent intent)
+            throws RemoteException {
+            Log.d(LOG_TAG, "wpman processMsg " + app_id + ":" + content_type);
+
+            WapPushManDBHelper dbh = getDatabase(mContext);
+            SQLiteDatabase db = dbh.getReadableDatabase();
+            WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, app_id, content_type);
+            db.close();
+
+            if (lastapp == null) {
+                Log.w(LOG_TAG, "no receiver app found for " + app_id + ":" + content_type);
+                return WapPushManagerParams.APP_QUERY_FAILED;
+            }
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "starting " + lastapp.packageName
+                    + "/" + lastapp.className);
+
+            if (lastapp.needSignature != 0) {
+                if (!signatureCheck(lastapp.packageName)) {
+                    return WapPushManagerParams.SIGNATURE_NO_MATCH;
+                }
+            }
+
+            if (lastapp.appType == WapPushManagerParams.APP_TYPE_ACTIVITY) {
+                //Intent intent = new Intent(Intent.ACTION_MAIN);
+                intent.setClassName(lastapp.packageName, lastapp.className);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+                try {
+                    mContext.startActivity(intent);
+                } catch (ActivityNotFoundException e) {
+                    Log.w(LOG_TAG, "invalid name " +
+                            lastapp.packageName + "/" + lastapp.className);
+                    return WapPushManagerParams.INVALID_RECEIVER_NAME;
+                }
+            } else {
+                intent.setClassName(mContext, lastapp.className);
+                intent.setComponent(new ComponentName(lastapp.packageName,
+                        lastapp.className));
+                if (mContext.startService(intent) == null) {
+                    Log.w(LOG_TAG, "invalid name " +
+                            lastapp.packageName + "/" + lastapp.className);
+                    return WapPushManagerParams.INVALID_RECEIVER_NAME;
+                }
+            }
+
+            return WapPushManagerParams.MESSAGE_HANDLED
+                    | (lastapp.furtherProcessing == 1 ?
+                            WapPushManagerParams.FURTHER_PROCESSING : 0);
+        }
+
+        protected boolean appTypeCheck(int app_type) {
+            if (app_type == WapPushManagerParams.APP_TYPE_ACTIVITY ||
+                    app_type == WapPushManagerParams.APP_TYPE_SERVICE) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        /**
+         * Returns true if adding the package succeeded.
+         */
+        public boolean addPackage(String x_app_id, String content_type,
+                String package_name, String class_name,
+                int app_type, boolean need_signature, boolean further_processing) {
+            WapPushManDBHelper dbh = getDatabase(mContext);
+            SQLiteDatabase db = dbh.getWritableDatabase();
+            WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
+            boolean ret = false;
+            boolean insert = false;
+            int sq = 0;
+
+            if (!appTypeCheck(app_type)) {
+                Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be "
+                        + WapPushManagerParams.APP_TYPE_ACTIVITY + " or "
+                        + WapPushManagerParams.APP_TYPE_SERVICE);
+                return false;
+            }
+
+            if (lastapp == null) {
+                insert = true;
+                sq = 0;
+            } else if (!lastapp.packageName.equals(package_name) ||
+                    !lastapp.className.equals(class_name)) {
+                insert = true;
+                sq = lastapp.installOrder + 1;
+            }
+
+            if (insert) {
+                ContentValues values = new ContentValues();
+
+                values.put("x_wap_application", x_app_id);
+                values.put("content_type", content_type);
+                values.put("package_name", package_name);
+                values.put("class_name", class_name);
+                values.put("app_type", app_type);
+                values.put("need_signature", need_signature ? 1 : 0);
+                values.put("further_processing", further_processing ? 1 : 0);
+                values.put("install_order", sq);
+                db.insert(APPID_TABLE_NAME, null, values);
+                if (LOCAL_LOGV) Log.v(LOG_TAG, "add:" + x_app_id + ":" + content_type
+                        + " " + package_name + "." + class_name
+                        + ", newsq:" + sq);
+                ret = true;
+            }
+
+            db.close();
+
+            return ret;
+        }
+
+        /**
+         * Returns true if updating the package succeeded.
+         */
+        public boolean updatePackage(String x_app_id, String content_type,
+                String package_name, String class_name,
+                int app_type, boolean need_signature, boolean further_processing) {
+
+            if (!appTypeCheck(app_type)) {
+                Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be "
+                        + WapPushManagerParams.APP_TYPE_ACTIVITY + " or "
+                        + WapPushManagerParams.APP_TYPE_SERVICE);
+                return false;
+            }
+
+            WapPushManDBHelper dbh = getDatabase(mContext);
+            SQLiteDatabase db = dbh.getWritableDatabase();
+            WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
+
+            if (lastapp == null) {
+                db.close();
+                return false;
+            }
+
+            ContentValues values = new ContentValues();
+            String where = "x_wap_application=\'" + x_app_id + "\'"
+                    + " and content_type=\'" + content_type + "\'"
+                    + " and install_order=" + lastapp.installOrder;
+
+            values.put("package_name", package_name);
+            values.put("class_name", class_name);
+            values.put("app_type", app_type);
+            values.put("need_signature", need_signature ? 1 : 0);
+            values.put("further_processing", further_processing ? 1 : 0);
+
+            int num = db.update(APPID_TABLE_NAME, values, where, null);
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "update:" + x_app_id + ":" + content_type + " "
+                    + package_name + "." + class_name
+                    + ", sq:" + lastapp.installOrder);
+
+            db.close();
+
+            return num > 0;
+        }
+
+        /**
+         * Returns true if deleting the package succeeded.
+         */
+        public boolean deletePackage(String x_app_id, String content_type,
+                String package_name, String class_name) {
+            WapPushManDBHelper dbh = getDatabase(mContext);
+            SQLiteDatabase db = dbh.getWritableDatabase();
+            String where = "x_wap_application=\'" + x_app_id + "\'"
+                    + " and content_type=\'" + content_type + "\'"
+                    + " and package_name=\'" + package_name + "\'"
+                    + " and class_name=\'" + class_name + "\'";
+            int num_removed = db.delete(APPID_TABLE_NAME, where, null);
+
+            db.close();
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "deleted " + num_removed + " rows:"
+                    + x_app_id + ":" + content_type + " "
+                    + package_name + "." + class_name);
+            return num_removed > 0;
+        }
+    };
+
+
+    /**
+     * Linux IPC Binder
+     */
+    private final IWapPushManagerStub mBinder = new IWapPushManagerStub();
+
+    /**
+     * Default constructor
+     */
+    public WapPushManager() {
+        super();
+        mBinder.mContext = this;
+    }
+
+    @Override
+    public IBinder onBind(Intent arg0) {
+        return mBinder;
+    }
+
+    /**
+     * Application ID database instance
+     */
+    private WapPushManDBHelper mDbHelper = null;
+    protected WapPushManDBHelper getDatabase(Context context) {
+        if (mDbHelper == null) {
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "create new db inst.");
+            mDbHelper = new WapPushManDBHelper(context);
+        }
+        return mDbHelper;
+    }
+
+
+    /**
+     * This method is used for testing
+     */
+    public boolean verifyData(String x_app_id, String content_type,
+            String package_name, String class_name,
+            int app_type, boolean need_signature, boolean further_processing) {
+        WapPushManDBHelper dbh = getDatabase(this);
+        SQLiteDatabase db = dbh.getReadableDatabase();
+        WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
+
+        db.close();
+
+        if (lastapp == null) return false;
+
+        if (lastapp.packageName.equals(package_name)
+                && lastapp.className.equals(class_name)
+                && lastapp.appType == app_type
+                &&  lastapp.needSignature == (need_signature ? 1 : 0)
+                &&  lastapp.furtherProcessing == (further_processing ? 1 : 0)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * This method is used for testing
+     */
+    public boolean isDataExist(String x_app_id, String content_type,
+            String package_name, String class_name) {
+        WapPushManDBHelper dbh = getDatabase(this);
+        SQLiteDatabase db = dbh.getReadableDatabase();
+        boolean ret = dbh.queryLastApp(db, x_app_id, content_type) != null;
+
+        db.close();
+        return ret;
+    }
+
+}
+
diff --git a/packages/WAPPushManager/tests/Android.mk b/packages/WAPPushManager/tests/Android.mk
new file mode 100644
index 0000000..0a95b52
--- /dev/null
+++ b/packages/WAPPushManager/tests/Android.mk
@@ -0,0 +1,38 @@
+# Copyright 2008, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += \
+        src/com/android/smspush/unitTests/IDataVerify.aidl
+
+
+# Notice that we don't have to include the src files of Email because, by
+# running the tests using an instrumentation targeting Eamil, we
+# automatically get all of its classes loaded into our environment.
+
+LOCAL_PACKAGE_NAME := WAPPushManagerTests
+
+LOCAL_INSTRUMENTATION_FOR := WAPPushManager
+
+include $(BUILD_PACKAGE)
+
diff --git a/packages/WAPPushManager/tests/AndroidManifest.xml b/packages/WAPPushManager/tests/AndroidManifest.xml
new file mode 100644
index 0000000..da7634f
--- /dev/null
+++ b/packages/WAPPushManager/tests/AndroidManifest.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+
+-->
+
+<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.smspush.unitTests">
+
+
+     <uses-permission android:name="com.android.smspush.WAPPUSH_MANAGER_BIND" />
+     <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
+     <!--testing.../-->
+     <application android:icon="@drawable/icon" android:label="wappush test">
+         <uses-library android:name="android.test.runner" />
+         <activity android:name=".ClientTest"
+             android:label="wappush test">
+             <intent-filter>
+                 <action android:name="android.intent.action.MAIN" />
+                 <category android:name="android.intent.category.LAUNCHER" />
+             </intent-filter>
+         </activity>
+
+         <receiver android:name=".DrmReceiver" android:enabled="true">
+             <intent-filter>
+                 <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
+                 <data android:mimeType="application/vnd.oma.drm.rights+xml" />
+                 <data android:value="application/vnd.oma.drm.rights+wbxml" />
+             </intent-filter>
+         </receiver>
+
+         <service android:enabled="true" android:name=".ReceiverService"
+            android:exported="true"/>
+
+         <activity android:name=".ReceiverActivity"
+             android:exported="true" android:label="test receiver" />
+
+        <service android:name=".DataVerify"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.smspush.unitTests.IDataVerify" />
+            </intent-filter>
+        </service>
+
+     </application>
+
+     <!--
+     This declares that this application uses the instrumentation test runner targeting
+     the package of com.android.smspush.  To run the tests use the command:
+     "adb shell am instrument -w
+        com.android.smspush.unitTests/android.test.InstrumentationTestRunner"
+     -->
+     <instrumentation android:name="android.test.InstrumentationTestRunner"
+         android:targetPackage="com.android.smspush"
+         android:label="Tests for WAPPushManager"/>
+
+</manifest>
+
diff --git a/packages/WAPPushManager/tests/res/drawable-hdpi/icon.png b/packages/WAPPushManager/tests/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..8074c4c
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png b/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..1095584
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png b/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/packages/WAPPushManager/tests/res/layout/main.xml b/packages/WAPPushManager/tests/res/layout/main.xml
new file mode 100644
index 0000000..c7bdbb2
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/layout/main.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<AbsoluteLayout
+android:id="@+id/widget133"
+android:layout_width="fill_parent"
+android:layout_height="fill_parent"
+xmlns:android="http://schemas.android.com/apk/res/android"
+>
+<EditText
+android:id="@+id/app_id"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="10"
+android:textSize="18sp"
+android:layout_x="0px"
+android:layout_y="26px"
+>
+</EditText>
+<EditText
+android:id="@+id/cont"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="20"
+android:textSize="18sp"
+android:layout_x="47px"
+android:layout_y="26px"
+>
+</EditText>
+<EditText
+android:id="@+id/pkg"
+android:layout_width="125px"
+android:layout_height="wrap_content"
+android:text="pkg"
+android:textSize="18sp"
+android:layout_x="0px"
+android:layout_y="81px"
+>
+</EditText>
+<EditText
+android:id="@+id/cls"
+android:layout_width="173px"
+android:layout_height="wrap_content"
+android:text="cls"
+android:textSize="18sp"
+android:layout_x="147px"
+android:layout_y="81px"
+>
+</EditText>
+<Button
+android:id="@+id/addpkg"
+android:layout_width="182px"
+android:layout_height="wrap_content"
+android:text="add/update package"
+android:layout_x="15px"
+android:layout_y="225px"
+>
+</Button>
+<TextView
+android:id="@+id/widget52"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="input app_id, cont_type, pkg, cls"
+android:textSize="18sp"
+android:layout_x="0px"
+android:layout_y="0px"
+>
+</TextView>
+<Button
+android:id="@+id/procmsg"
+android:layout_width="109px"
+android:layout_height="wrap_content"
+android:text="process msg"
+android:layout_x="197px"
+android:layout_y="361px"
+>
+</Button>
+<RadioGroup
+android:id="@+id/widget137"
+android:layout_width="83px"
+android:layout_height="80px"
+android:orientation="vertical"
+android:layout_x="19px"
+android:layout_y="137px"
+>
+<RadioButton
+android:id="@+id/act"
+android:layout_width="182px"
+android:layout_height="wrap_content"
+android:text="act"
+android:checked="true"
+>
+</RadioButton>
+<RadioButton
+android:id="@+id/svc"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="svc"
+>
+</RadioButton>
+</RadioGroup>
+<Button
+android:id="@+id/delpkg"
+android:layout_width="174px"
+android:layout_height="wrap_content"
+android:text="delete pkg"
+android:layout_x="14px"
+android:layout_y="283px"
+>
+</Button>
+<EditText
+android:id="@+id/pdu"
+android:layout_width="186px"
+android:layout_height="83px"
+android:text="0006080302030aaf02905c030d6a0085070373616d706c6540646f636f6d6f2e6e652e6a700005c3072009102012345601"
+android:textSize="18sp"
+android:layout_x="10px"
+android:layout_y="341px"
+>
+</EditText>
+<CheckBox
+android:id="@+id/ftr"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="ftr_proc"
+android:layout_x="143px"
+android:layout_y="181px"
+>
+</CheckBox>
+<CheckBox
+android:id="@+id/sig"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="nd_sig"
+android:checked="true"
+android:layout_x="142px"
+android:layout_y="140px"
+>
+</CheckBox>
+</AbsoluteLayout>
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java
new file mode 100644
index 0000000..78fd174
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.smspush.unitTests;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.RadioButton;
+
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.telephony.WapPushManagerParams;
+import com.android.internal.telephony.WapPushOverSms;
+import com.android.internal.util.HexDump;
+import com.android.smspush.WapPushManager;
+
+/**
+ * WapPushManager test application
+ */
+public class ClientTest extends Activity {
+    private static final String LOG_TAG = "WAP PUSH";
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        Button addpbtn = (Button) findViewById(R.id.addpkg);
+        Button procbtn = (Button) findViewById(R.id.procmsg);
+        Button delbtn = (Button) findViewById(R.id.delpkg);
+
+        Log.v(LOG_TAG, "activity created!!");
+
+        addpbtn.setOnClickListener(new View.OnClickListener() {
+                public void onClick(View v) {
+                    EditText app_id = (EditText) findViewById(R.id.app_id);
+                    EditText cont = (EditText) findViewById(R.id.cont);
+                    EditText pkg = (EditText) findViewById(R.id.pkg);
+                    EditText cls = (EditText) findViewById(R.id.cls);
+                    RadioButton act = (RadioButton) findViewById(R.id.act);
+                    CheckBox sig = (CheckBox) findViewById(R.id.sig);
+                    CheckBox ftr = (CheckBox) findViewById(R.id.ftr);
+
+                    try {
+                        if (!mWapPushMan.addPackage(
+                                app_id.getText().toString(),
+                                cont.getText().toString(),
+                                pkg.getText().toString(),
+                                cls.getText().toString(),
+                                act.isChecked() ? WapPushManagerParams.APP_TYPE_ACTIVITY :
+                                WapPushManagerParams.APP_TYPE_SERVICE,
+                                sig.isChecked(), ftr.isChecked())) {
+
+                            Log.w(LOG_TAG, "remote add pkg failed...");
+                            mWapPushMan.updatePackage(
+                                    app_id.getText().toString(),
+                                    cont.getText().toString(),
+                                    pkg.getText().toString(),
+                                    cls.getText().toString(),
+                                    act.isChecked() ? WapPushManagerParams.APP_TYPE_ACTIVITY :
+                                    WapPushManagerParams.APP_TYPE_SERVICE,
+                                    sig.isChecked(), ftr.isChecked());
+                        }
+                    } catch (RemoteException e) {
+                            Log.w(LOG_TAG, "remote func failed...");
+                    }
+                }
+            });
+
+        delbtn.setOnClickListener(new View.OnClickListener() {
+                public void onClick(View v) {
+                    EditText app_id = (EditText) findViewById(R.id.app_id);
+                    EditText cont = (EditText) findViewById(R.id.cont);
+                    EditText pkg = (EditText) findViewById(R.id.pkg);
+                    EditText cls = (EditText) findViewById(R.id.cls);
+                    // CheckBox delall = (CheckBox) findViewById(R.id.delall);
+                    // Log.d(LOG_TAG, "button clicked");
+
+                    try {
+                        mWapPushMan.deletePackage(
+                                app_id.getText().toString(),
+                                cont.getText().toString(),
+                                pkg.getText().toString(),
+                                cls.getText().toString());
+                        // delall.isChecked());
+                    } catch (RemoteException e) {
+                        Log.w(LOG_TAG, "remote func failed...");
+                    }
+                }
+            });
+
+        procbtn.setOnClickListener(new View.OnClickListener() {
+                public void onClick(View v) {
+                    EditText pdu = (EditText) findViewById(R.id.pdu);
+                    EditText app_id = (EditText) findViewById(R.id.app_id);
+                    EditText cont = (EditText) findViewById(R.id.cont);
+
+                    // WapPushOverSms wap = new WapPushOverSms();
+                    // wap.dispatchWapPdu(strToHex(pdu.getText().toString()));
+                    try {
+                        Intent intent = new Intent();
+                        intent.putExtra("transactionId", 0);
+                        intent.putExtra("pduType", 6);
+                        intent.putExtra("header",
+                                HexDump.hexStringToByteArray(pdu.getText().toString()));
+                        intent.putExtra("data",
+                                HexDump.hexStringToByteArray(pdu.getText().toString()));
+
+                        mWapPushMan.processMessage(
+                                app_id.getText().toString(),
+                                cont.getText().toString(),
+                                intent);
+                        //HexDump.hexStringToByteArray(pdu.getText().toString()), 0, 6, 5, 5);
+                    } catch (RemoteException e) {
+                        Log.w(LOG_TAG, "remote func failed...");
+                    }
+                }
+            });
+    }
+
+    private IWapPushManager mWapPushMan;
+    private ServiceConnection conn = new ServiceConnection() {
+        public void onServiceDisconnected(ComponentName name) {
+            mWapPushMan = null;
+            Log.v(LOG_TAG, "service disconnected.");
+        }
+
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mWapPushMan = IWapPushManager.Stub.asInterface(service);
+            Log.v(LOG_TAG, "service connected.");
+        }
+        };
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        Log.v(LOG_TAG, "onStart bind WAPPushManager service "
+                + IWapPushManager.class.getName());
+        this.bindService(new Intent(IWapPushManager.class.getName()), conn,
+                Context.BIND_AUTO_CREATE);
+        Log.v(LOG_TAG, "bind service done.");
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        this.unbindService(conn);
+    }
+
+}
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java
new file mode 100644
index 0000000..ef491fd
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java
@@ -0,0 +1,119 @@
+/*
+ * 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.smspush.unitTests;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * To verify that receiver application receives correct body data.
+ */
+public class DataVerify extends Service {
+    private static final String LOG_TAG = "WAP PUSH";
+    private static final int TIME_WAIT = 100;
+    private static final int WAIT_COUNT = 100;
+    private static byte[] mLastReceivedPdu = null;
+    private static boolean sDataSet = false;
+
+    private class IDataVerifyStub extends IDataVerify.Stub {
+        public Context mContext;
+
+        public IDataVerifyStub() {
+        }
+
+        boolean arrayCompare(byte[] arr1, byte[] arr2) {
+            int i;
+
+            if (arr1 == null || arr2 == null) {
+                if (arr1 == null) {
+                    Log.w(LOG_TAG, "arr1 is null");
+                } else {
+                    Log.w(LOG_TAG, "arr2 is null");
+                }
+                return false;
+            }
+
+            if (arr1.length != arr2.length) {
+                return false;
+            }
+
+            for (i = 0; i < arr1.length; i++) {
+                if (arr1[i] != arr2[i]) return false;
+            }
+            return true;
+        }
+
+        /**
+         * Compare pdu and received pdu
+         */
+        public synchronized boolean verifyData(byte[] pdu) {
+            int cnt = 0;
+
+            while (!sDataSet) {
+                // wait for the activity receive data.
+                try {
+                    Thread.sleep(TIME_WAIT);
+                    if (cnt++ > WAIT_COUNT) {
+                        // don't wait more than 10 sec.
+                        return false;
+                    }
+                } catch (InterruptedException e) {}
+            }
+
+            Log.v(LOG_TAG, "verify pdu");
+            boolean ret = arrayCompare(pdu, mLastReceivedPdu);
+            return ret;
+        }
+
+        /**
+         * Clear the old data. This method must be called before starting the test
+         */
+        public void resetData() {
+            mLastReceivedPdu = null;
+            sDataSet = false;
+        }
+    }
+
+    private final IDataVerifyStub binder = new IDataVerifyStub();
+
+    /**
+     * Constructor
+     */
+    public DataVerify() {
+    }
+
+    /**
+     * Receiver application must call this method when it receives the wap push message
+     */
+    public static void SetLastReceivedPdu(byte[] pdu) {
+        mLastReceivedPdu = pdu;
+        sDataSet = true;
+    }
+
+    @Override
+    public IBinder onBind(Intent arg0) {
+        return binder;
+    }
+
+}
+
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java
new file mode 100644
index 0000000..5f5f121
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java
@@ -0,0 +1,53 @@
+/*
+ * 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.smspush.unitTests;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * A sample wap push receiver application for existing framework
+ * This class is listening for "application/vnd.oma.drm.rights+xml" message
+ */
+public class DrmReceiver extends BroadcastReceiver {
+    private static final String LOG_TAG = "WAP PUSH";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.d(LOG_TAG, "DrmReceiver received.");
+
+        byte[] body;
+        byte[] header;
+
+        body = intent.getByteArrayExtra("data");
+        header = intent.getByteArrayExtra("header");
+
+        Log.d(LOG_TAG, "header:");
+        Log.d(LOG_TAG, HexDump.dumpHexString(header));
+        Log.d(LOG_TAG, "body:");
+        Log.d(LOG_TAG, HexDump.dumpHexString(body));
+
+        DataVerify.SetLastReceivedPdu(body);
+    }
+
+}
+
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl
new file mode 100644
index 0000000..f0670fa
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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.smspush.unitTests;
+
+/**
+ * Interface to receiver application data verifyer class
+ */
+interface IDataVerify {
+    /**
+     * Verify data
+     */
+    boolean verifyData(in byte[] pdu);
+
+    /**
+     * Initialize data
+     */
+    void resetData();
+}
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java
new file mode 100644
index 0000000..07f55ea
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java
@@ -0,0 +1,55 @@
+/*
+ * 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.smspush.unitTests;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * Activity type receiver application
+ */
+public class ReceiverActivity extends Activity {
+    private static final String LOG_TAG = "WAP PUSH";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.d(LOG_TAG, "activity created!!");
+
+        Intent in = getIntent();
+        byte[] body;
+        byte[] header;
+
+        body = in.getByteArrayExtra("data");
+        header = in.getByteArrayExtra("header");
+
+        Log.d(LOG_TAG, "header:");
+        Log.d(LOG_TAG, HexDump.dumpHexString(header));
+        Log.d(LOG_TAG, "body:");
+        Log.d(LOG_TAG, HexDump.dumpHexString(body));
+
+        DataVerify.SetLastReceivedPdu(body);
+
+        finish();
+
+    }
+}
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java
new file mode 100644
index 0000000..b024bf5
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java
@@ -0,0 +1,63 @@
+/*
+ * 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.smspush.unitTests;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * Service type receiver application
+ */
+public class ReceiverService extends Service {
+    private static final String LOG_TAG = "WAP PUSH";
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.d(LOG_TAG, "Receiver service created");
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.d(LOG_TAG, "Receiver service started");
+
+        byte[] body;
+        byte[] header;
+        body = intent.getByteArrayExtra("data");
+        header = intent.getByteArrayExtra("header");
+
+        Log.d(LOG_TAG, "header:");
+        Log.d(LOG_TAG, HexDump.dumpHexString(header));
+        Log.d(LOG_TAG, "body:");
+        Log.d(LOG_TAG, HexDump.dumpHexString(body));
+
+        DataVerify.SetLastReceivedPdu(body);
+        return START_STICKY;
+    }
+}
+
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
new file mode 100644
index 0000000..9b0a36b
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
@@ -0,0 +1,2513 @@
+/*
+ * 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.smspush.unitTests;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.provider.Telephony.Sms.Intents;
+import android.test.ServiceTestCase;
+import android.util.Log;
+import android.util.Config;
+
+import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.telephony.WapPushManagerParams;
+import com.android.internal.telephony.WspTypeDecoder;
+import com.android.internal.util.HexDump;
+import com.android.smspush.WapPushManager;
+
+import java.util.Random;
+
+/**
+ * This is a simple framework for a test of a Service.  See {@link android.test.ServiceTestCase
+ * ServiceTestCase} for more information on how to write and extend service tests.
+ *
+ * To run this test, you can type:
+ * adb shell am instrument -w \
+ * -e class com.android.smspush.unitTests.WapPushTest \
+ * com.android.smspush.unitTests/android.test.InstrumentationTestRunner
+ */
+public class WapPushTest extends ServiceTestCase<WapPushManager> {
+    private static final String LOG_TAG = "WAP PUSH";
+    private static final boolean LOCAL_LOGV = false;
+    private static final int TIME_WAIT = 100;
+
+    protected int mAppIdValue = 0x8002;
+    protected String mAppIdName = "x-wap-application:*";
+    protected int mContentTypeValue = 0x030a;
+    protected String mContentTypeName = "application/vnd.wap.sic";
+
+    protected String mPackageName;
+    protected String mClassName;
+
+    protected byte[] mGsmHeader = {
+            (byte) 0x00, // sc address
+            (byte) 0x40, // TP-MTI
+            (byte) 0x04, // sender address length?
+            (byte) 0x81, (byte) 0x55, (byte) 0x45, // sender address?
+            (byte) 0x00, // data schema
+            (byte) 0x00, // proto ID
+            (byte) 0x01, (byte) 0x60, (byte) 0x12, (byte) 0x31,
+            (byte) 0x74, (byte) 0x34, (byte) 0x63 // time stamp
+    };
+
+    protected byte[] mUserDataHeader = {
+            (byte) 0x07, // UDH len
+            (byte) 0x06, // header len
+            (byte) 0x05, // port addressing type?
+            (byte) 0x00, // dummy
+            (byte) 0x0B, (byte) 0x84, // dest port
+            (byte) 0x23, (byte) 0xF0 // src port
+    };
+
+    protected byte[] mWspHeader;
+
+    protected byte[] mMessageBody = {
+            (byte) 0x00,
+            (byte) 0x01,
+            (byte) 0x02,
+            (byte) 0x03,
+            (byte) 0x04,
+            (byte) 0x05,
+            (byte) 0x06,
+            (byte) 0x07,
+            (byte) 0x08,
+            (byte) 0x09,
+            (byte) 0x0a,
+            (byte) 0x0b,
+            (byte) 0x0c,
+            (byte) 0x0d,
+            (byte) 0x0e,
+            (byte) 0x0f
+    };
+
+    protected int mWspHeaderStart;
+    protected int mWspHeaderLen;
+    protected int mWspContentTypeStart;
+
+    /**
+     * OMA application ID in binary form
+     * http://www.openmobilealliance.org/tech/omna/omna-push-app-id.aspx
+     */
+    final int[] OMA_APPLICATION_ID_VALUES = new int[] {
+            0x00,
+            0x01,
+            0x02,
+            0x03,
+            0x04,
+            0x05,
+            0x06,
+            0x07,
+            0x08,
+            0x09,
+            0x0A,
+            0x8000,
+            0x8001,
+            0x8002,
+            0x8003,
+            0x8004,
+            0x8005,
+            0x8006,
+            0x8007,
+            0x8008,
+            0x8009,
+            0x800B,
+            0x8010
+    };
+
+    /**
+     * OMA application ID in string form
+     * http://www.openmobilealliance.org/tech/omna/omna-push-app-id.aspx
+     */
+    final String[] OMA_APPLICATION_ID_NAMES = new String[] {
+            "x-wap-application:*",
+            "x-wap-application:push.sia",
+            "x-wap-application:wml.ua",
+            "x-wap-application:wta.ua",
+            "x-wap-application:mms.ua",
+            "x-wap-application:push.syncml",
+            "x-wap-application:loc.ua",
+            "x-wap-application:syncml.dm",
+            "x-wap-application:drm.ua",
+            "x-wap-application:emn.ua",
+            "x-wap-application:wv.ua",
+            "x-wap-microsoft:localcontent.ua",
+            "x-wap-microsoft:IMclient.ua",
+            "x-wap-docomo:imode.mail.ua",
+            "x-wap-docomo:imode.mr.ua",
+            "x-wap-docomo:imode.mf.ua",
+            "x-motorola:location.ua",
+            "x-motorola:now.ua",
+            "x-motorola:otaprov.ua",
+            "x-motorola:browser.ua",
+            "x-motorola:splash.ua",
+            "x-wap-nai:mvsw.command",
+            "x-wap-openwave:iota.ua"
+    };
+
+    /**
+     * OMA content type in binary form
+     * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
+     */
+    final int[] OMA_CONTENT_TYPE_VALUES = new int[] {
+            0x00,
+            0x01,
+            0x02,
+            0x03,
+            0x04,
+            0x05,
+            0x06,
+            0x07,
+            0x08,
+            0x09,
+            0x0A,
+            0x0B,
+            0x0C,
+            0x0D,
+            0x0E,
+            0x0F,
+            0x10,
+            0x11,
+            0x12,
+            0x13,
+            0x14,
+            0x15,
+            0x16,
+            0x17,
+            0x18,
+            0x19,
+            0x1A,
+            0x1B,
+            0x1C,
+            0x1D,
+            0x1E,
+            0x1F,
+            0x20,
+            0x21,
+            0x22,
+            0x23,
+            0x24,
+            0x25,
+            0x26,
+            0x27,
+            0x28,
+            0x29,
+            0x2A,
+            0x2B,
+            0x2C,
+            0x2D,
+            0x2E,
+            0x2F,
+            0x30,
+            0x31,
+            0x32,
+            0x33,
+            0x34,
+            0x35,
+            0x36,
+            0x37,
+            0x38,
+            0x39,
+            0x3A,
+            0x3B,
+            0x3C,
+            0x3D,
+            0x3E,
+            0x3F,
+            0x40,
+            0x41,
+            0x42,
+            0x43,
+            0x44,
+            0x45,
+            0x46,
+            0x47,
+            0x48,
+            0x49,
+            0x4A,
+            0x4B,
+            0x4C,
+            0x4D,
+            0x4E,
+            0x4F,
+            0x50,
+            0x51,
+            0x52,
+            0x53,
+            0x54,
+//            0x55,
+//            0x56,
+//            0x57,
+//            0x58,
+            0x0201,
+            0x0202,
+            0x0203,
+            0x0204,
+            0x0205,
+            0x0206,
+            0x0207,
+            0x0208,
+            0x0209,
+            0x020A,
+            0x020B,
+            0x020C,
+            0x0300,
+            0x0301,
+            0x0302,
+            0x0303,
+            0x0304,
+            0x0305,
+            0x0306,
+            0x0307,
+            0x0308,
+            0x0309,
+            0x030A,
+            0x030B,
+            0x030C,
+            0x030D,
+            0x030E,
+            0x030F,
+            0x0310,
+            0x0311,
+            0x0312,
+            0x0313,
+            0x0314,
+            0x0315,
+            0x0316,
+            0x0317,
+            0x0318,
+            0x0319,
+            0x031A,
+            0x031B
+            /*0x031C,
+              0x031D*/
+    };
+
+    /**
+     * OMA content type in string form
+     * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
+     */
+    final String[] OMA_CONTENT_TYPE_NAMES = new String[] {
+            "*/*",
+            "text/*",
+            "text/html",
+            "text/plain",
+            "text/x-hdml",
+            "text/x-ttml",
+            "text/x-vCalendar",
+            "text/x-vCard",
+            "text/vnd.wap.wml",
+            "text/vnd.wap.wmlscript",
+            "text/vnd.wap.wta-event",
+            "multipart/*",
+            "multipart/mixed",
+            "multipart/form-data",
+            "multipart/byterantes",
+            "multipart/alternative",
+            "application/*",
+            "application/java-vm",
+            "application/x-www-form-urlencoded",
+            "application/x-hdmlc",
+            "application/vnd.wap.wmlc",
+            "application/vnd.wap.wmlscriptc",
+            "application/vnd.wap.wta-eventc",
+            "application/vnd.wap.uaprof",
+            "application/vnd.wap.wtls-ca-certificate",
+            "application/vnd.wap.wtls-user-certificate",
+            "application/x-x509-ca-cert",
+            "application/x-x509-user-cert",
+            "image/*",
+            "image/gif",
+            "image/jpeg",
+            "image/tiff",
+            "image/png",
+            "image/vnd.wap.wbmp",
+            "application/vnd.wap.multipart.*",
+            "application/vnd.wap.multipart.mixed",
+            "application/vnd.wap.multipart.form-data",
+            "application/vnd.wap.multipart.byteranges",
+            "application/vnd.wap.multipart.alternative",
+            "application/xml",
+            "text/xml",
+            "application/vnd.wap.wbxml",
+            "application/x-x968-cross-cert",
+            "application/x-x968-ca-cert",
+            "application/x-x968-user-cert",
+            "text/vnd.wap.si",
+            "application/vnd.wap.sic",
+            "text/vnd.wap.sl",
+            "application/vnd.wap.slc",
+            "text/vnd.wap.co",
+            "application/vnd.wap.coc",
+            "application/vnd.wap.multipart.related",
+            "application/vnd.wap.sia",
+            "text/vnd.wap.connectivity-xml",
+            "application/vnd.wap.connectivity-wbxml",
+            "application/pkcs7-mime",
+            "application/vnd.wap.hashed-certificate",
+            "application/vnd.wap.signed-certificate",
+            "application/vnd.wap.cert-response",
+            "application/xhtml+xml",
+            "application/wml+xml",
+            "text/css",
+            "application/vnd.wap.mms-message",
+            "application/vnd.wap.rollover-certificate",
+            "application/vnd.wap.locc+wbxml",
+            "application/vnd.wap.loc+xml",
+            "application/vnd.syncml.dm+wbxml",
+            "application/vnd.syncml.dm+xml",
+            "application/vnd.syncml.notification",
+            "application/vnd.wap.xhtml+xml",
+            "application/vnd.wv.csp.cir",
+            "application/vnd.oma.dd+xml",
+            "application/vnd.oma.drm.message",
+            "application/vnd.oma.drm.content",
+            "application/vnd.oma.drm.rights+xml",
+            "application/vnd.oma.drm.rights+wbxml",
+            "application/vnd.wv.csp+xml",
+            "application/vnd.wv.csp+wbxml",
+            "application/vnd.syncml.ds.notification",
+            "audio/*",
+            "video/*",
+            "application/vnd.oma.dd2+xml",
+            "application/mikey",
+            "application/vnd.oma.dcd",
+            "application/vnd.oma.dcdc",
+//            "text/x-vMessage",
+//            "application/vnd.omads-email+wbxml",
+//            "text/x-vBookmark",
+//            "application/vnd.syncml.dm.notification",
+            "application/vnd.uplanet.cacheop-wbxml",
+            "application/vnd.uplanet.signal",
+            "application/vnd.uplanet.alert-wbxml",
+            "application/vnd.uplanet.list-wbxml",
+            "application/vnd.uplanet.listcmd-wbxml",
+            "application/vnd.uplanet.channel-wbxml",
+            "application/vnd.uplanet.provisioning-status-uri",
+            "x-wap.multipart/vnd.uplanet.header-set",
+            "application/vnd.uplanet.bearer-choice-wbxml",
+            "application/vnd.phonecom.mmc-wbxml",
+            "application/vnd.nokia.syncset+wbxml",
+            "image/x-up-wpng",
+            "application/iota.mmc-wbxml",
+            "application/iota.mmc-xml",
+            "application/vnd.syncml+xml",
+            "application/vnd.syncml+wbxml",
+            "text/vnd.wap.emn+xml",
+            "text/calendar",
+            "application/vnd.omads-email+xml",
+            "application/vnd.omads-file+xml",
+            "application/vnd.omads-folder+xml",
+            "text/directory;profile=vCard",
+            "application/vnd.wap.emn+wbxml",
+            "application/vnd.nokia.ipdc-purchase-response",
+            "application/vnd.motorola.screen3+xml",
+            "application/vnd.motorola.screen3+gzip",
+            "application/vnd.cmcc.setting+wbxml",
+            "application/vnd.cmcc.bombing+wbxml",
+            "application/vnd.docomo.pf",
+            "application/vnd.docomo.ub",
+            "application/vnd.omaloc-supl-init",
+            "application/vnd.oma.group-usage-list+xml",
+            "application/oma-directory+xml",
+            "application/vnd.docomo.pf2",
+            "application/vnd.oma.drm.roap-trigger+wbxml",
+            "application/vnd.sbm.mid2",
+            "application/vnd.wmf.bootstrap",
+            "application/vnc.cmcc.dcd+xml",
+            "application/vnd.sbm.cid",
+            "application/vnd.oma.bcast.provisioningtrigger",
+            /*"application/vnd.docomo.dm",
+              "application/vnd.oma.scidm.messages+xml"*/
+    };
+
+    private IDataVerify mIVerify = null;
+
+    ServiceConnection mConn = new ServiceConnection() {
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                Log.v(LOG_TAG, "data verify interface connected.");
+                mIVerify = IDataVerify.Stub.asInterface(service);
+            }
+            public void onServiceDisconnected(ComponentName name) {
+            }
+        };
+
+    /**
+     * Main WapPushManager test module constructor
+     */
+    public WapPushTest() {
+        super(WapPushManager.class);
+        mClassName = this.getClass().getName();
+        mPackageName = this.getClass().getPackage().getName();
+    }
+
+    /**
+     * Initialize the verifier
+     */
+    @Override
+    public void setUp() {
+        try {
+            super.setUp();
+            // get verifier
+            getContext().bindService(new Intent(IDataVerify.class.getName()),
+                    mConn, Context.BIND_AUTO_CREATE);
+        } catch (Exception e) {
+            Log.w(LOG_TAG, "super exception");
+        }
+        // Log.d(LOG_TAG, "test setup");
+    }
+
+    private IWapPushManager mWapPush = null;
+    IWapPushManager getInterface() {
+        if (mWapPush != null) return mWapPush;
+        Intent startIntent = new Intent();
+        startIntent.setClass(getContext(), WapPushManager.class);
+        IBinder service = bindService(startIntent);
+
+        mWapPush = IWapPushManager.Stub.asInterface(service);
+        return mWapPush;
+    }
+
+    /*
+     * All methods need to start with 'test'.
+     * Use various assert methods to pass/fail the test case.
+     */
+    protected void utAddPackage(boolean need_sig, boolean more_proc) {
+        IWapPushManager iwapman = getInterface();
+
+        // insert new data
+        try {
+            assertTrue(iwapman.addPackage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        // verify the data
+        WapPushManager wpman = getService();
+        assertTrue(wpman.verifyData(Integer.toString(mAppIdValue),
+                Integer.toString(mContentTypeValue),
+                mPackageName, mClassName,
+                WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+    }
+
+    /**
+     * Add package test
+     */
+    public void testAddPackage1() {
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+
+        utAddPackage(true, true);
+        mAppIdValue += 10;
+        utAddPackage(true, false);
+        mContentTypeValue += 20;
+        utAddPackage(false, true);
+        mContentTypeValue += 20;
+        utAddPackage(false, false);
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+
+        // clean up data
+        try {
+            IWapPushManager iwapman = getInterface();
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            mAppIdValue += 10;
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            mContentTypeValue += 20;
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            mContentTypeValue += 20;
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /**
+     * Add duprecated package test.
+     */
+    public void testAddPackage2() {
+        try {
+            IWapPushManager iwapman = getInterface();
+
+            // set up data
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+                    false, false);
+            iwapman.addPackage(Integer.toString(mAppIdValue + 10),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+                    false, false);
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue + 10), mPackageName, mClassName, 0,
+                    false, false);
+
+            assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+                    false, false));
+            assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue + 10),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+                    false, false));
+            assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue + 10), mPackageName, mClassName, 0,
+                    false, false));
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue + 10), mPackageName, mClassName);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+    }
+
+    protected void utUpdatePackage(boolean need_sig, boolean more_proc) {
+        IWapPushManager iwapman = getInterface();
+
+        // insert new data
+        try {
+            assertTrue(iwapman.updatePackage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        // verify the data
+        WapPushManager wpman = getService();
+        assertTrue(wpman.verifyData(
+                Integer.toString(mAppIdValue),
+                Integer.toString(mContentTypeValue),
+                mPackageName, mClassName,
+                WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+    }
+
+    /**
+     * Updating package test
+     */
+    public void testUpdatePackage1() {
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+
+        // set up data
+        try {
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            mAppIdValue += 10;
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            mContentTypeValue += 20;
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            mContentTypeValue += 20;
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        utUpdatePackage(false, false);
+        mAppIdValue += 10;
+        utUpdatePackage(false, true);
+        mContentTypeValue += 20;
+        utUpdatePackage(true, false);
+        mContentTypeValue += 20;
+        utUpdatePackage(true, true);
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+
+        // clean up data
+        try {
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            mAppIdValue += 10;
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            mContentTypeValue += 20;
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            mContentTypeValue += 20;
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /**
+     * Updating invalid package test
+     */
+    public void testUpdatePackage2() {
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+
+        try {
+            // set up data
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            assertFalse(iwapman.updatePackage(
+                    Integer.toString(mAppIdValue + 10),
+                    Integer.toString(mContentTypeValue),
+                    mPackageName, mClassName, 0, false, false));
+            assertFalse(iwapman.updatePackage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue + 10),
+                    mPackageName, mClassName, 0, false, false));
+            assertTrue(iwapman.updatePackage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    mPackageName + "dummy_data", mClassName, 0, false, false));
+            assertTrue(iwapman.updatePackage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    mPackageName, mClassName + "dummy_data", 0, false, false));
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName,
+                    mClassName + "dummy_data");
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+    }
+
+    protected void utDeletePackage() {
+        IWapPushManager iwapman = getInterface();
+
+        try {
+            assertTrue(iwapman.deletePackage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    mPackageName, mClassName));
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        // verify the data
+        WapPushManager wpman = getService();
+        assertTrue(!wpman.isDataExist(
+                Integer.toString(mAppIdValue),
+                Integer.toString(mContentTypeValue),
+                mPackageName, mClassName));
+    }
+
+    /**
+     * Deleting package test
+     */
+    public void testDeletePackage1() {
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+
+        // set up data
+        try {
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            mAppIdValue += 10;
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            mContentTypeValue += 20;
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            mContentTypeValue += 20;
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        utDeletePackage();
+        mAppIdValue += 10;
+        utDeletePackage();
+        mContentTypeValue += 20;
+        utDeletePackage();
+        mContentTypeValue += 20;
+        utDeletePackage();
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /**
+     * Deleting invalid package test
+     */
+    public void testDeletePackage2() {
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+
+        try {
+            // set up data
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+
+            assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName));
+            assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue + 20), mPackageName, mClassName));
+            assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+                    Integer.toString(mContentTypeValue + 20), mPackageName, mClassName));
+
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+    }
+
+
+    protected int encodeUint32(int uint32Val, byte[] arr, int start) {
+        int bit = 1;
+        int topbit = 0;
+        int encodeLen;
+        int tmpVal;
+
+        assertTrue(uint32Val >= 0);
+        for (int i = 0; i < 31; i++) {
+            if ((bit & uint32Val) > 0) topbit = i;
+            bit = (bit << 1);
+        }
+        encodeLen = topbit/7 + 1;
+        if (arr == null) return encodeLen;
+
+        //Log.d(LOG_TAG, "uint32Val = " + Integer.toHexString(uint32Val) + ", topbit = "
+        //      + topbit + ", encodeLen = " + encodeLen);
+
+        tmpVal = uint32Val;
+        for (int i = encodeLen - 1; i >= 0; i--) {
+            long val = 0;
+            if (i < encodeLen - 1) val = 0x80;
+            val |= tmpVal & 0x7f;
+            arr[start + i] = (byte) (val & 0xFF);
+            tmpVal = (tmpVal >> 7);
+        }
+        return encodeLen;
+    }
+
+    protected int encodeShortInt(int sintVal, byte[] arr, int start) {
+        int encodeLen = 0;
+
+        if (sintVal >= 0x80) return encodeLen;
+        encodeLen = 1;
+        arr[start] = (byte) (sintVal | 0x80);
+        return encodeLen;
+    }
+
+
+    /**
+     * Generate Random WSP header with integer application ID
+     */
+    protected void createRandomWspHeader(byte[] arr, Random rd, int headerStart,
+            boolean noAppId) {
+
+        boolean appIdAdded = false;
+
+        Log.d(LOG_TAG, "headerStart = " + headerStart + ", appId = " + mAppIdValue
+                + "(" + Integer.toHexString(mAppIdValue) + ")");
+        Log.d(LOG_TAG, "random arr length:" + arr.length);
+        String typename[] = new String[] { "short int", "long int", "string", "uint32"};
+
+        while (!appIdAdded) {
+            int type;
+            int index = headerStart;
+            int len = arr.length;
+            int i;
+            boolean addAppid = false;
+            int tmpVal = 0;
+            int tmpVal2 = 0;
+
+            while (true) {
+                int add;
+
+                /*
+                 * field name
+                 * 0: short int
+                 * 1: long int
+                 * 2: text
+                 * (no uint for param value)
+                 */
+                type = rd.nextInt(3);
+                switch (type) {
+                case 0: // header short integer
+                    if (index > 100 && !appIdAdded) addAppid = true;
+                    add = 1;
+                    break;
+                case 1: // header long int
+                    add = 1 + rd.nextInt(29);
+                    break;
+                default: // header string
+                    add = 2 + rd.nextInt(10);
+                    break;
+                }
+                if (index + add >= len) break;
+
+                // fill header name
+                switch (type) {
+                case 0: // header short integer
+                    if (!addAppid) {
+                        do {
+                            arr[index] = (byte) (0x80 | rd.nextInt(128));
+                        } while (arr[index] == (byte) 0xaf);
+                    } else {
+                        Log.d(LOG_TAG, "appId added.");
+                        arr[index] = (byte) 0xaf;
+                        // if noAppId case, appId fld must be decieved.
+                        if (noAppId) arr[index]++;
+                    }
+                    break;
+                case 1: // header long int
+                    arr[index] = (byte) (add - 1);
+                    tmpVal2 = 0;
+                    for (i = 1; i < add; i++) {
+                        tmpVal = rd.nextInt(255);
+                        tmpVal2 = (tmpVal2 << 8) | tmpVal;
+                        arr[index + i] = (byte) tmpVal;
+                    }
+                    // don't set application id
+                    if (tmpVal2 == 0x2f) arr[index + 1]++;
+                    break;
+                default: // header string
+                    for (i = 0; i < add - 1; i++) {
+                        tmpVal = rd.nextInt(127);
+                        if (tmpVal < 32) tmpVal= (32 + tmpVal);
+                        arr[index + i] = (byte) tmpVal;
+                    }
+                    arr[index + i] = (byte) 0x0;
+                    break;
+                }
+
+                if (LOCAL_LOGV) {
+                    Log.d(LOG_TAG, "field name index:" + index);
+                    Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+                    if (type != 2) {
+                        for (i = index; i< index + add; i++) {
+                            System.out.print(Integer.toHexString(0xff & arr[i]));
+                            System.out.print(' ');
+                        }
+                    } else {
+                        System.out.print(Integer.toHexString(0xff & arr[index]));
+                        System.out.print(' ');
+                        String str = new String(arr, index + 1, add - 2);
+                        for (i = 0; i < str.length(); i++) {
+                            System.out.print(str.charAt(i));
+                            System.out.print(' ');
+                        }
+                    }
+                    System.out.print('\n');
+                }
+                index += add;
+
+
+                /*
+                 * field value
+                 * 0: short int
+                 * 1: long int
+                 * 2: text
+                 * 3: uint
+                 */
+                if (addAppid) {
+                    type = 1;
+                } else {
+                    type = rd.nextInt(4);
+                }
+                switch (type) {
+                case 0: // header short integer
+                    add = 1;
+                    break;
+                case 1: // header long int
+                    if (addAppid) {
+                        int bit = 1;
+                        int topBit = 0;
+
+                        for (i = 0; i < 31; i++) {
+                            if ((mAppIdValue & bit) > 0) topBit = i;
+                            bit = (bit << 1);
+                        }
+                        add = 2 + topBit/8;
+                    } else {
+                        add = 1 + rd.nextInt(29);
+                    }
+                    break;
+                case 2: // header string
+                    add = 2 + rd.nextInt(10);
+                    break;
+                default: // uint32
+                    add = 6;
+                }
+                if (index + add >= len) break;
+
+                // fill field value
+                switch (type) {
+                case 0: // header short int
+                    arr[index] = (byte) (0x80 | rd.nextInt(128));
+                    break;
+                case 1: // header long int
+                    if (addAppid) {
+                        addAppid = false;
+                        appIdAdded = true;
+
+                        arr[index] = (byte) (add - 1);
+                        tmpVal = mAppIdValue;
+                        for (i = add; i > 1; i--) {
+                            arr[index + i - 1] = (byte) (tmpVal & 0xff);
+                            tmpVal = (tmpVal >> 8);
+                        }
+                    } else {
+                        arr[index] = (byte) (add - 1);
+                        for (i = 1; i < add; i++) {
+                            arr[index + i] = (byte) rd.nextInt(255);
+                        }
+                    }
+                    break;
+                case 2:// header string
+                    for (i = 0; i < add - 1; i++) {
+                        tmpVal = rd.nextInt(127);
+                        if (tmpVal < 32) tmpVal= (32 + tmpVal);
+                        arr[index + i] = (byte) tmpVal;
+                    }
+                    arr[index + i] = (byte) 0x0;
+                    break;
+                default: // header uvarint
+                    arr[index] = (byte) 31;
+                    tmpVal = rd.nextInt(0x0FFFFFFF);
+                    add = 1 + encodeUint32(tmpVal, null, index + 1);
+                    encodeUint32(tmpVal, arr, index + 1);
+                    break;
+
+                }
+
+                if (LOCAL_LOGV) {
+                    Log.d(LOG_TAG, "field value index:" + index);
+                    Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+                    if (type != 2) {
+                        for (i = index; i< index + add; i++) {
+                            System.out.print(Integer.toHexString(0xff & arr[i]));
+                            System.out.print(' ');
+                        }
+                    } else {
+                        System.out.print(Integer.toHexString(0xff & arr[index]));
+                        System.out.print(' ');
+                        String str = new String(arr, index + 1, add - 2);
+                        for (i = 0; i < str.length(); i++) {
+                            System.out.print(str.charAt(i));
+                            System.out.print(' ');
+                        }
+                    }
+                    System.out.print('\n');
+                }
+                index += add;
+            }
+            if (noAppId) break;
+        }
+
+        Log.d(LOG_TAG, HexDump.dumpHexString(arr));
+    }
+
+    /**
+     * Generate Random WSP header with string application ID
+     */
+    protected void createRandomWspHeaderStrAppId(byte[] arr, Random rd, int headerStart,
+            boolean randomStr) {
+
+        boolean appIdAdded = false;
+
+        Log.d(LOG_TAG, "random arr length:" + arr.length);
+        String typename[] = new String[] { "short int", "long int", "string", "uint32"};
+
+        while (!appIdAdded) {
+            int type;
+            int index = headerStart;
+            int len = arr.length;
+            int i;
+            boolean addAppid = false;
+            int tmpVal = 0;
+            int tmpVal2 = 0;
+
+            while (true) {
+                int add;
+
+                /*
+                 * field name
+                 * 0: short int
+                 * 1: long int
+                 * 2: text
+                 * (no uint for param value)
+                 */
+                type = rd.nextInt(3);
+                switch (type) {
+                case 0: // header short integer
+                    if (index > 100 && !appIdAdded) addAppid = true;
+                    add = 1;
+                    break;
+                case 1: // header long int
+                    add = 1 + rd.nextInt(29);
+                    break;
+                default: // header string
+                    add = 2 + rd.nextInt(10);
+                    break;
+                }
+                if (index + add >= len) break;
+
+                // fill header name
+                switch (type) {
+                case 0: // header short integer
+                    if (!addAppid) {
+                        do {
+                            arr[index] = (byte) (0x80 | rd.nextInt(128));
+                        } while (arr[index] == (byte) 0xaf);
+                    } else {
+                        Log.d(LOG_TAG, "appId added.");
+                        arr[index] = (byte) 0xaf;
+                    }
+                    break;
+                case 1: // header long int
+                    arr[index] = (byte) (add - 1);
+                    tmpVal2 = 0;
+                    for (i = 1; i < add; i++) {
+                        tmpVal = rd.nextInt(255);
+                        tmpVal2 = (tmpVal2 << 8) | tmpVal;
+                        arr[index + i] = (byte) tmpVal;
+                    }
+                    // don't set application id
+                    if (tmpVal2 == 0x2f) arr[index + 1]++;
+                    break;
+                default: // header string
+                    for (i = 0; i < add - 1; i++) {
+                        tmpVal = rd.nextInt(127);
+                        if (tmpVal < 32) tmpVal= (32 + tmpVal);
+                        arr[index + i] = (byte) tmpVal;
+                    }
+                    arr[index + i] = (byte) 0x0;
+                    break;
+                }
+
+                if (LOCAL_LOGV) {
+                    Log.d(LOG_TAG, "field name index:" + index);
+                    Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+                    if (type != 2) {
+                        for (i = index; i < index + add; i++) {
+                            System.out.print(Integer.toHexString(0xff & arr[i]));
+                            System.out.print(' ');
+                        }
+                    } else {
+                        System.out.print(Integer.toHexString(0xff & arr[index]));
+                        System.out.print(' ');
+                        String str = new String(arr, index + 1, add - 2);
+                        for (i = 0; i < str.length(); i++) {
+                            System.out.print(str.charAt(i));
+                            System.out.print(' ');
+                        }
+                    }
+                    System.out.print('\n');
+                }
+                index += add;
+
+
+                /*
+                 * field value
+                 * 0: short int
+                 * 1: long int
+                 * 2: text
+                 * 3: uint
+                 */
+                if (addAppid) {
+                    type = 2;
+                } else {
+                    type = rd.nextInt(4);
+                }
+                switch (type) {
+                case 0: // header short integer
+                    add = 1;
+                    break;
+                case 1: // header long int
+                    add = 1 + rd.nextInt(29);
+                    break;
+                case 2: // header string
+                    if (addAppid) {
+                        if (randomStr) {
+                            add = 1 + rd.nextInt(10);
+                            byte[] randStr= new byte[add];
+                            for (i = 0; i < add; i++) {
+                                tmpVal = rd.nextInt(127);
+                                if (tmpVal < 32) tmpVal= (32 + tmpVal);
+                                randStr[i] = (byte) tmpVal;
+                            }
+                            mAppIdName = new String(randStr);
+                        }
+                        add = mAppIdName.length() + 1;
+                    } else {
+                        add = 2 + rd.nextInt(10);
+                    }
+                    break;
+                default: // uint32
+                    add = 6;
+                }
+                if (index + add >= len) break;
+
+                // fill field value
+                switch (type) {
+                case 0: // header short int
+                    arr[index] = (byte) (0x80 | rd.nextInt(128));
+                    break;
+                case 1: // header long int
+                    arr[index] = (byte) (add - 1);
+                    for (i = 1; i < add; i++)
+                        arr[index + i] = (byte) rd.nextInt(255);
+                    break;
+                case 2:// header string
+                    if (addAppid) {
+                        addAppid = false;
+                        appIdAdded = true;
+                        for (i = 0; i < add - 1; i++) {
+                            arr[index + i] = (byte) (mAppIdName.charAt(i));
+                        }
+                        Log.d(LOG_TAG, "mAppIdName added [" + mAppIdName + "]");
+                    } else {
+                        for (i = 0; i < add - 1; i++) {
+                            tmpVal = rd.nextInt(127);
+                            if (tmpVal < 32) tmpVal= (32 + tmpVal);
+                            arr[index + i] = (byte) tmpVal;
+                        }
+                    }
+                    arr[index + i] = (byte) 0x0;
+                    break;
+                default: // header uvarint
+                    arr[index] = (byte) 31;
+                    tmpVal = rd.nextInt(0x0FFFFFFF);
+                    add = 1 + encodeUint32(tmpVal, null, index + 1);
+                    encodeUint32(tmpVal, arr, index + 1);
+                    break;
+
+                }
+
+                if (LOCAL_LOGV) {
+                    Log.d(LOG_TAG, "field value index:" + index);
+                    Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+                    if (type != 2) {
+                        for (i = index; i < index + add; i++) {
+                            System.out.print(Integer.toHexString(0xff & arr[i]));
+                            System.out.print(' ');
+                        }
+                    } else {
+                        System.out.print(Integer.toHexString(0xff & arr[index]));
+                        System.out.print(' ');
+                        String str = new String(arr, index + 1, add - 2);
+                        for (i = 0; i < str.length(); i++) {
+                            System.out.print(str.charAt(i));
+                            System.out.print(' ');
+                        }
+                    }
+                    System.out.print('\n');
+                }
+                index += add;
+            }
+        }
+
+        Log.d(LOG_TAG, "headerStart = " + headerStart + ", mAppIdName = " + mAppIdName);
+        Log.d(LOG_TAG, HexDump.dumpHexString(arr));
+    }
+
+    protected byte[] createPDU(int testNum) {
+        byte[] array = null;
+        // byte[] wsp = null;
+
+        switch (testNum) {
+            // sample pdu
+        case 1:
+            byte[] array1 = {
+                    (byte) 0x00, // TID
+                    (byte) 0x06, // Type = wap push
+                    (byte) 0x00, // Length to be set later.
+
+                    // Content-Type
+                    (byte) 0x03, (byte) 0x02,
+                    (byte) ((mContentTypeValue >> 8) & 0xff),
+                    (byte) (mContentTypeValue & 0xff),
+
+                    // Application-id
+                    (byte) 0xaf, (byte) 0x02,
+                    (byte) ((mAppIdValue >> 8) & 0xff),
+                    (byte) (mAppIdValue& 0xff)
+            };
+            array1[2] = (byte) (array1.length - 3);
+            mWspHeader = array1;
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + 7;
+            mWspHeaderLen = array1.length;
+            break;
+
+            // invalid wsp header
+        case 2:
+            byte[] array2 = {
+                    (byte) 0x00, // invalid data
+            };
+            mWspHeader = array2;
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length;
+            mWspHeaderLen = array2.length;
+            break;
+
+            // random wsp header
+        case 3:
+            Random rd = new Random();
+            int arrSize = 150 + rd.nextInt(100);
+            byte[] array3 = new byte[arrSize];
+            int hdrEncodeLen;
+
+            array3[0] = (byte) 0x0;
+            array3[1] = (byte) 0x6;
+            hdrEncodeLen = encodeUint32(array3.length, null, 2);
+            hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+            array3[hdrEncodeLen + 2] = (byte) 0x3;
+            array3[hdrEncodeLen + 3] = (byte) 0x2;
+            array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+            array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+            createRandomWspHeader(array3, rd, hdrEncodeLen + 6, false);
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+            mWspHeaderLen = array3.length;
+
+            Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+                    + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+            mWspHeader = array3;
+            break;
+
+            // random wsp header w/o appid
+        case 4:
+            rd = new Random();
+            arrSize = 150 + rd.nextInt(100);
+            array3 = new byte[arrSize];
+
+            array3[0] = (byte) 0x0;
+            array3[1] = (byte) 0x6;
+            hdrEncodeLen = encodeUint32(array3.length, null, 2);
+            hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+            array3[hdrEncodeLen + 2] = (byte) 0x3;
+            array3[hdrEncodeLen + 3] = (byte) 0x2;
+            array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+            array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+            createRandomWspHeader(array3, rd, hdrEncodeLen + 6, true);
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+            mWspHeaderLen = array3.length;
+
+            Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+                    + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+            mWspHeader = array3;
+            break;
+
+            // random wsp header w/ random appid string
+        case 5:
+            rd = new Random();
+            arrSize = 150 + rd.nextInt(100);
+            array3 = new byte[arrSize];
+
+            array3[0] = (byte) 0x0;
+            array3[1] = (byte) 0x6;
+            hdrEncodeLen = encodeUint32(array3.length, null, 2);
+            hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+            array3[hdrEncodeLen + 2] = (byte) 0x3;
+            array3[hdrEncodeLen + 3] = (byte) 0x2;
+            array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+            array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+            createRandomWspHeaderStrAppId(array3, rd, hdrEncodeLen + 6, true);
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+            mWspHeaderLen = array3.length;
+
+            Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+                    + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+            mWspHeader = array3;
+            break;
+
+            // random wsp header w/ OMA appid string
+        case 6:
+            rd = new Random();
+            arrSize = 150 + rd.nextInt(100);
+            array3 = new byte[arrSize];
+
+            array3[0] = (byte) 0x0;
+            array3[1] = (byte) 0x6;
+            hdrEncodeLen = encodeUint32(array3.length, null, 2);
+            hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+            array3[hdrEncodeLen + 2] = (byte) 0x3;
+            array3[hdrEncodeLen + 3] = (byte) 0x2;
+            array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+            array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+            createRandomWspHeaderStrAppId(array3, rd, hdrEncodeLen + 6, false);
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+            mWspHeaderLen = array3.length;
+
+            Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+                    + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+            mWspHeader = array3;
+            break;
+
+            // random wsp header w/ OMA content type
+        case 7:
+            rd = new Random();
+            arrSize = 150 + rd.nextInt(100);
+            array3 = new byte[arrSize];
+
+            array3[0] = (byte) 0x0;
+            array3[1] = (byte) 0x6;
+            hdrEncodeLen = encodeUint32(array3.length, null, 2);
+            hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+
+            // encode content type
+            int contentLen = mContentTypeName.length();
+            int next = 2 + hdrEncodeLen;
+            mWspContentTypeStart = mGsmHeader.length + mUserDataHeader.length + next;
+            // next += encodeUint32(contentLen, array3, next);
+            int i;
+            Log.d(LOG_TAG, "mContentTypeName = " + mContentTypeName
+                    + ", contentLen = " + contentLen);
+
+            for (i = 0; i < contentLen; i++) {
+                array3[next + i] = (byte) mContentTypeName.charAt(i);
+            }
+            array3[next + i] = (byte) 0x0;
+
+            createRandomWspHeader(array3, rd, next + contentLen + 1, false);
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length
+                    + next + contentLen + 1;
+            mWspHeaderLen = array3.length;
+
+            mWspHeader = array3;
+            break;
+
+            // random wsp header w/ OMA content type, OMA app ID
+        case 8:
+            rd = new Random();
+            arrSize = 150 + rd.nextInt(100);
+            array3 = new byte[arrSize];
+
+            array3[0] = (byte) 0x0;
+            array3[1] = (byte) 0x6;
+            hdrEncodeLen = encodeUint32(array3.length, null, 2);
+            hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+
+            // encode content type
+            contentLen = mContentTypeName.length();
+            next = 2 + hdrEncodeLen;
+            mWspContentTypeStart = mGsmHeader.length + mUserDataHeader.length + next;
+            // next += encodeUint32(contentLen, array3, next);
+            Log.d(LOG_TAG, "mContentTypeName = " + mContentTypeName
+                    + ", contentLen = " + contentLen);
+
+            for (i = 0; i < contentLen; i++) {
+                array3[next + i] = (byte) mContentTypeName.charAt(i);
+            }
+            array3[next + i] = (byte) 0x0;
+
+            createRandomWspHeaderStrAppId(array3, rd, next + contentLen + 1, false);
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length
+                    + next + contentLen + 1;
+            mWspHeaderLen = array3.length;
+
+            mWspHeader = array3;
+            break;
+
+        default:
+            return null;
+        }
+        array = new byte[mGsmHeader.length + mUserDataHeader.length + mWspHeader.length
+                + mMessageBody.length];
+        System.arraycopy(mGsmHeader, 0, array, 0, mGsmHeader.length);
+        System.arraycopy(mUserDataHeader, 0, array,
+                mGsmHeader.length, mUserDataHeader.length);
+        System.arraycopy(mWspHeader, 0, array,
+                mGsmHeader.length + mUserDataHeader.length, mWspHeader.length);
+        System.arraycopy(mMessageBody, 0, array,
+                mGsmHeader.length + mUserDataHeader.length + mWspHeader.length, 
+                mMessageBody.length);
+        return array;
+
+    }
+
+    Intent createIntent(int pduType, int tranId) {
+        Intent intent = new Intent();
+        intent.putExtra("transactionId", tranId);
+        intent.putExtra("pduType", pduType);
+        intent.putExtra("header", mGsmHeader);
+        intent.putExtra("data", mMessageBody);
+        // intent.putExtra("contentTypeParameters", null);
+        return intent;
+    }
+
+    /**
+     * Message processing test, start activity
+     */
+    public void testProcessMsg1() {
+        byte[] pdu = createPDU(1);
+        int headerLen = pdu.length -
+                (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+        int pduType = 6;
+        int tranId = 0;
+        String originalPackageName = mPackageName;
+        String originalClassName = mClassName;
+
+        try {
+
+            mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+
+            // set up data
+            IWapPushManager iwapman = getInterface();
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+            assertTrue((iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId))
+                    & WapPushManagerParams.MESSAGE_HANDLED) ==
+                    WapPushManagerParams.MESSAGE_HANDLED);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mPackageName = originalPackageName;
+        mClassName = originalClassName;
+    }
+
+    /**
+     * Message processing test, start service
+     */
+    public void testProcessMsg2() {
+        byte[] pdu = createPDU(1);
+        int headerLen = pdu.length - (mGsmHeader.length +
+                mUserDataHeader.length + mMessageBody.length);
+        int pduType = 6;
+        int tranId = 0;
+        String originalPackageName = mPackageName;
+        String originalClassName = mClassName;
+
+        try {
+
+            mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+            // set up data
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+            assertTrue((iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId))
+                    & WapPushManagerParams.MESSAGE_HANDLED) ==
+                    WapPushManagerParams.MESSAGE_HANDLED);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mPackageName = originalPackageName;
+        mClassName = originalClassName;
+    }
+
+    /**
+     * Message processing test, no signature
+     */
+    public void testProcessMsg3() {
+        byte[] pdu = createPDU(1);
+        int headerLen = pdu.length -
+                (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+        int pduType = 6;
+        int tranId = 0;
+        String originalPackageName = mPackageName;
+        String originalClassName = mClassName;
+
+        try {
+
+            mPackageName = "com.android.development";
+            mClassName = "com.android.development.Development";
+
+            // set up data
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_SERVICE, true, false);
+
+            assertFalse((iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId))
+                    & WapPushManagerParams.MESSAGE_HANDLED) ==
+                    WapPushManagerParams.MESSAGE_HANDLED);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mPackageName = originalPackageName;
+        mClassName = originalClassName;
+    }
+
+    IDataVerify getVerifyInterface() {
+        while (mIVerify == null) {
+            // wait for the activity receive data.
+            try {
+                Thread.sleep(TIME_WAIT);
+            } catch (InterruptedException e) {}
+        }
+        return mIVerify;
+    }
+
+
+    /**
+     * Message processing test, received body data verification test
+     */
+    public void testProcessMsg4() {
+        byte[] originalMessageBody = mMessageBody;
+        mMessageBody = new byte[] {
+                (byte) 0xee,
+                (byte) 0xff,
+                (byte) 0xee,
+                (byte) 0xff,
+                (byte) 0xee,
+                (byte) 0xff,
+                (byte) 0xee,
+                (byte) 0xff,
+                (byte) 0xee,
+                (byte) 0xff,
+                (byte) 0xee,
+                (byte) 0xff,
+        };
+
+        byte[] pdu = createPDU(1);
+        int headerLen = pdu.length -
+                (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+        int pduType = 6;
+        int tranId = 0;
+        String originalPackageName = mPackageName;
+        String originalClassName = mClassName;
+
+        try {
+            IWapPushManager iwapman = getInterface();
+            IDataVerify dataverify = getVerifyInterface();
+
+            dataverify.resetData();
+
+            // set up data
+            mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+            iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId));
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+            assertTrue(dataverify.verifyData(mMessageBody));
+
+            // set up data
+            dataverify.resetData();
+            mClassName = "com.android.smspush.unitTests.ReceiverService";
+            mMessageBody = new byte[] {
+                    (byte) 0xaa,
+                    (byte) 0xbb,
+                    (byte) 0x11,
+                    (byte) 0x22,
+                    (byte) 0xaa,
+                    (byte) 0xbb,
+                    (byte) 0x11,
+                    (byte) 0x22,
+                    (byte) 0xaa,
+                    (byte) 0xbb,
+                    (byte) 0x11,
+                    (byte) 0x22,
+                    (byte) 0xaa,
+                    (byte) 0xbb,
+                    (byte) 0x11,
+                    (byte) 0x22,
+                    (byte) 0xaa,
+                    (byte) 0xbb,
+                    (byte) 0x11,
+                    (byte) 0x22,
+                    (byte) 0xaa,
+                    (byte) 0xbb,
+                    (byte) 0x11,
+                    (byte) 0x22,
+            };
+            pdu = createPDU(1);
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+            iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId));
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+            // Log.d(LOG_TAG, HexDump.dumpHexString(mMessageBody));
+            assertTrue(dataverify.verifyData(mMessageBody));
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mPackageName = originalPackageName;
+        mClassName = originalClassName;
+        mMessageBody = originalMessageBody;
+    }
+
+    /**
+     * Message processing test, send invalid sms data
+     */
+    public void testProcessMsg5() {
+        byte[] pdu = createPDU(2);
+        int headerLen = pdu.length -
+                (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+        int pduType = 6;
+        int tranId = 0;
+        String originalPackageName = mPackageName;
+        String originalClassName = mClassName;
+
+        try {
+
+            mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+
+            // set up data
+            IWapPushManager iwapman = getInterface();
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+            assertTrue((iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId))
+                    & WapPushManagerParams.MESSAGE_HANDLED) ==
+                    WapPushManagerParams.MESSAGE_HANDLED);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mPackageName = originalPackageName;
+        mClassName = originalClassName;
+    }
+
+    /**
+     * Message processing test, no receiver application
+     */
+    public void testProcessMsg6() {
+        byte[] pdu = createPDU(1);
+        int headerLen = pdu.length -
+                (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+        int pduType = 6;
+        int tranId = 0;
+        String originalPackageName = mPackageName;
+        String originalClassName = mClassName;
+
+        try {
+
+            mClassName = "com.android.smspush.unitTests.NoReceiver";
+
+            // set up data
+            IWapPushManager iwapman = getInterface();
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+            assertFalse((iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId))
+                    & WapPushManagerParams.MESSAGE_HANDLED) ==
+                    WapPushManagerParams.MESSAGE_HANDLED);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+            // set up data
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+            assertFalse((iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId))
+                    & WapPushManagerParams.MESSAGE_HANDLED) ==
+                    WapPushManagerParams.MESSAGE_HANDLED);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mPackageName = originalPackageName;
+        mClassName = originalClassName;
+    }
+
+    /**
+     * WspTypeDecoder test, normal pdu
+     */
+    public void testDecoder1() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        Random rd = new Random();
+
+        for (int i = 0; i < 10; i++) {
+            mAppIdValue = rd.nextInt(0xFFFF);
+            byte[] pdu = createPDU(1);
+            WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+            res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+                    mWspHeaderStart + mWspHeaderLen - 1);
+            assertTrue(res);
+
+            int index = (int) pduDecoder.getValue32();
+            res = pduDecoder.decodeXWapApplicationId(index);
+            assertTrue(res);
+
+            Log.d(LOG_TAG, "mAppIdValue: " + mAppIdValue
+                    + ", val: " + pduDecoder.getValue32());
+            assertTrue(mAppIdValue == (int) pduDecoder.getValue32());
+        }
+
+        mAppIdValue = originalAppIdValue;
+    }
+
+    /**
+     * WspTypeDecoder test, no header
+     */
+    public void testDecoder2() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        Random rd = new Random();
+
+        mAppIdValue = rd.nextInt(0xFFFF);
+        byte[] pdu = createPDU(2);
+        WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+        res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+                mWspHeaderStart + mWspHeaderLen - 1);
+        assertFalse(res);
+
+        mAppIdValue = originalAppIdValue;
+    }
+
+    /**
+     * WspTypeDecoder test, decode appid test
+     */
+    public void testDecoder3() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        Random rd = new Random();
+
+        for (int i = 0; i < 100; i++) {
+            mAppIdValue = rd.nextInt(0x0FFFFFFF);
+            mContentTypeValue = rd.nextInt(0x0FFF);
+            byte[] pdu = createPDU(3);
+            WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+            res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+                    mWspHeaderStart + mWspHeaderLen - 1);
+            assertTrue(res);
+
+            int index = (int) pduDecoder.getValue32();
+            res = pduDecoder.decodeXWapApplicationId(index);
+            assertTrue(res);
+
+            Log.d(LOG_TAG, "mAppIdValue: " + mAppIdValue
+                    + ", val: " + pduDecoder.getValue32());
+            assertTrue(mAppIdValue == (int) pduDecoder.getValue32());
+        }
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /*
+      public void testEnc() {
+      byte[] arr = new byte[20];
+      int index = 0;
+      index += encodeUint32(0x87a5, arr, index);
+      index += encodeUint32(0x1, arr, index);
+      index += encodeUint32(0x9b, arr, index);
+      index += encodeUint32(0x10, arr, index);
+      index += encodeUint32(0xe0887, arr, index);
+      index += encodeUint32(0x791a23d0, arr, index);
+
+      Log.d(LOG_TAG, HexDump.dumpHexString(arr));
+      }
+    */
+
+    /**
+     * WspTypeDecoder test, no appid test
+     */
+    public void testDecoder4() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        Random rd = new Random();
+
+        for (int i = 0; i < 100; i++) {
+            mAppIdValue = rd.nextInt(0x0FFFFFFF);
+            mContentTypeValue = rd.nextInt(0x0FFF);
+            byte[] pdu = createPDU(4);
+            WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+            res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+                    mWspHeaderStart + mWspHeaderLen - 1);
+            assertFalse(res);
+
+        }
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /**
+     * WspTypeDecoder test, decode string appid test
+     */
+    public void testDecoder5() {
+        boolean res;
+        String originalAppIdName = mAppIdName;
+        int originalContentTypeValue  = mContentTypeValue;
+        Random rd = new Random();
+
+        for (int i = 0; i < 10; i++) {
+            mAppIdValue = rd.nextInt(0x0FFFFFFF);
+            mContentTypeValue = rd.nextInt(0x0FFF);
+            byte[] pdu = createPDU(5);
+            WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+            res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+                    mWspHeaderStart + mWspHeaderLen - 1);
+            assertTrue(res);
+
+            int index = (int) pduDecoder.getValue32();
+            res = pduDecoder.decodeXWapApplicationId(index);
+            assertTrue(res);
+
+            Log.d(LOG_TAG, "mAppIdValue: [" + mAppIdName + "], val: ["
+                    + pduDecoder.getValueString() + "]");
+            assertTrue(mAppIdName.equals(pduDecoder.getValueString()));
+        }
+
+        mAppIdName = originalAppIdName;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /**
+     * WspTypeDecoder test, decode string appid test
+     */
+    public void testDecoder6() {
+        boolean res;
+        String originalAppIdName = mAppIdName;
+        int originalContentTypeValue  = mContentTypeValue;
+        Random rd = new Random();
+
+        for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length; i++) {
+            mAppIdName = OMA_APPLICATION_ID_NAMES[i];
+            mContentTypeValue = rd.nextInt(0x0FFF);
+            byte[] pdu = createPDU(6);
+            WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+            res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+                    mWspHeaderStart + mWspHeaderLen - 1);
+            assertTrue(res);
+
+            int index = (int) pduDecoder.getValue32();
+            res = pduDecoder.decodeXWapApplicationId(index);
+            assertTrue(res);
+
+            Log.d(LOG_TAG, "mAppIdValue: [" + mAppIdName + "], val: ["
+                    + pduDecoder.getValueString() + "]");
+            assertTrue(mAppIdName.equals(pduDecoder.getValueString()));
+        }
+
+        mAppIdName = originalAppIdName;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /**
+     * WspTypeDecoder test, decode OMA content type
+     */
+    public void testDecoder7() {
+        boolean res;
+        String originalAppIdName = mAppIdName;
+        int originalContentTypeValue  = mContentTypeValue;
+        Random rd = new Random();
+
+        for (int i = 0; i < OMA_CONTENT_TYPE_NAMES.length; i++) {
+            mContentTypeName = OMA_CONTENT_TYPE_NAMES[i];
+            byte[] pdu = createPDU(7);
+            WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+            res = pduDecoder.decodeContentType(mWspContentTypeStart);
+            assertTrue(res);
+
+            Log.d(LOG_TAG, "mContentTypeName: [" + mContentTypeName + "], val: ["
+                    + pduDecoder.getValueString() + "]");
+            assertTrue(mContentTypeName.equals(pduDecoder.getValueString()));
+        }
+
+        mAppIdName = originalAppIdName;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+
+    /**
+     * Copied from WapPushOverSms.
+     * The code flow is not changed from the original.
+     */
+    public int dispatchWapPdu(byte[] pdu, IWapPushManager wapPushMan) {
+
+        if (Config.DEBUG) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
+
+        int index = 0;
+        int transactionId = pdu[index++] & 0xFF;
+        int pduType = pdu[index++] & 0xFF;
+        int headerLength = 0;
+
+        if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
+                (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
+            if (Config.DEBUG) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
+            return Intents.RESULT_SMS_HANDLED;
+        }
+
+        WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+        /**
+         * Parse HeaderLen(unsigned integer).
+         * From wap-230-wsp-20010705-a section 8.1.2
+         * The maximum size of a uintvar is 32 bits.
+         * So it will be encoded in no more than 5 octets.
+         */
+        if (pduDecoder.decodeUintvarInteger(index) == false) {
+            if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Length error.");
+            return Intents.RESULT_SMS_GENERIC_ERROR;
+        }
+        headerLength = (int) pduDecoder.getValue32();
+        index += pduDecoder.getDecodedDataLength();
+
+        int headerStartIndex = index;
+
+        /**
+         * Parse Content-Type.
+         * From wap-230-wsp-20010705-a section 8.4.2.24
+         *
+         * Content-type-value = Constrained-media | Content-general-form
+         * Content-general-form = Value-length Media-type
+         * Media-type = (Well-known-media | Extension-Media) *(Parameter)
+         * Value-length = Short-length | (Length-quote Length)
+         * Short-length = <Any octet 0-30>   (octet <= WAP_PDU_SHORT_LENGTH_MAX)
+         * Length-quote = <Octet 31>         (WAP_PDU_LENGTH_QUOTE)
+         * Length = Uintvar-integer
+         */
+        if (pduDecoder.decodeContentType(index) == false) {
+            if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
+            return Intents.RESULT_SMS_GENERIC_ERROR;
+        }
+
+        String mimeType = pduDecoder.getValueString();
+        long binaryContentType = pduDecoder.getValue32();
+        index += pduDecoder.getDecodedDataLength();
+
+        byte[] header = new byte[headerLength];
+        System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
+
+        byte[] intentData;
+
+        if (mimeType != null && 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);
+        }
+
+        /**
+         * Seek for application ID field in WSP header.
+         * If application ID is found, WapPushManager substitute the message
+         * processing. Since WapPushManager is optional module, if WapPushManager
+         * is not found, legacy message processing will be continued.
+         */
+        if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) {
+            index = (int) pduDecoder.getValue32();
+            pduDecoder.decodeXWapApplicationId(index);
+            String wapAppId = pduDecoder.getValueString();
+            if (wapAppId == null) {
+                wapAppId = Integer.toString((int) pduDecoder.getValue32());
+            }
+
+            String contentType = ((mimeType == null) ?
+                    Long.toString(binaryContentType) : mimeType);
+            if (Config.DEBUG) Log.v(LOG_TAG, "appid found: " + wapAppId + ":" + contentType);
+
+            try {
+                boolean processFurther = true;
+                // IWapPushManager wapPushMan = mWapConn.getWapPushManager();
+                if (wapPushMan == null) {
+                    if (Config.DEBUG) Log.w(LOG_TAG, "wap push manager not found!");
+                } else {
+                    Intent intent = new Intent();
+                    intent.putExtra("transactionId", transactionId);
+                    intent.putExtra("pduType", pduType);
+                    intent.putExtra("header", header);
+                    intent.putExtra("data", intentData);
+                    intent.putExtra("contentTypeParameters",
+                            pduDecoder.getContentParameters());
+
+                    int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
+                    if (Config.DEBUG) Log.v(LOG_TAG, "procRet:" + procRet);
+                    if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0
+                            && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) {
+                        processFurther = false;
+                    }
+                }
+                if (!processFurther) {
+                    return Intents.RESULT_SMS_HANDLED;
+                }
+            } catch (RemoteException e) {
+                if (Config.DEBUG) Log.w(LOG_TAG, "remote func failed...");
+            }
+        }
+        if (Config.DEBUG) Log.v(LOG_TAG, "fall back to existing handler");
+
+        return Activity.RESULT_OK;
+    }
+
+    protected byte[] retrieveWspBody() {
+        byte[] array = new byte[mWspHeader.length + mMessageBody.length];
+
+        System.arraycopy(mWspHeader, 0, array, 0, mWspHeader.length);
+        System.arraycopy(mMessageBody, 0, array, mWspHeader.length, mMessageBody.length);
+        return array;
+    }
+
+    protected String getContentTypeName(int ctypeVal) {
+        int i;
+
+        for (i = 0; i < OMA_CONTENT_TYPE_VALUES.length; i++) {
+            if (ctypeVal == OMA_CONTENT_TYPE_VALUES[i]) {
+                return OMA_CONTENT_TYPE_NAMES[i];
+            }
+        }
+        return null;
+    }
+
+    protected boolean isContentTypeMapped(int ctypeVal) {
+        int i;
+
+        for (i = 0; i < OMA_CONTENT_TYPE_VALUES.length; i++) {
+            if (ctypeVal == OMA_CONTENT_TYPE_VALUES[i]) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Integration test 1, simple case
+     */
+    public void testIntegration1() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        String originalAppIdName = mAppIdName;
+        String originalContentTypeName = mContentTypeName;
+        String originalClassName = mClassName;
+        byte[] originalMessageBody = mMessageBody;
+        Random rd = new Random();
+
+        mMessageBody = new byte[100 + rd.nextInt(100)];
+        rd.nextBytes(mMessageBody);
+
+        byte[] pdu = createPDU(1);
+        byte[] wappushPdu = retrieveWspBody();
+
+
+        mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+        // Phone dummy = new DummyPhone(getContext());
+        // Phone gsm = PhoneFactory.getGsmPhone();
+        // GSMPhone gsm = new GSMPhone(getContext(), new SimulatedCommands(), null, true);
+        // WapPushOverSms dispatcher = new WapPushOverSms(dummy, null);
+
+        try {
+            // set up data
+            IWapPushManager iwapman = getInterface();
+            IDataVerify dataverify = getVerifyInterface();
+
+            dataverify.resetData();
+
+            if (isContentTypeMapped(mContentTypeValue)) {
+                // content type is mapped
+                mContentTypeName = getContentTypeName(mContentTypeValue);
+                Log.d(LOG_TAG, "mContentTypeValue mapping "
+                        + mContentTypeName + ":" + mContentTypeValue);
+            } else {
+                mContentTypeName = Integer.toString(mContentTypeValue);
+            }
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    mContentTypeName, mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+            dispatchWapPdu(wappushPdu, iwapman);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    mContentTypeName, mPackageName, mClassName);
+
+            assertTrue(dataverify.verifyData(mMessageBody));
+        } catch (RemoteException e) {
+        }
+
+
+        mClassName = originalClassName;
+        mAppIdName = originalAppIdName;
+        mContentTypeName = originalContentTypeName;
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        mMessageBody = originalMessageBody;
+    }
+
+    /**
+     * Integration test 2, random mAppIdValue(int), all OMA content type
+     */
+    public void testIntegration2() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        String originalAppIdName = mAppIdName;
+        String originalContentTypeName = mContentTypeName;
+        String originalClassName = mClassName;
+        byte[] originalMessageBody = mMessageBody;
+        Random rd = new Random();
+
+        IWapPushManager iwapman = getInterface();
+        IDataVerify dataverify = getVerifyInterface();
+        mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+
+        for (int i = 0; i < OMA_CONTENT_TYPE_NAMES.length; i++) {
+            mContentTypeName = OMA_CONTENT_TYPE_NAMES[i];
+            mAppIdValue = rd.nextInt(0x0FFFFFFF);
+
+            mMessageBody = new byte[100 + rd.nextInt(100)];
+            rd.nextBytes(mMessageBody);
+
+            byte[] pdu = createPDU(7);
+            byte[] wappushPdu = retrieveWspBody();
+
+            try {
+                dataverify.resetData();
+                // set up data
+                iwapman.addPackage(Integer.toString(mAppIdValue),
+                        mContentTypeName, mPackageName, mClassName,
+                        WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+                dispatchWapPdu(wappushPdu, iwapman);
+
+                // clean up data
+                iwapman.deletePackage(Integer.toString(mAppIdValue),
+                        mContentTypeName, mPackageName, mClassName);
+
+                if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+                    assertTrue(dataverify.verifyData(wappushPdu));
+                } else {
+                    assertTrue(dataverify.verifyData(mMessageBody));
+                }
+            } catch (RemoteException e) {
+            }
+        }
+
+
+        mClassName = originalClassName;
+        mAppIdName = originalAppIdName;
+        mContentTypeName = originalContentTypeName;
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        mMessageBody = originalMessageBody;
+    }
+
+    /**
+     * Integration test 3, iterate OmaApplication ID, random binary content type
+     */
+    public void testIntegration3() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        String originalAppIdName = mAppIdName;
+        String originalContentTypeName = mContentTypeName;
+        String originalClassName = mClassName;
+        byte[] originalMessageBody = mMessageBody;
+        Random rd = new Random();
+
+        IWapPushManager iwapman = getInterface();
+        IDataVerify dataverify = getVerifyInterface();
+        mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+        for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length; i++) {
+            mAppIdName = OMA_APPLICATION_ID_NAMES[i];
+            mContentTypeValue = rd.nextInt(0x0FFF);
+
+            mMessageBody = new byte[100 + rd.nextInt(100)];
+            rd.nextBytes(mMessageBody);
+
+            byte[] pdu = createPDU(6);
+            byte[] wappushPdu = retrieveWspBody();
+
+            try {
+                dataverify.resetData();
+                // set up data
+                if (isContentTypeMapped(mContentTypeValue)) {
+                    // content type is mapped to integer value
+                    mContentTypeName = getContentTypeName(mContentTypeValue);
+                    Log.d(LOG_TAG, "mContentTypeValue mapping "
+                            + mContentTypeValue + ":" + mContentTypeName);
+                } else {
+                    mContentTypeName = Integer.toString(mContentTypeValue);
+                }
+
+                iwapman.addPackage(mAppIdName,
+                        mContentTypeName, mPackageName, mClassName,
+                        WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+                dispatchWapPdu(wappushPdu, iwapman);
+
+                // clean up data
+                iwapman.deletePackage(mAppIdName,
+                        mContentTypeName, mPackageName, mClassName);
+
+                if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+                    assertTrue(dataverify.verifyData(wappushPdu));
+                } else {
+                    assertTrue(dataverify.verifyData(mMessageBody));
+                }
+            } catch (RemoteException e) {
+            }
+        }
+
+        mClassName = originalClassName;
+        mAppIdName = originalAppIdName;
+        mContentTypeName = originalContentTypeName;
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        mMessageBody = originalMessageBody;
+    }
+
+    /**
+     * Integration test 4, iterate OmaApplication ID, Oma content type
+     */
+    public void testIntegration4() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        String originalAppIdName = mAppIdName;
+        String originalContentTypeName = mContentTypeName;
+        String originalClassName = mClassName;
+        byte[] originalMessageBody = mMessageBody;
+        Random rd = new Random();
+
+        IWapPushManager iwapman = getInterface();
+        IDataVerify dataverify = getVerifyInterface();
+        mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+        for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length
+                + OMA_CONTENT_TYPE_NAMES.length; i++) {
+            mAppIdName = OMA_APPLICATION_ID_NAMES[rd.nextInt(OMA_APPLICATION_ID_NAMES.length)];
+            int contIndex = rd.nextInt(OMA_CONTENT_TYPE_NAMES.length);
+            mContentTypeName = OMA_CONTENT_TYPE_NAMES[contIndex];
+
+            mMessageBody = new byte[100 + rd.nextInt(100)];
+            rd.nextBytes(mMessageBody);
+
+            byte[] pdu = createPDU(8);
+            byte[] wappushPdu = retrieveWspBody();
+
+            try {
+                dataverify.resetData();
+                // set up data
+                iwapman.addPackage(mAppIdName,
+                        mContentTypeName, mPackageName, mClassName,
+                        WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+                dispatchWapPdu(wappushPdu, iwapman);
+
+                // clean up data
+                iwapman.deletePackage(mAppIdName,
+                        mContentTypeName, mPackageName, mClassName);
+
+                if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+                    assertTrue(dataverify.verifyData(wappushPdu));
+                } else {
+                    assertTrue(dataverify.verifyData(mMessageBody));
+                }
+            } catch (RemoteException e) {
+            }
+        }
+
+        mClassName = originalClassName;
+        mAppIdName = originalAppIdName;
+        mContentTypeName = originalContentTypeName;
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        mMessageBody = originalMessageBody;
+    }
+
+    /**
+     * Integration test 5, iterate binary OmaApplication ID, Oma binary content type
+     */
+    public void testIntegration5() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        String originalAppIdName = mAppIdName;
+        String originalContentTypeName = mContentTypeName;
+        String originalClassName = mClassName;
+        byte[] originalMessageBody = mMessageBody;
+        Random rd = new Random();
+
+        IWapPushManager iwapman = getInterface();
+        IDataVerify dataverify = getVerifyInterface();
+        mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+        for (int i = 0; i < OMA_APPLICATION_ID_VALUES.length +
+                    OMA_CONTENT_TYPE_VALUES.length; i++) {
+            mAppIdValue = OMA_APPLICATION_ID_VALUES[rd.nextInt(
+                    OMA_APPLICATION_ID_VALUES.length)];
+            mContentTypeValue =
+                    OMA_CONTENT_TYPE_VALUES[rd.nextInt(OMA_CONTENT_TYPE_VALUES.length)];
+
+            mMessageBody = new byte[100 + rd.nextInt(100)];
+            rd.nextBytes(mMessageBody);
+
+            byte[] pdu = createPDU(3);
+            byte[] wappushPdu = retrieveWspBody();
+
+            try {
+                dataverify.resetData();
+                // set up data
+                if (isContentTypeMapped(mContentTypeValue)) {
+                    // content type is mapped to integer value
+                    mContentTypeName = getContentTypeName(mContentTypeValue);
+                    Log.d(LOG_TAG, "mContentTypeValue mapping "
+                            + mContentTypeValue + ":" + mContentTypeName);
+                } else {
+                    mContentTypeName = Integer.toString(mContentTypeValue);
+                }
+
+                iwapman.addPackage(Integer.toString(mAppIdValue),
+                        mContentTypeName, mPackageName, mClassName,
+                        WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+                dispatchWapPdu(wappushPdu, iwapman);
+
+                // clean up data
+                iwapman.deletePackage(Integer.toString(mAppIdValue),
+                        mContentTypeName, mPackageName, mClassName);
+
+                if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+                    assertTrue(dataverify.verifyData(wappushPdu));
+                } else {
+                    assertTrue(dataverify.verifyData(mMessageBody));
+                }
+            } catch (RemoteException e) {
+            }
+        }
+
+        mClassName = originalClassName;
+        mAppIdName = originalAppIdName;
+        mContentTypeName = originalContentTypeName;
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        mMessageBody = originalMessageBody;
+    }
+
+}
diff --git a/telephony/java/com/android/internal/telephony/IWapPushManager.aidl b/telephony/java/com/android/internal/telephony/IWapPushManager.aidl
new file mode 100644
index 0000000..d5ecb94
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IWapPushManager.aidl
@@ -0,0 +1,52 @@
+/*
+ * 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 android.content.Intent;
+
+interface IWapPushManager {
+    /**
+     * Processes WAP push message and triggers the receiver application registered
+     * in the application ID table.
+     */
+    int processMessage(String app_id, String content_type, in Intent intent);
+
+    /**
+     * Add receiver application into the application ID table.
+     * Returns true if inserting the information is successfull. Inserting the duplicated
+     * record in the application ID table is not allowed. Use update/delete method.
+     */
+    boolean addPackage(String x_app_id, String content_type,
+            String package_name, String class_name,
+            int app_type, boolean need_signature, boolean further_processing);
+
+    /**
+     * Updates receiver application that is last added.
+     * Returns true if updating the information is successfull.
+     */
+    boolean updatePackage(String x_app_id, String content_type,
+            String package_name, String class_name,
+            int app_type, boolean need_signature, boolean further_processing);
+
+    /**
+     * Delites receiver application information.
+     * Returns true if deleting is successfull.
+     */
+    boolean deletePackage(String x_app_id, String content_type,
+                            String package_name, String class_name);
+}
+
diff --git a/telephony/java/com/android/internal/telephony/WapPushManagerParams.java b/telephony/java/com/android/internal/telephony/WapPushManagerParams.java
new file mode 100644
index 0000000..11e5ff9
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/WapPushManagerParams.java
@@ -0,0 +1,70 @@
+/*
+ * 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;
+
+/**
+ * WapPushManager constant value definitions
+ */
+public class WapPushManagerParams {
+    /**
+     * Application type activity
+     */
+    public static final int APP_TYPE_ACTIVITY = 0;
+
+    /**
+     * Application type service
+     */
+    public static final int APP_TYPE_SERVICE = 1;
+
+    /**
+     * Process Message return value
+     * Message is handled
+     */
+    public static final int MESSAGE_HANDLED = 0x1;
+
+    /**
+     * Process Message return value
+     * Application ID or content type was not found in the application ID table
+     */
+    public static final int APP_QUERY_FAILED = 0x2;
+
+    /**
+     * Process Message return value
+     * Receiver application signature check failed
+     */
+    public static final int SIGNATURE_NO_MATCH = 0x4;
+
+    /**
+     * Process Message return value
+     * Receiver application was not found
+     */
+    public static final int INVALID_RECEIVER_NAME = 0x8;
+
+    /**
+     * Process Message return value
+     * Unknown exception
+     */
+    public static final int EXCEPTION_CAUGHT = 0x10;
+
+    /**
+     * Process Message return value
+     * Need further processing after WapPushManager message processing
+     */
+    public static final int FURTHER_PROCESSING = 0x8000;
+
+}
+
diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
index 2f5d3ec..7704667 100644
--- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
@@ -14,15 +14,20 @@
  * limitations under the License.
  */
 
+
 package com.android.internal.telephony;
 
 import android.app.Activity;
 import android.content.Context;
+import android.content.ComponentName;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.provider.Telephony;
 import android.provider.Telephony.Sms.Intents;
 import android.util.Config;
 import android.util.Log;
+import android.os.IBinder;
+import android.os.RemoteException;
 
 /**
  * WAP push handler class.
@@ -42,11 +47,83 @@
      */
     private final int WAKE_LOCK_TIMEOUT = 5000;
 
+    private final int BIND_RETRY_INTERVAL = 1000;
+    /**
+     * A handle to WapPushManager interface
+     */
+    private WapPushConnection mWapConn = null;
+    private class WapPushConnection implements ServiceConnection {
+        private IWapPushManager mWapPushMan;
+        private Context mOwner;
+
+        public WapPushConnection(Context ownerContext) {
+            mOwner = ownerContext;
+        }
+
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mWapPushMan = IWapPushManager.Stub.asInterface(service);
+            if (Config.DEBUG) Log.v(LOG_TAG, "wappush manager connected to " +
+                    mOwner.hashCode());
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            mWapPushMan = null;
+            if (Config.DEBUG) Log.v(LOG_TAG, "wappush manager disconnected.");
+            // WapPushManager must be always attached.
+            rebindWapPushManager();
+        }
+
+        /**
+         * bind WapPushManager
+         */
+        public void bindWapPushManager() {
+            if (mWapPushMan != null) return;
+
+            final ServiceConnection wapPushConnection = this;
+
+            mOwner.bindService(new Intent(IWapPushManager.class.getName()),
+                    wapPushConnection, Context.BIND_AUTO_CREATE);
+        }
+
+        /**
+         * rebind WapPushManager
+         * This method is called when WapPushManager is disconnected unexpectedly.
+         */
+        private void rebindWapPushManager() {
+            if (mWapPushMan != null) return;
+
+            final ServiceConnection wapPushConnection = this;
+            new Thread() {
+                public void run() {
+                    while (mWapPushMan == null) {
+                        mOwner.bindService(new Intent(IWapPushManager.class.getName()),
+                                wapPushConnection, Context.BIND_AUTO_CREATE);
+                        try {
+                            Thread.sleep(BIND_RETRY_INTERVAL);
+                        } catch (InterruptedException e) {
+                            if (Config.DEBUG) Log.v(LOG_TAG, "sleep interrupted.");
+                        }
+                    }
+                }
+            }.start();
+        }
+
+        /**
+         * Returns interface to WapPushManager
+         */
+        public IWapPushManager getWapPushManager() {
+            return mWapPushMan;
+        }
+    }
+
     public WapPushOverSms(Phone phone, SMSDispatcher smsDispatcher) {
         mSmsDispatcher = smsDispatcher;
         mContext = phone.getContext();
+        mWapConn = new WapPushConnection(mContext);
+        mWapConn.bindWapPushManager();
     }
 
+
     /**
      * Dispatches inbound messages that are in the WAP PDU format. See
      * wap-230-wsp-20010705-a section 8 for details on the WAP PDU format.
@@ -106,16 +183,15 @@
         }
 
         String mimeType = pduDecoder.getValueString();
-
+        long binaryContentType = pduDecoder.getValue32();
         index += pduDecoder.getDecodedDataLength();
 
         byte[] header = new byte[headerLength];
         System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
 
         byte[] intentData;
-        String permission;
 
-        if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+        if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
             intentData = pdu;
         } else {
             int dataIndex = headerStartIndex + headerLength;
@@ -123,6 +199,62 @@
             System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
         }
 
+        /**
+         * Seek for application ID field in WSP header.
+         * If application ID is found, WapPushManager substitute the message
+         * processing. Since WapPushManager is optional module, if WapPushManager
+         * is not found, legacy message processing will be continued.
+         */
+        if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) {
+            index = (int) pduDecoder.getValue32();
+            pduDecoder.decodeXWapApplicationId(index);
+            String wapAppId = pduDecoder.getValueString();
+            if (wapAppId == null) {
+                wapAppId = Integer.toString((int) pduDecoder.getValue32());
+            }
+
+            String contentType = ((mimeType == null) ?
+                                  Long.toString(binaryContentType) : mimeType);
+            if (Config.DEBUG) Log.v(LOG_TAG, "appid found: " + wapAppId + ":" + contentType);
+
+            try {
+                boolean processFurther = true;
+                IWapPushManager wapPushMan = mWapConn.getWapPushManager();
+
+                if (wapPushMan == null) {
+                    if (Config.DEBUG) Log.w(LOG_TAG, "wap push manager not found!");
+                } else {
+                    Intent intent = new Intent();
+                    intent.putExtra("transactionId", transactionId);
+                    intent.putExtra("pduType", pduType);
+                    intent.putExtra("header", header);
+                    intent.putExtra("data", intentData);
+                    intent.putExtra("contentTypeParameters",
+                            pduDecoder.getContentParameters());
+
+                    int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
+                    if (Config.DEBUG) Log.v(LOG_TAG, "procRet:" + procRet);
+                    if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0
+                        && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) {
+                        processFurther = false;
+                    }
+                }
+                if (!processFurther) {
+                    return Intents.RESULT_SMS_HANDLED;
+                }
+            } catch (RemoteException e) {
+                if (Config.DEBUG) Log.w(LOG_TAG, "remote func failed...");
+            }
+        }
+        if (Config.DEBUG) Log.v(LOG_TAG, "fall back to existing handler");
+
+        if (mimeType == null) {
+            if (Config.DEBUG) Log.w(LOG_TAG, "Header Content-Type error.");
+            return Intents.RESULT_SMS_GENERIC_ERROR;
+        }
+
+        String permission;
+
         if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_MMS)) {
             permission = "android.permission.RECEIVE_MMS";
         } else {
@@ -141,4 +273,4 @@
 
         return Activity.RESULT_OK;
     }
-}
\ 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 6bf6b13..c8dd718 100644
--- a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
+++ b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
@@ -37,6 +37,7 @@
     private final static HashMap<Integer, String> WELL_KNOWN_PARAMETERS =
             new HashMap<Integer, String>();
 
+    public static final int PARAMETER_ID_X_WAP_APPLICATION_ID = 0x2f;
     private static final int Q_VALUE = 0x00;
 
     static {
@@ -603,6 +604,70 @@
     }
 
     /**
+     * Seek for the "X-Wap-Application-Id" field for WSP pdu
+     *
+     * @param startIndex The starting position of seek pointer
+     * @param endIndex Valid seek area end point
+     *
+     * @return false when error(not a X-Wap-Application-Id) occur
+     *         return value can be retrieved by getValue32()
+     */
+    public boolean seekXWapApplicationId(int startIndex, int endIndex) {
+        int index = startIndex;
+
+        try {
+            for (index = startIndex; index <= endIndex; ) {
+                /**
+                 * 8.4.1.1  Field name
+                 * Field name is integer or text.
+                 */
+                if (decodeIntegerValue(index)) {
+                    int fieldValue = (int) getValue32();
+
+                    if (fieldValue == PARAMETER_ID_X_WAP_APPLICATION_ID) {
+                        unsigned32bit = index + 1;
+                        return true;
+                    }
+                } else {
+                    if (!decodeTextString(index)) return false;
+                }
+                index += getDecodedDataLength();
+                if (index > endIndex) return false;
+
+                /**
+                 * 8.4.1.2 Field values
+                 * Value Interpretation of First Octet
+                 * 0 - 30 This octet is followed by the indicated number (0 - 30)
+                        of data octets
+                 * 31 This octet is followed by a uintvar, which indicates the number
+                 *      of data octets after it
+                 * 32 - 127 The value is a text string, terminated by a zero octet
+                        (NUL character)
+                 * 128 - 255 It is an encoded 7-bit value; this header has no more data
+                 */
+                byte val = wspData[index];
+                if (0 <= val && val <= WAP_PDU_SHORT_LENGTH_MAX) {
+                    index += wspData[index] + 1;
+                } else if (val == WAP_PDU_LENGTH_QUOTE) {
+                    if (index + 1 >= endIndex) return false;
+                    index++;
+                    if (!decodeUintvarInteger(index)) return false;
+                    index += getDecodedDataLength();
+                } else if (WAP_PDU_LENGTH_QUOTE < val && val <= 127) {
+                    if (!decodeTextString(index)) return false;
+                    index += getDecodedDataLength();
+                } else {
+                    index++;
+                }
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            //seek application ID failed. WSP header might be corrupted
+            return false;
+        }
+        return false;
+    }
+
+    /**
      * Decode the "X-Wap-Content-URI" type for WSP pdu
      *
      * @param startIndex The starting position of the "X-Wap-Content-URI" in this pdu
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index c5aa573..15570e4 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -45,7 +45,7 @@
           mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
           mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
           mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL),
-          mMaxResVersion(NULL), mDebugMode(false), mProduct(NULL),
+          mMaxResVersion(NULL), mDebugMode(false), mNonConstantId(false), mProduct(NULL),
           mArgc(0), mArgv(NULL)
         {}
     ~Bundle(void) {}
@@ -139,6 +139,8 @@
     void setMaxResVersion(const char * val) { mMaxResVersion = val; }
     bool getDebugMode() { return mDebugMode; }
     void setDebugMode(bool val) { mDebugMode = val; }
+    bool getNonConstantId() { return mNonConstantId; }
+    void setNonConstantId(bool val) { mNonConstantId = val; }
     const char* getProduct() const { return mProduct; }
     void setProduct(const char * val) { mProduct = val; }
 
@@ -239,6 +241,7 @@
     const char* mCustomPackage;
     const char* mMaxResVersion;
     bool        mDebugMode;
+    bool        mNonConstantId;
     const char* mProduct;
 
     /* file specification */
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 739b01f..266a02f 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -160,7 +160,11 @@
         "       product variants\n"
         "   --utf16\n"
         "       changes default encoding for resources to UTF-16.  Only useful when API\n"
-        "       level is set to 7 or higher where the default encoding is UTF-8.\n");
+        "       level is set to 7 or higher where the default encoding is UTF-8.\n"
+        "   --non-constant-id\n"
+        "       Make the resources ID non constant. This is required to make an R java class\n"
+        "       that does not contain the final value but is used to make reusable compiled\n"
+        "       libraries that need to access resources.\n");
 }
 
 /*
@@ -497,6 +501,8 @@
                         goto bail;
                     }
                     bundle.setProduct(argv[0]);
+                } else if (strcmp(cp, "-non-constant-id") == 0) {
+                    bundle.setNonConstantId(true);
                 } else {
                     fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
                     wantUsage = true;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 7f84df6..730bd71 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1723,7 +1723,8 @@
 
 static status_t writeSymbolClass(
     FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
-    const sp<AaptSymbols>& symbols, const String8& className, int indent)
+    const sp<AaptSymbols>& symbols, const String8& className, int indent,
+    bool nonConstantId)
 {
     fprintf(fp, "%spublic %sfinal class %s {\n",
             getIndentSpace(indent),
@@ -1733,6 +1734,10 @@
     size_t i;
     status_t err = NO_ERROR;
 
+    const char * id_format = nonConstantId ?
+            "%spublic static int %s=0x%08x;\n" :
+            "%spublic static final int %s=0x%08x;\n";
+
     size_t N = symbols->getSymbols().size();
     for (i=0; i<N; i++) {
         const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
@@ -1785,7 +1790,7 @@
         if (deprecated) {
             fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
         }
-        fprintf(fp, "%spublic static final int %s=0x%08x;\n",
+        fprintf(fp, id_format,
                 getIndentSpace(indent),
                 String8(name).string(), (int)sym.int32Val);
     }
@@ -1836,7 +1841,7 @@
         if (nclassName == "styleable") {
             styleableSymbols = nsymbols;
         } else {
-            err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent);
+            err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId);
         }
         if (err != NO_ERROR) {
             return err;
@@ -1907,7 +1912,7 @@
         "\n"
         "package %s;\n\n", package.string());
 
-        status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0);
+        status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0, bundle->getNonConstantId());
         if (err != NO_ERROR) {
             return err;
         }