Merge "Phone crash when old callback func is woken up when enabling BT."
diff --git a/Android.mk b/Android.mk
index e987eb7..f640e37 100644
--- a/Android.mk
+++ b/Android.mk
@@ -184,6 +184,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/content/res/Resources.java b/core/java/android/content/res/Resources.java
index a6513aa..e9a2929 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1352,7 +1352,6 @@
                 }
             }
         }
-        cache.clear();
     }
 
     /**
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 49b72fe..7d128ec 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -85,9 +85,9 @@
     // sometimes we store linked lists of these things
     /*package*/ Message next;
 
-    private static Object mPoolSync = new Object();
-    private static Message mPool;
-    private static int mPoolSize = 0;
+    private static final Object sPoolSync = new Object();
+    private static Message sPool;
+    private static int sPoolSize = 0;
 
     private static final int MAX_POOL_SIZE = 10;
     
@@ -96,11 +96,12 @@
      * avoid allocating new objects in many cases.
      */
     public static Message obtain() {
-        synchronized (mPoolSync) {
-            if (mPool != null) {
-                Message m = mPool;
-                mPool = m.next;
+        synchronized (sPoolSync) {
+            if (sPool != null) {
+                Message m = sPool;
+                sPool = m.next;
                 m.next = null;
+                sPoolSize--;
                 return m;
             }
         }
@@ -237,12 +238,12 @@
      * freed.
      */
     public void recycle() {
-        synchronized (mPoolSync) {
-            if (mPoolSize < MAX_POOL_SIZE) {
+        synchronized (sPoolSync) {
+            if (sPoolSize < MAX_POOL_SIZE) {
                 clearForRecycle();
-                
-                next = mPool;
-                mPool = this;
+                next = sPool;
+                sPool = this;
+                sPoolSize++;
             }
         }
     }
@@ -453,4 +454,3 @@
         replyTo = Messenger.readMessengerOrNullFromParcel(source);
     }
 }
-
diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java
index 95e54324..fae5f1a 100644
--- a/core/java/android/preference/PreferenceScreen.java
+++ b/core/java/android/preference/PreferenceScreen.java
@@ -80,6 +80,8 @@
     private ListAdapter mRootAdapter;
     
     private Dialog mDialog;
+
+    private ListView mListView;
     
     /**
      * Do NOT use this constructor, use {@link PreferenceManager#createPreferenceScreen(Context)}.
@@ -145,15 +147,18 @@
     
     private void showDialog(Bundle state) {
         Context context = getContext();
-        ListView listView = new ListView(context);
-        bind(listView);
+        if (mListView != null) {
+            mListView.setAdapter(null);
+        }
+        mListView = new ListView(context);
+        bind(mListView);
 
         // Set the title bar if title is available, else no title bar
         final CharSequence title = getTitle();
         Dialog dialog = mDialog = new Dialog(context, TextUtils.isEmpty(title)
                 ? com.android.internal.R.style.Theme_NoTitleBar
                 : com.android.internal.R.style.Theme);
-        dialog.setContentView(listView);
+        dialog.setContentView(mListView);
         if (!TextUtils.isEmpty(title)) {
             dialog.setTitle(title);
         }
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
old mode 100644
new mode 100755
index 803446f..1a35112
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -115,8 +115,8 @@
 
         public static final int STATUS_NONE = -1;
         public static final int STATUS_COMPLETE = 0;
-        public static final int STATUS_PENDING = 64;
-        public static final int STATUS_FAILED = 128;
+        public static final int STATUS_PENDING = 32;
+        public static final int STATUS_FAILED = 64;
 
         /**
          * The subject of the message, if present
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 26346d2..9f362d3 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -514,8 +514,11 @@
                         mLayout.windowAnimations =
                                 com.android.internal.R.style.Animation_Wallpaper;
                         mInputChannel = new InputChannel();
-                        mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets,
-                                mInputChannel);
+                        if (mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets,
+                                mInputChannel) < 0) {
+                            Log.w(TAG, "Failed to add window while updating wallpaper surface.");
+                            return;
+                        }
                         mCreated = true;
 
                         InputQueue.registerInputChannel(mInputChannel, mInputHandler,
diff --git a/core/java/android/text/InputFilter.java b/core/java/android/text/InputFilter.java
index 2f55677..8d4b08e 100644
--- a/core/java/android/text/InputFilter.java
+++ b/core/java/android/text/InputFilter.java
@@ -88,7 +88,14 @@
             } else if (keep >= end - start) {
                 return null; // keep original
             } else {
-                return source.subSequence(start, start + keep);
+                keep += start;
+                if (Character.isHighSurrogate(source.charAt(keep - 1))) {
+                    --keep;
+                    if (keep == start) {
+                        return "";
+                    }
+                }
+                return source.subSequence(start, keep);
             }
         }
 
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 60ca384..ceecbce 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/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 091844e..d6dcd4c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1050,14 +1050,6 @@
                 gravity = o.gravity;
                 changes |= LAYOUT_CHANGED;
             }
-            if (horizontalMargin != o.horizontalMargin) {
-                horizontalMargin = o.horizontalMargin;
-                changes |= LAYOUT_CHANGED;
-            }
-            if (verticalMargin != o.verticalMargin) {
-                verticalMargin = o.verticalMargin;
-                changes |= LAYOUT_CHANGED;
-            }
             if (format != o.format) {
                 format = o.format;
                 changes |= FORMAT_CHANGED;
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index 2a76e33..6095a64 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -234,6 +234,14 @@
         // high time constant.
         private static final float MAX_DEVIATION_FROM_GRAVITY = 1.5f;
 
+        // Minimum acceleration considered, in m/s^2. Below this threshold sensor noise will have
+        // significant impact on the calculations and in case of the vector (0, 0, 0) there is no
+        // defined rotation or tilt at all. Low or zero readings can happen when space travelling
+        // or free falling, but more commonly when shaking or getting bad readings from the sensor.
+        // The accelerometer is turned off when not used and polling it too soon after it is
+        // turned on may result in (0, 0, 0).
+        private static final float MIN_ABS_ACCELERATION = 1.5f;
+
         // Actual sampling period corresponding to SensorManager.SENSOR_DELAY_NORMAL.  There's no
         // way to get this information from SensorManager.
         // Note the actual period is generally 3-30ms larger than this depending on the device, but
@@ -347,6 +355,9 @@
             float deviation = Math.abs(magnitude - SensorManager.STANDARD_GRAVITY);
 
             handleAccelerationDistrust(deviation);
+            if (magnitude < MIN_ABS_ACCELERATION) {
+                return; // Ignore tilt and orientation when (0, 0, 0) or low reading
+            }
 
             // only filter tilt when we're accelerating
             float alpha = 1;
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index f54b207..e36602f 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -58,7 +58,11 @@
         // Load libwebcore during static initialization. This happens in the
         // zygote process so it will be shared read-only across all app
         // processes.
-        System.loadLibrary("webcore");
+        try {
+            System.loadLibrary("webcore");
+        } catch (UnsatisfiedLinkError e) {
+            Log.e(LOGTAG, "Unable to load webcore library");
+        }
     }
 
     /*
diff --git a/core/java/android/widget/EdgeGlow.java b/core/java/android/widget/EdgeGlow.java
index 416be86..a11de6f 100644
--- a/core/java/android/widget/EdgeGlow.java
+++ b/core/java/android/widget/EdgeGlow.java
@@ -317,6 +317,7 @@
                     mEdgeScaleY = mEdgeScaleYStart +
                         (mEdgeScaleYFinish - mEdgeScaleYStart) *
                             interp * factor;
+                    mState = STATE_RECEDE;
                     break;
                 case STATE_RECEDE:
                     mState = STATE_IDLE;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index ec7d927..2a695e5 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -666,8 +666,8 @@
 
             if (mProgress > max) {
                 mProgress = max;
-                refreshProgress(R.id.progress, mProgress, false);
             }
+            refreshProgress(R.id.progress, mProgress, false);
         }
     }
     
diff --git a/data/keyboards/AVRCP.kl b/data/keyboards/AVRCP.kl
new file mode 100644
index 0000000..aeb3c8a
--- /dev/null
+++ b/data/keyboards/AVRCP.kl
@@ -0,0 +1,7 @@
+key 200   MEDIA_PLAY_PAUSE    WAKE
+key 201   MEDIA_PLAY_PAUSE    WAKE
+key 166   MEDIA_STOP          WAKE
+key 163   MEDIA_NEXT          WAKE
+key 165   MEDIA_PREVIOUS      WAKE
+key 168   MEDIA_REWIND        WAKE
+key 208   MEDIA_FAST_FORWARD  WAKE
diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk
new file mode 100644
index 0000000..81ac530
--- /dev/null
+++ b/data/keyboards/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := qwerty.kcm
+include $(BUILD_KEY_CHAR_MAP)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := qwerty2.kcm
+include $(BUILD_KEY_CHAR_MAP)
+
+file := $(TARGET_OUT_KEYLAYOUT)/qwerty.kl
+ALL_PREBUILT += $(file)
+$(file): $(LOCAL_PATH)/qwerty.kl | $(ACP)
+	$(transform-prebuilt-to-target)
+
+file := $(TARGET_OUT_KEYLAYOUT)/AVRCP.kl
+ALL_PREBUILT += $(file)
+$(file) : $(LOCAL_PATH)/AVRCP.kl | $(ACP)
+	$(transform-prebuilt-to-target)
diff --git a/data/keyboards/qwerty.kcm b/data/keyboards/qwerty.kcm
new file mode 100644
index 0000000..8056364
--- /dev/null
+++ b/data/keyboards/qwerty.kcm
@@ -0,0 +1,64 @@
+[type=QWERTY]                                           
+                                                        
+# keycode       display number  base    caps    fn      caps_fn
+                                                        
+A               'A'     '2'     'a'     'A'     '#'     0x00
+B               'B'     '2'     'b'     'B'     '<'     0x00
+C               'C'     '2'     'c'     'C'     '9'     0x00E7
+D               'D'     '3'     'd'     'D'     '5'     0x00
+E               'E'     '3'     'e'     'E'     '2'     0x0301
+F               'F'     '3'     'f'     'F'     '6'     0x00A5
+G               'G'     '4'     'g'     'G'     '-'     '_'
+H               'H'     '4'     'h'     'H'     '['     '{'
+I               'I'     '4'     'i'     'I'     '$'     0x0302
+J               'J'     '5'     'j'     'J'     ']'     '}'
+K               'K'     '5'     'k'     'K'     '"'     '~'
+L               'L'     '5'     'l'     'L'     '''     '`'
+M               'M'     '6'     'm'     'M'     '!'     0x00
+N               'N'     '6'     'n'     'N'     '>'     0x0303
+O               'O'     '6'     'o'     'O'     '('     0x00
+P               'P'     '7'     'p'     'P'     ')'     0x00
+Q               'Q'     '7'     'q'     'Q'     '*'     0x0300
+R               'R'     '7'     'r'     'R'     '3'     0x20AC
+S               'S'     '7'     's'     'S'     '4'     0x00DF
+T               'T'     '8'     't'     'T'     '+'     0x00A3
+U               'U'     '8'     'u'     'U'     '&'     0x0308
+V               'V'     '8'     'v'     'V'     '='     '^'
+W               'W'     '9'     'w'     'W'     '1'     0x00
+X               'X'     '9'     'x'     'X'     '8'     0xEF00
+Y               'Y'     '9'     'y'     'Y'     '%'     0x00A1
+Z               'Z'     '9'     'z'     'Z'     '7'     0x00
+                                                        
+# on pc keyboards
+COMMA           ','     ','     ','     ';'     ';'     '|'
+PERIOD          '.'     '.'     '.'     ':'     ':'     0x2026
+AT              '@'     '0'     '@'     '0'     '0'     0x2022
+SLASH           '/'     '/'     '/'     '?'     '?'     '\'
+                                                        
+SPACE           0x20    0x20    0x20    0x20    0xEF01  0xEF01
+ENTER         0xa     0xa     0xa     0xa     0xa     0xa
+                                                        
+TAB             0x9     0x9     0x9     0x9     0x9     0x9
+0               '0'     '0'     '0'     ')'     ')'     ')'
+1               '1'     '1'     '1'     '!'     '!'     '!'
+2               '2'     '2'     '2'     '@'     '@'     '@'
+3               '3'     '3'     '3'     '#'     '#'     '#'
+4               '4'     '4'     '4'     '$'     '$'     '$'
+5               '5'     '5'     '5'     '%'     '%'     '%'
+6               '6'     '6'     '6'     '^'     '^'     '^'
+7               '7'     '7'     '7'     '&'     '&'     '&'
+8               '8'     '8'     '8'     '*'     '*'     '*'
+9               '9'     '9'     '9'     '('     '('     '('
+                                                        
+GRAVE           '`'     '`'     '`'     '~'     '`'     '~'
+MINUS           '-'     '-'     '-'     '_'     '-'     '_'
+EQUALS          '='     '='     '='     '+'     '='     '+'
+LEFT_BRACKET    '['     '['     '['     '{'     '['     '{'
+RIGHT_BRACKET   ']'     ']'     ']'     '}'     ']'     '}'
+BACKSLASH       '\'     '\'     '\'     '|'     '\'     '|'
+SEMICOLON       ';'     ';'     ';'     ':'     ';'     ':'
+APOSTROPHE      '''     '''     '''     '"'     '''     '"'
+STAR            '*'     '*'     '*'     '*'     '*'     '*'
+POUND           '#'     '#'     '#'     '#'     '#'     '#'
+PLUS            '+'     '+'     '+'     '+'     '+'     '+'
+                                                        
diff --git a/data/keyboards/qwerty.kl b/data/keyboards/qwerty.kl
new file mode 100644
index 0000000..201c798
--- /dev/null
+++ b/data/keyboards/qwerty.kl
@@ -0,0 +1,91 @@
+key 399   GRAVE
+key 2     1
+key 3     2
+key 4     3
+key 5     4
+key 6     5
+key 7     6
+key 8     7
+key 9     8
+key 10    9
+key 11    0
+key 158   BACK              WAKE_DROPPED
+key 230   SOFT_RIGHT        WAKE
+key 60    SOFT_RIGHT        WAKE
+key 107   ENDCALL           WAKE_DROPPED
+key 62    ENDCALL           WAKE_DROPPED
+key 229   MENU              WAKE_DROPPED
+key 139   MENU              WAKE_DROPPED
+key 59    MENU              WAKE_DROPPED
+key 127   SEARCH            WAKE_DROPPED
+key 217   SEARCH            WAKE_DROPPED
+key 228   POUND
+key 227   STAR
+key 231   CALL              WAKE_DROPPED
+key 61    CALL              WAKE_DROPPED
+key 232   DPAD_CENTER       WAKE_DROPPED
+key 108   DPAD_DOWN         WAKE_DROPPED
+key 103   DPAD_UP           WAKE_DROPPED
+key 102   HOME              WAKE
+key 105   DPAD_LEFT         WAKE_DROPPED
+key 106   DPAD_RIGHT        WAKE_DROPPED
+key 115   VOLUME_UP         WAKE
+key 114   VOLUME_DOWN       WAKE
+key 116   POWER             WAKE
+key 212   CAMERA
+
+key 16    Q
+key 17    W
+key 18    E
+key 19    R
+key 20    T
+key 21    Y
+key 22    U
+key 23    I
+key 24    O
+key 25    P
+key 26    LEFT_BRACKET
+key 27    RIGHT_BRACKET
+key 43    BACKSLASH
+
+key 30    A
+key 31    S
+key 32    D
+key 33    F
+key 34    G
+key 35    H
+key 36    J
+key 37    K
+key 38    L
+key 39    SEMICOLON
+key 40    APOSTROPHE
+key 14    DEL
+        
+key 44    Z
+key 45    X
+key 46    C
+key 47    V
+key 48    B
+key 49    N
+key 50    M
+key 51    COMMA
+key 52    PERIOD
+key 53    SLASH
+key 28    ENTER
+        
+key 56    ALT_LEFT
+key 100   ALT_RIGHT
+key 42    SHIFT_LEFT
+key 54    SHIFT_RIGHT
+key 15    TAB
+key 57    SPACE
+key 150   EXPLORER
+key 155   ENVELOPE        
+
+key 12    MINUS
+key 13    EQUALS
+key 215   AT
+
+# On an AT keyboard: ESC, F10
+key 1     BACK              WAKE_DROPPED
+key 68    MENU              WAKE_DROPPED
diff --git a/data/keyboards/qwerty2.kcm b/data/keyboards/qwerty2.kcm
new file mode 100644
index 0000000..1487fb7
--- /dev/null
+++ b/data/keyboards/qwerty2.kcm
@@ -0,0 +1,81 @@
+[type=QWERTY]                                           
+                                                        
+# this keymap is to be used in the emulator only. note
+# that I have liberally modified certain key strokes to
+# make it more usable in this context. the main differences
+# with the reference keyboard image are:
+#
+#  - cap-2 produces '@', and not '"', without that, typing
+#    email addresses becomes a major pain with a qwerty
+#    keyboard. note that you can type '"' with fn-E anyway.
+#
+#  - cap-COMMA and cap-PERIOD return '<' and '>', instead
+#    of nothing.
+#
+#
+                                                        
+# keycode       display  number base    caps    fn      caps_fn
+                                                        
+A               'A'     '2'     'a'     'A'     'a'     'A'
+B               'B'     '2'     'b'     'B'     'b'     'B'
+C               'C'     '2'     'c'     'C'     0x00e7  0x00E7
+D               'D'     '3'     'd'     'D'     '''     '''
+E               'E'     '3'     'e'     'E'     '"'     0x0301
+F               'F'     '3'     'f'     'F'     '['     '['
+G               'G'     '4'     'g'     'G'     ']'     ']'
+H               'H'     '4'     'h'     'H'     '<'     '<'
+I               'I'     '4'     'i'     'I'     '-'     0x0302
+J               'J'     '5'     'j'     'J'     '>'     '>'
+K               'K'     '5'     'k'     'K'     ';'     '~'
+L               'L'     '5'     'l'     'L'     ':'     '`'
+M               'M'     '6'     'm'     'M'     '%'     0x00
+N               'N'     '6'     'n'     'N'     0x00    0x0303
+O               'O'     '6'     'o'     'O'     '+'     '+'
+P               'P'     '7'     'p'     'P'     '='     0x00A5
+Q               'Q'     '7'     'q'     'Q'     '|'     0x0300
+R               'R'     '7'     'r'     'R'     '`'     0x20AC
+S               'S'     '7'     's'     'S'     '\'     0x00DF
+T               'T'     '8'     't'     'T'     '{'     0x00A3
+U               'U'     '8'     'u'     'U'     '_'     0x0308
+V               'V'     '8'     'v'     'V'     'v'     'V'
+W               'W'     '9'     'w'     'W'     '~'     '~'
+X               'X'     '9'     'x'     'X'     'x'     0xEF00
+Y               'Y'     '9'     'y'     'Y'     '}'     0x00A1
+Z               'Z'     '9'     'z'     'Z'     'z'     'Z'
+                                                        
+COMMA           ','     ','     ','     '<'     ','     ','
+PERIOD          '.'     '.'     '.'     '>'     '.'     0x2026
+AT              '@'     '@'     '@'     '@'     '@'     0x2022
+SLASH           '/'     '/'     '/'     '?'     '?'     '?'
+                                                        
+SPACE           0x20    0x20    0x20    0x20    0xEF01  0xEF01
+ENTER         0xa     0xa     0xa     0xa     0xa     0xa
+                                                        
+0               '0'     '0'     '0'     ')'     ')'     ')'
+1               '1'     '1'     '1'     '!'     '!'     '!'
+2               '2'     '2'     '2'     '@'     '@'     '@'
+3               '3'     '3'     '3'     '#'     '#'     '#'
+4               '4'     '4'     '4'     '$'     '$'     '$'
+5               '5'     '5'     '5'     '%'     '%'     '%'
+6               '6'     '6'     '6'     '^'     '^'     '^'
+7               '7'     '7'     '7'     '&'     '&'     '&'
+8               '8'     '8'     '8'     '*'     '*'     '*'
+9               '9'     '9'     '9'     '('     '('     '('
+                                                        
+# the rest is for a qwerty keyboard
+#
+TAB             0x9     0x9     0x9     0x9     0x9     0x9
+GRAVE           '`'     '`'     '`'     '~'     '`'     '~'
+MINUS           '-'     '-'     '-'     '_'     '-'     '_'
+EQUALS          '='     '='     '='     '+'     '='     '+'
+LEFT_BRACKET    '['     '['     '['     '{'     '['     '{'
+RIGHT_BRACKET   ']'     ']'     ']'     '}'     ']'     '}'
+BACKSLASH       '\'     '\'     '\'     '|'     '\'     '|'
+SEMICOLON       ';'     ';'     ';'     ':'     ';'     ':'
+APOSTROPHE      '''     '''     '''     '"'     '''     '"'
+STAR            '*'     '*'     '*'     '*'     '*'     '*'
+POUND           '#'     '#'     '#'     '#'     '#'     '#'
+PLUS            '+'     '+'     '+'     '+'     '+'     '+'
+                                                        
+                                                        
+                                                        
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 4f3b0da..03a653c 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -494,6 +494,7 @@
       <td>Keyboard availability</td>
       <td>
         <code>keysexposed</code><br/>
+        <code>keyshidden</code><br/>
         <code>keyssoft</code>
       </td>
       <td>
diff --git a/drm/libdrmframework/plugins/common/Android.mk b/drm/libdrmframework/plugins/common/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/Android.mk
@@ -0,0 +1,16 @@
+#
+# 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.
+#
+include $(call all-subdir-makefiles)
diff --git a/drm/libdrmframework/plugins/common/util/Android.mk b/drm/libdrmframework/plugins/common/util/Android.mk
new file mode 100644
index 0000000..15dda80
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/Android.mk
@@ -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.
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    src/MimeTypeUtil.cpp
+
+LOCAL_MODULE := libdrmutility
+
+LOCAL_SHARED_LIBRARIES :=  \
+    libutils \
+    libdl \
+    libdvm \
+    libandroid_runtime \
+    libnativehelper \
+    liblog
+
+
+base := frameworks/base
+
+LOCAL_C_INCLUDES += \
+    $(JNI_H_INCLUDE) \
+    $(base)/include \
+    $(base)/include/drm \
+    $(base)/include/drm/plugins \
+    $(LOCAL_PATH)/include
+
+
+ifneq ($(TARGET_BUILD_VARIANT),user)
+LOCAL_C_INCLUDES += \
+    $(LOCAL_PATH)/tools
+
+endif
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h b/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h
new file mode 100644
index 0000000..4d12a61
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef __MIMETYPEUTIL_H__
+#define __MIMETYPEUTIL_H__
+
+#include <utils/String8.h>
+
+namespace android {
+
+class MimeTypeUtil {
+
+public:
+
+    MimeTypeUtil() {}
+
+    virtual ~MimeTypeUtil() {}
+
+/**
+ * May convert the mimetype if there is a well known
+ * replacement mimetype otherwise the original mimetype
+ * is returned.
+ *
+ * @param mimeType - mimetype in lower case to convert.
+ *
+ * @return mimetype or null.
+ */
+static String8 convertMimeType(String8& mimeType);
+
+};
+};
+
+#endif /* __MIMETYPEUTIL_H__ */
diff --git a/drm/libdrmframework/plugins/common/util/include/SessionMap.h b/drm/libdrmframework/plugins/common/util/include/SessionMap.h
new file mode 100644
index 0000000..3dff58c
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/include/SessionMap.h
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+#ifndef __SESSIONMAP_H__
+#define __SESSIONMAP_H__
+
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+/**
+ * A wrapper template class for handling DRM Engine sessions.
+ */
+template <typename NODE>
+class SessionMap {
+
+public:
+    KeyedVector<int, NODE> map;
+
+    SessionMap() {}
+
+    virtual ~SessionMap() {
+        destroyMap();
+    }
+
+/**
+ * Adds a new value in the session map table. It expects memory to be allocated already
+ * for the session object
+ *
+ * @param key - key or Session ID
+ * @param value - session object to add
+ *
+ * @return boolean result of adding value. returns false if key is already exist.
+ */
+bool addValue(int key, NODE value) {
+    bool result = false;
+
+    if (!isCreated(key)) {
+        map.add(key, value);
+        result = true;
+    }
+
+    return result;
+}
+
+
+/**
+ * returns the session object by the key
+ *
+ * @param key - key or Session ID
+ *
+ * @return session object as per the key
+ */
+NODE getValue(int key) {
+    NODE value = NULL;
+
+    if (isCreated(key)) {
+        value = (NODE) map.valueFor(key);
+    }
+
+    return value;
+}
+
+/**
+ * returns the number of objects in the session map table
+ *
+ * @return count of number of session objects.
+ */
+int getSize() {
+    return map.size();
+}
+
+/**
+ * returns the session object by the index in the session map table
+ *
+ * @param index - index of the value required
+ *
+ * @return session object as per the index
+ */
+NODE getValueAt(unsigned int index) {
+    NODE value = NULL;
+
+    if (map.size() > index) {
+      value = map.valueAt(index);
+    }
+
+    return value;
+}
+
+/**
+ * deletes the object from session map. It also frees up memory for the session object.
+ *
+ * @param key - key of the value to be deleted
+ *
+ */
+void removeValue(int key) {
+    deleteValue(getValue(key));
+    map.removeItem(key);
+}
+
+/**
+ * decides if session is already created.
+ *
+ * @param key - key of the value for the session
+ *
+ * @return boolean result of whether session is created
+ */
+bool isCreated(int key) {
+    return (0 <= map.indexOfKey(key));
+}
+
+/**
+ * empty the entire session table. It releases all the memory for session objects.
+ */
+void destroyMap() {
+    int size = map.size();
+    int i = 0;
+
+    for (i = 0; i < size; i++) {
+        deleteValue(map.valueAt(i));
+    }
+
+    map.clear();
+}
+
+/**
+ * free up the memory for the session object.
+ * Make sure if any reference to the session object anywhere, otherwise it will be a
+ * dangle pointer after this call.
+ *
+ * @param value - session object to free
+ *
+ */
+void deleteValue(NODE value) {
+    delete value;
+}
+
+};
+
+};
+
+#endif /* __SESSIONMAP_H__ */
diff --git a/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp b/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp
new file mode 100644
index 0000000..4ee903e
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#include <MimeTypeUtil.h>
+#include <utils/Log.h>
+
+namespace android {
+
+#undef LOG_TAG
+#define LOG_TAG "MimeTypeUtil"
+
+enum {
+    MIMETYPE_AUDIO       = 0,
+    MIMETYPE_APPLICATION = 1,
+    MIMETYPE_IMAGE       = 2,
+    MIMETYPE_VIDEO       = 3,
+    MIMETYPE_LAST        = -1,
+};
+
+struct MimeGroup{
+    int         type;     // Audio, video,.. use the enum values
+    const char* pGroup;   // "audio/", "video/",.. should contain the last "/"
+    int         size;     // Number of bytes. e.g. "audio/" = 6 bytes
+};
+
+struct MimeTypeList{
+    int         type;
+    const char* pMimeExt;  // Everything after the '/' e.g. audio/x-mpeg -> "x-mpeg"
+    int         size;      // Number of bytes. e.g. "x-mpeg" = 6 bytes
+    const char* pMimeType; // Mimetype that should be returned
+};
+
+
+// Known mimetypes by android
+static const char mime_type_audio_mpeg[]  = "audio/mpeg";
+static const char mime_type_audio_3gpp[]  = "audio/3gpp";
+static const char mime_type_audio_amr[]   = "audio/amr-wb";
+static const char mime_type_audio_aac[]   = "audio/mp4a-latm";
+static const char mime_type_audio_wav[]   = "audio/wav";
+
+static const char mime_type_video_mpeg4[] = "video/mpeg4";
+static const char mime_type_video_3gpp[]  = "video/3gpp";
+
+// Known mimetype groups
+static const char mime_group_audio[]       = "audio/";
+static const char mime_group_application[] = "application/";
+static const char mime_group_image[]       = "image/";
+static const char mime_group_video[]       = "video/";
+
+static struct MimeGroup mimeGroup[] = {
+    {MIMETYPE_AUDIO,       mime_group_audio,        sizeof(mime_group_audio)-1},
+    {MIMETYPE_APPLICATION, mime_group_application,  sizeof(mime_group_application)-1},
+    {MIMETYPE_IMAGE,       mime_group_image,        sizeof(mime_group_image)-1},
+    {MIMETYPE_VIDEO,       mime_group_video,        sizeof(mime_group_video)-1},
+    {MIMETYPE_LAST,        NULL,                    0} // Must be last entry
+};
+
+// List of all mimetypes that should be converted.
+static struct MimeTypeList mimeTypeList[] = {
+    // Mp3 mime types
+    {MIMETYPE_AUDIO, "mp3",          sizeof("mp3")-1,         mime_type_audio_mpeg},
+    {MIMETYPE_AUDIO, "x-mpeg",       sizeof("x-mpeg")-1,      mime_type_audio_mpeg},
+    {MIMETYPE_AUDIO, "x-mp3",        sizeof("x-mp3")-1,       mime_type_audio_mpeg},
+    {MIMETYPE_AUDIO, "mpg",          sizeof("mpg")-1,         mime_type_audio_mpeg},
+    {MIMETYPE_AUDIO, "mpg3",         sizeof("mpg")-1,         mime_type_audio_mpeg},
+    {MIMETYPE_AUDIO, "x-mpg",        sizeof("x-mpg")-1,       mime_type_audio_mpeg},
+    {MIMETYPE_AUDIO, "x-mpegaudio",  sizeof("x-mpegaudio")-1, mime_type_audio_mpeg},
+
+    // 3gpp audio mime types
+    {MIMETYPE_AUDIO, "3gp",          sizeof("3gp")-1,         mime_type_audio_3gpp},
+
+    // Amr audio mime types
+    {MIMETYPE_AUDIO, "amr",          sizeof("amr")-1,         mime_type_audio_amr},
+
+    // Aac audio mime types
+    {MIMETYPE_AUDIO, "aac",          sizeof("aac")-1,         mime_type_audio_aac},
+
+    // Wav audio mime types
+    {MIMETYPE_AUDIO, "x-wav",        sizeof("x-wav")-1,       mime_type_audio_wav},
+
+    // Mpeg4 video mime types
+    {MIMETYPE_VIDEO, "mpg4",         sizeof("mpg4")-1,        mime_type_video_mpeg4},
+    {MIMETYPE_VIDEO, "mp4v-es",      sizeof("mp4v-es")-1,     mime_type_video_mpeg4},
+
+    // 3gpp video mime types
+    {MIMETYPE_VIDEO, "3gp",          sizeof("3gp")-1,         mime_type_video_3gpp},
+
+    // Must be last entry
+    {MIMETYPE_LAST,  NULL,           0,                       NULL}
+};
+
+/**
+ * May convert the mimetype if there is a well known
+ * replacement mimetype otherwise the original mimetype
+ * is returned.
+ *
+ * @param mimeType - mimetype in lower case to convert.
+ *
+ * @return mimetype or null.
+ */
+String8 MimeTypeUtil::convertMimeType(String8& mimeType) {
+    String8 result = mimeType;
+    const char* pTmp;
+    const char* pMimeType;
+    struct MimeGroup* pGroup;
+    struct MimeTypeList* pMimeItem;
+    int len;
+
+    pMimeType = mimeType.string();
+    if (NULL != pMimeType) {
+        /* Check which group the mimetype is */
+        pGroup = mimeGroup;
+
+        while (MIMETYPE_LAST != pGroup->type) {
+            if (0 == strncmp(pMimeType, pGroup->pGroup, pGroup->size)) {
+                break;
+            }
+            pGroup++;
+        }
+
+        /* Go through the mimetype list. Only check items of the correct group */
+        if (MIMETYPE_LAST != pGroup->type) {
+            pMimeItem = mimeTypeList;
+            len = strlen (pMimeType+pGroup->size);
+
+            while (MIMETYPE_LAST != pMimeItem->type) {
+                if ((len == pMimeItem->size) &&
+                    (0 == strcmp(pMimeType+pGroup->size, pMimeItem->pMimeExt))) {
+                    result = String8(pMimeItem->pMimeType);
+                    break;
+                }
+                pMimeItem++;
+            }
+        }
+        LOGI("convertMimeType got mimetype %s, converted into mimetype %s",
+             pMimeType, result.string());
+    }
+
+    return result;
+}
+};
diff --git a/drm/libdrmframework/plugins/forward-lock/Android.mk b/drm/libdrmframework/plugins/forward-lock/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/Android.mk
@@ -0,0 +1,16 @@
+#
+# 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.
+#
+include $(call all-subdir-makefiles)
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
new file mode 100644
index 0000000..d4a6f18
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
@@ -0,0 +1,67 @@
+#
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+base := frameworks/base
+
+# Determine whether the DRM framework uses 64-bit data types for file offsets and do the same.
+ifneq ($(shell grep -c 'off64_t offset' $(base)/drm/libdrmframework/plugins/common/include/IDrmEngine.h), 0)
+LOCAL_CFLAGS += -DUSE_64BIT_DRM_API
+endif
+
+LOCAL_SRC_FILES:= \
+    src/FwdLockEngine.cpp
+
+LOCAL_MODULE := libfwdlockengine
+
+LOCAL_SHARED_LIBRARIES := \
+    libicui18n \
+    libicuuc \
+    libutils \
+    libdl \
+    libandroid_runtime \
+    libnativehelper \
+    libcrypto \
+    libssl \
+    libdrmframework
+
+LOCAL_STATIC_LIBRARIES := \
+    libdrmutility \
+    libdrmframeworkcommon \
+    libfwdlock-common \
+    libfwdlock-converter \
+    libfwdlock-decoder
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_C_INCLUDES += \
+    $(JNI_H_INCLUDE) \
+    $(base)/include/drm \
+    $(base)/drm/libdrmframework/plugins/common/include \
+    $(base)/drm/libdrmframework/plugins/common/util/include \
+    $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/common \
+    $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/converter \
+    $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/decoder \
+    $(LOCAL_PATH)/include \
+    external/openssl/include
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/drm/plugins/native
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
new file mode 100644
index 0000000..34804cf
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
@@ -0,0 +1,559 @@
+/*
+ * 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.
+ */
+
+#ifndef __FWDLOCKENGINE_H__
+#define __FWDLOCKENGINE_H__
+
+#include <DrmEngineBase.h>
+#include <DrmConstraints.h>
+#include <DrmRights.h>
+#include <DrmInfo.h>
+#include <DrmInfoStatus.h>
+#include <DrmConvertedStatus.h>
+#include <DrmInfoRequest.h>
+#include <DrmSupportInfo.h>
+#include <DrmInfoEvent.h>
+
+#include "SessionMap.h"
+#include "FwdLockConv.h"
+
+namespace android {
+
+/**
+ * Forward Lock Engine class.
+ */
+class FwdLockEngine : public android::DrmEngineBase {
+
+public:
+    FwdLockEngine();
+    virtual ~FwdLockEngine();
+
+protected:
+/**
+ * Get constraint information associated with input content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @param action Actions defined such as,
+ *     Action::DEFAULT, Action::PLAY, etc
+ * @return DrmConstraints
+ *     key-value pairs of constraint are embedded in it
+ * @note
+ *     In case of error, return NULL
+ */
+DrmConstraints* onGetConstraints(int uniqueId, const String8* path, int action);
+
+/**
+ * Get metadata information associated with input content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @return DrmMetadata
+ *      For Forward Lock engine, it returns an empty object
+ * @note
+ *     In case of error, returns NULL
+ */
+DrmMetadata* onGetMetadata(int uniqueId, const String8* path);
+
+/**
+ * Initialize plug-in.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onInitialize(int uniqueId);
+
+/**
+ * Register a callback to be invoked when the caller required to
+ * receive necessary information.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param infoListener Listener
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener);
+
+/**
+ * Terminate the plug-in and release resources bound to it.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onTerminate(int uniqueId);
+
+/**
+ * Get whether the given content can be handled by this plugin or not.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path to the protected object
+ * @return bool
+ *      Returns true if this plugin can handle , false in case of not able to handle
+ */
+bool onCanHandle(int uniqueId, const String8& path);
+
+/**
+ * Processes the given DRM information as appropriate for its type.
+ * Not used for Forward Lock Engine.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param drmInfo Information that needs to be processed
+ * @return DrmInfoStatus
+ *      instance as a result of processing given input
+ */
+DrmInfoStatus* onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+/**
+ * Save DRM rights to specified rights path
+ * and make association with content path.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param drmRights DrmRights to be saved
+ * @param rightsPath File path where rights to be saved
+ * @param contentPath File path where content was saved
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onSaveRights(int uniqueId,
+                      const DrmRights& drmRights,
+                      const String8& rightsPath,
+                      const String8& contentPath);
+
+/**
+ * Retrieves necessary information for registration, unregistration or rights
+ * acquisition information.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param drmInfoRequest Request information to retrieve drmInfo
+ * @return DrmInfo
+ *      instance as a result of processing given input
+ */
+DrmInfo* onAcquireDrmInfo(int uniqueId,
+                          const DrmInfoRequest* drmInfoRequest);
+
+/**
+ * Retrieves the mime type embedded inside the original content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @return String8
+ *       Returns mime-type of the original content, such as "video/mpeg"
+ */
+String8 onGetOriginalMimeType(int uniqueId, const String8& path);
+
+/**
+ * Retrieves the type of the protected object (content, rights, etc..)
+ * using specified path or mimetype. At least one parameter should be non null
+ * to retrieve DRM object type.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the content or null.
+ * @param mimeType Mime type of the content or null.
+ * @return type of the DRM content,
+ *     such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
+ */
+int onGetDrmObjectType(int uniqueId,
+                       const String8& path,
+                       const String8& mimeType);
+
+/**
+ * Check whether the given content has valid rights or not.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @param action Action to perform (Action::DEFAULT, Action::PLAY, etc)
+ * @return the status of the rights for the protected content,
+ *     such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc.
+ */
+int onCheckRightsStatus(int uniqueId,
+                        const String8& path,
+                        int action);
+
+/**
+ * Consumes the rights for a content.
+ * If the reserve parameter is true the rights are reserved until the same
+ * application calls this api again with the reserve parameter set to false.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param action Action to perform. (Action::DEFAULT, Action::PLAY, etc)
+ * @param reserve True if the rights should be reserved.
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onConsumeRights(int uniqueId,
+                         DecryptHandle* decryptHandle,
+                         int action,
+                         bool reserve);
+
+/**
+ * Informs the DRM Engine about the playback actions performed on the DRM files.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE)
+ * @param position Position in the file (in milliseconds) where the start occurs.
+ *     Only valid together with Playback::START.
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+#ifdef USE_64BIT_DRM_API
+status_t onSetPlaybackStatus(int uniqueId,
+                             DecryptHandle* decryptHandle,
+                             int playbackStatus,
+                             int64_t position);
+#else
+status_t onSetPlaybackStatus(int uniqueId,
+                             DecryptHandle* decryptHandle,
+                             int playbackStatus,
+                             int position);
+#endif
+
+/**
+ *  Validates whether an action on the DRM content is allowed or not.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @param action Action to validate (Action::PLAY, Action::TRANSFER, etc)
+ * @param description Detailed description of the action
+ * @return true if the action is allowed.
+ */
+bool onValidateAction(int uniqueId,
+                      const String8& path,
+                      int action,
+                      const ActionDescription& description);
+
+/**
+ * Removes the rights associated with the given protected content.
+ * Not used for Forward Lock Engine.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onRemoveRights(int uniqueId, const String8& path);
+
+/**
+ * Removes all the rights information of each plug-in associated with
+ * DRM framework. Will be used in master reset but does nothing for
+ * Forward Lock Engine.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onRemoveAllRights(int uniqueId);
+
+/**
+ * Starts the Forward Lock file conversion session.
+ * Each time the application tries to download a new DRM file
+ * which needs to be converted, then the application has to
+ * begin with calling this API. The convertId is used as the conversion session key
+ * and must not be the same for different convert sessions.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param convertId Handle for the convert session
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onOpenConvertSession(int uniqueId, int convertId);
+
+/**
+ * Accepts and converts the input data which is part of DRM file.
+ * The resultant converted data and the status is returned in the DrmConvertedInfo
+ * object. This method will be called each time there is a new block
+ * of data received by the application.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param convertId Handle for the convert session
+ * @param inputData Input Data which need to be converted
+ * @return Return object contains the status of the data conversion,
+ *       the output converted data and offset. In this case the
+ *      application will ignore the offset information.
+ */
+DrmConvertedStatus* onConvertData(int uniqueId,
+                                  int convertId,
+                                  const DrmBuffer* inputData);
+
+/**
+ * Closes the convert session in case of data supply completed or error occurred.
+ * Upon successful conversion of the complete data, it returns signature calculated over
+ * the entire data used over a conversion session. This signature must be copied to the offset
+ * mentioned in the DrmConvertedStatus. Signature is used for data integrity protection.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param convertId Handle for the convert session
+ * @return Return object contains the status of the data conversion,
+ *      the header and body signature data. It also informs
+ *      the application about the file offset at which this
+ *      signature data should be written.
+ */
+DrmConvertedStatus* onCloseConvertSession(int uniqueId, int convertId);
+
+/**
+ * Returns the information about the Drm Engine capabilities which includes
+ * supported MimeTypes and file suffixes.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return DrmSupportInfo
+ *      instance which holds the capabilities of a plug-in
+ */
+DrmSupportInfo* onGetSupportInfo(int uniqueId);
+
+/**
+ * Open the decrypt session to decrypt the given protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the current decryption session
+ * @param fd File descriptor of the protected content to be decrypted
+ * @param offset Start position of the content
+ * @param length The length of the protected content
+ * @return
+ *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+#ifdef USE_64BIT_DRM_API
+status_t onOpenDecryptSession(int uniqueId,
+                              DecryptHandle* decryptHandle,
+                              int fd, off64_t offset, off64_t length);
+#else
+status_t onOpenDecryptSession(int uniqueId,
+                              DecryptHandle* decryptHandle,
+                              int fd, int offset, int length);
+#endif
+
+/**
+ * Open the decrypt session to decrypt the given protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the current decryption session
+ * @param uri Path of the protected content to be decrypted
+ * @return
+ *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t onOpenDecryptSession(int uniqueId,
+                              DecryptHandle* decryptHandle,
+                              const char* uri);
+
+/**
+ * Close the decrypt session for the given handle.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onCloseDecryptSession(int uniqueId,
+                               DecryptHandle* decryptHandle);
+
+/**
+ * Initialize decryption for the given unit of the protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param headerInfo Information for initializing decryption of this decrypUnit
+ * @return
+ *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t onInitializeDecryptUnit(int uniqueId,
+                                 DecryptHandle* decryptHandle,
+                                 int decryptUnitId,
+                                 const DrmBuffer* headerInfo);
+
+/**
+ * Decrypt the protected content buffers for the given unit.
+ * This method will be called any number of times, based on number of
+ * encrypted streams received from application.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param encBuffer Encrypted data block
+ * @param decBuffer Decrypted data block
+ * @return status_t
+ *     Returns the error code for this API
+ *     DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED
+ *     DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
+ *     DRM_ERROR_DECRYPT for failure.
+ */
+status_t onDecrypt(int uniqueId,
+                   DecryptHandle* decryptHandle,
+                   int decryptUnitId,
+                   const DrmBuffer* encBuffer,
+                   DrmBuffer** decBuffer);
+
+/**
+ * Decrypt the protected content buffers for the given unit.
+ * This method will be called any number of times, based on number of
+ * encrypted streams received from application.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptId Handle for the decryption session
+ * @param decryptUnitId ID Specifies decryption unit, such as track ID
+ * @param encBuffer Encrypted data block
+ * @param decBuffer Decrypted data block
+ * @param IV Optional buffer
+ * @return status_t
+ *     Returns the error code for this API
+ *     DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED
+ *     DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
+ *     DRM_ERROR_DECRYPT for failure.
+ */
+status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle,
+                   int decryptUnitId, const DrmBuffer* encBuffer,
+                   DrmBuffer** decBuffer, DrmBuffer* IV);
+
+/**
+ * Finalize decryption for the given unit of the protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param decryptUnitId ID Specifies decryption unit, such as track ID
+ * @return
+ *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t onFinalizeDecryptUnit(int uniqueId,
+                               DecryptHandle* decryptHandle,
+                               int decryptUnitId);
+
+/**
+ * Reads the specified number of bytes from an open DRM file.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param buffer Reference to the buffer that should receive the read data.
+ * @param numBytes Number of bytes to read.
+ *
+ * @return Number of bytes read.
+ * @retval -1 Failure.
+ */
+ssize_t onRead(int uniqueId,
+               DecryptHandle* decryptHandle,
+               void* pBuffer,
+               int numBytes);
+
+/**
+ * Updates the file position within an open DRM file.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param offset Offset with which to update the file position.
+ * @param whence One of SEEK_SET, SEEK_CUR, and SEEK_END.
+ *           These constants are defined in unistd.h.
+ *
+ * @return New file position.
+ * @retval ((off_t)-1) Failure.
+ */
+#ifdef USE_64BIT_DRM_API
+off64_t onLseek(int uniqueId,
+                DecryptHandle* decryptHandle,
+                off64_t offset,
+                int whence);
+#else
+off_t onLseek(int uniqueId,
+              DecryptHandle* decryptHandle,
+              off_t offset,
+              int whence);
+#endif
+
+/**
+ * Reads the specified number of bytes from an open DRM file.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param buffer Reference to the buffer that should receive the read data.
+ * @param numBytes Number of bytes to read.
+ * @param offset Offset with which to update the file position.
+ *
+ * @return Number of bytes read. Returns -1 for Failure.
+ */
+#ifdef USE_64BIT_DRM_API
+ssize_t onPread(int uniqueId,
+                DecryptHandle* decryptHandle,
+                void* buffer,
+                ssize_t numBytes,
+                off64_t offset);
+#else
+ssize_t onPread(int uniqueId,
+                DecryptHandle* decryptHandle,
+                void* buffer,
+                ssize_t numBytes,
+                off_t offset);
+#endif
+
+private:
+
+/**
+ * Session Class for Forward Lock Conversion. An object of this class is created
+ * for every conversion.
+ */
+class ConvertSession {
+    public :
+        int uniqueId;
+        FwdLockConv_Output_t output;
+
+        ConvertSession() {
+            uniqueId = 0;
+            memset(&output, 0, sizeof(FwdLockConv_Output_t));
+        }
+
+        virtual ~ConvertSession() {}
+};
+
+/**
+ * Session Class for Forward Lock decoder. An object of this class is created
+ * for every decoding session.
+ */
+class DecodeSession {
+    public :
+        int fileDesc;
+        off_t offset;
+
+        DecodeSession() {
+            fileDesc = -1;
+            offset = 0;
+        }
+
+        DecodeSession(int fd) {
+            fileDesc = fd;
+            offset = 0;
+        }
+
+        virtual ~DecodeSession() {}
+};
+
+/**
+ * Session Map Tables for Conversion and Decoding of forward lock files.
+ */
+SessionMap<ConvertSession*> convertSessionMap;
+SessionMap<DecodeSession*> decodeSessionMap;
+
+/**
+ * Converts the error code from Forward Lock Converter to DrmConvertStatus error code.
+ *
+ * @param Forward Lock Converter error code
+ *
+ * @return Status code from DrmConvertStatus.
+ */
+static int getConvertedStatus(FwdLockConv_Status_t status);
+};
+
+};
+
+#endif /* __FWDLOCKENGINE_H__ */
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h
new file mode 100644
index 0000000..da95d60
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef __FWDLOCKENGINECONST_H__
+#define __FWDLOCKENGINECONST_H__
+
+namespace android {
+
+/**
+ * Constants for forward Lock Engine used for exposing engine's capabilities.
+ */
+#define FWDLOCK_EXTENSION_FL           ("FL")
+#define FWDLOCK_DOTEXTENSION_FL        (".fl")
+#define FWDLOCK_MIMETYPE_FL            ("application/x-android-drm-fl")
+
+#define FWDLOCK_EXTENSION_DM           ("DM")
+#define FWDLOCK_DOTEXTENSION_DM        (".dm")
+#define FWDLOCK_MIMETYPE_DM            ("application/vnd.oma.drm.message")
+
+#define FWDLOCK_DESCRIPTION            ("OMA V1 Forward Lock")
+
+};
+
+#endif /* __FWDLOCKENGINECONST_H__ */
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
new file mode 100644
index 0000000..d430f72
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
@@ -0,0 +1,628 @@
+/*
+ * 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.
+ */
+
+#include "SessionMap.h"
+#include "FwdLockEngine.h"
+#include <utils/Log.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "drm_framework_common.h"
+#include <fcntl.h>
+#include <limits.h>
+#include <DrmRights.h>
+#include <DrmConstraints.h>
+#include <DrmMetadata.h>
+#include <DrmInfo.h>
+#include <DrmInfoStatus.h>
+#include <DrmInfoRequest.h>
+#include <DrmSupportInfo.h>
+#include <DrmConvertedStatus.h>
+#include <utils/String8.h>
+#include "FwdLockConv.h"
+#include "FwdLockFile.h"
+#include "FwdLockGlue.h"
+#include "FwdLockEngineConst.h"
+#include "MimeTypeUtil.h"
+
+#undef LOG_TAG
+#define LOG_TAG "FwdLockEngine"
+
+using namespace android;
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" IDrmEngine* create() {
+    return new FwdLockEngine();
+}
+
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" void destroy(IDrmEngine* plugIn) {
+    delete plugIn;
+}
+
+FwdLockEngine::FwdLockEngine() {
+    LOGD("FwdLockEngine Construction");
+}
+
+FwdLockEngine::~FwdLockEngine() {
+    LOGD("FwdLockEngine Destruction");
+
+    convertSessionMap.destroyMap();
+    decodeSessionMap.destroyMap();
+}
+
+int FwdLockEngine::getConvertedStatus(FwdLockConv_Status_t status) {
+    int retStatus = DrmConvertedStatus::STATUS_ERROR;
+
+    switch(status) {
+        case FwdLockConv_Status_OK:
+            retStatus = DrmConvertedStatus::STATUS_OK;
+            break;
+        case FwdLockConv_Status_SyntaxError:
+        case FwdLockConv_Status_InvalidArgument:
+        case FwdLockConv_Status_UnsupportedFileFormat:
+        case FwdLockConv_Status_UnsupportedContentTransferEncoding:
+            LOGD("FwdLockEngine getConvertedStatus: file conversion Error %d. " \
+                  "Returning STATUS_INPUTDATA_ERROR", status);
+            retStatus = DrmConvertedStatus::STATUS_INPUTDATA_ERROR;
+            break;
+        default:
+            LOGD("FwdLockEngine getConvertedStatus: file conversion Error %d. " \
+                  "Returning STATUS_ERROR", status);
+            retStatus = DrmConvertedStatus::STATUS_ERROR;
+            break;
+    }
+
+    return retStatus;
+}
+
+DrmConstraints* FwdLockEngine::onGetConstraints(int uniqueId, const String8* path, int action) {
+    DrmConstraints* drmConstraints = NULL;
+
+    LOGD("FwdLockEngine::onGetConstraints");
+
+    if (NULL != path &&
+        (RightsStatus::RIGHTS_VALID == onCheckRightsStatus(uniqueId, *path, action))) {
+        // Return the empty constraints to show no error condition.
+        drmConstraints = new DrmConstraints();
+    }
+
+    return drmConstraints;
+}
+
+DrmMetadata* FwdLockEngine::onGetMetadata(int uniqueId, const String8* path) {
+    DrmMetadata* drmMetadata = NULL;
+
+    LOGD("FwdLockEngine::onGetMetadata");
+
+    if (NULL != path) {
+        // Returns empty metadata to show no error condition.
+        drmMetadata = new DrmMetadata();
+    }
+
+    return drmMetadata;
+}
+
+android::status_t FwdLockEngine::onInitialize(int uniqueId) {
+    LOGD("FwdLockEngine::onInitialize");
+
+
+    if (FwdLockGlue_InitializeKeyEncryption()) {
+        LOGD("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption succeeded");
+    } else {
+        LOGD("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption failed:"
+             "errno = %d", errno);
+    }
+
+    return DRM_NO_ERROR;
+}
+
+android::status_t
+FwdLockEngine::onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener) {
+    // Not used
+    LOGD("FwdLockEngine::onSetOnInfoListener");
+
+    return DRM_NO_ERROR;
+}
+
+android::status_t FwdLockEngine::onTerminate(int uniqueId) {
+    LOGD("FwdLockEngine::onTerminate");
+
+    return DRM_NO_ERROR;
+}
+
+DrmSupportInfo* FwdLockEngine::onGetSupportInfo(int uniqueId) {
+    DrmSupportInfo* pSupportInfo = new DrmSupportInfo();
+
+    LOGD("FwdLockEngine::onGetSupportInfo");
+
+    // fill all Forward Lock mimetypes and extensions
+    if (NULL != pSupportInfo) {
+        pSupportInfo->addMimeType(String8(FWDLOCK_MIMETYPE_FL));
+        pSupportInfo->addFileSuffix(String8(FWDLOCK_DOTEXTENSION_FL));
+        pSupportInfo->addMimeType(String8(FWDLOCK_MIMETYPE_DM));
+        pSupportInfo->addFileSuffix(String8(FWDLOCK_DOTEXTENSION_DM));
+
+        pSupportInfo->setDescription(String8(FWDLOCK_DESCRIPTION));
+    }
+
+    return pSupportInfo;
+}
+
+bool FwdLockEngine::onCanHandle(int uniqueId, const String8& path) {
+    bool result = false;
+
+    String8 extString = path.getPathExtension();
+
+    extString.toLower();
+
+    if ((extString == String8(FWDLOCK_DOTEXTENSION_FL)) ||
+        (extString == String8(FWDLOCK_DOTEXTENSION_DM))) {
+        result = true;
+    }
+    return result;
+}
+
+DrmInfoStatus* FwdLockEngine::onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+    DrmInfoStatus *drmInfoStatus = NULL;
+
+    // Nothing to process
+
+    drmInfoStatus = new DrmInfoStatus((int)DrmInfoStatus::STATUS_OK, 0, NULL, String8(""));
+
+    LOGD("FwdLockEngine::onProcessDrmInfo");
+
+    return drmInfoStatus;
+}
+
+status_t FwdLockEngine::onSaveRights(
+            int uniqueId,
+            const DrmRights& drmRights,
+            const String8& rightsPath,
+            const String8& contentPath) {
+    // No rights to save. Return
+    LOGD("FwdLockEngine::onSaveRights");
+    return DRM_ERROR_UNKNOWN;
+}
+
+DrmInfo* FwdLockEngine::onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+    DrmInfo* drmInfo = NULL;
+
+    // Nothing to be done for Forward Lock file
+    LOGD("FwdLockEngine::onAcquireDrmInfo");
+
+    return drmInfo;
+}
+
+int FwdLockEngine::onCheckRightsStatus(int uniqueId,
+                                       const String8& path,
+                                       int action) {
+    int result = RightsStatus::RIGHTS_INVALID;
+
+    LOGD("FwdLockEngine::onCheckRightsStatus");
+
+    // Only Transfer action is not allowed for forward Lock files.
+    if (onCanHandle(uniqueId, path)) {
+        switch(action) {
+            case Action::DEFAULT:
+            case Action::PLAY:
+            case Action::RINGTONE:
+            case Action::OUTPUT:
+            case Action::PREVIEW:
+            case Action::EXECUTE:
+            case Action::DISPLAY:
+                result = RightsStatus::RIGHTS_VALID;
+                break;
+
+            case Action::TRANSFER:
+            default:
+                result = RightsStatus::RIGHTS_INVALID;
+                break;
+        }
+    }
+
+    return result;
+}
+
+status_t FwdLockEngine::onConsumeRights(int uniqueId,
+                                        DecryptHandle* decryptHandle,
+                                        int action,
+                                        bool reserve) {
+    // No rights consumption
+    LOGD("FwdLockEngine::onConsumeRights");
+    return DRM_NO_ERROR;
+}
+
+bool FwdLockEngine::onValidateAction(int uniqueId,
+                                     const String8& path,
+                                     int action,
+                                     const ActionDescription& description) {
+    LOGD("FwdLockEngine::onValidateAction");
+
+    // For the forwardlock engine checkRights and ValidateAction are the same.
+    return (onCheckRightsStatus(uniqueId, path, action) == RightsStatus::RIGHTS_VALID);
+}
+
+String8 FwdLockEngine::onGetOriginalMimeType(int uniqueId, const String8& path) {
+    LOGD("FwdLockEngine::onGetOriginalMimeType");
+    String8 mimeString = String8("");
+    int fileDesc = FwdLockFile_open(path.string());
+
+    if (-1 < fileDesc) {
+        const char* pMimeType = FwdLockFile_GetContentType(fileDesc);
+
+        if (NULL != pMimeType) {
+            String8 contentType = String8(pMimeType);
+            contentType.toLower();
+            mimeString = MimeTypeUtil::convertMimeType(contentType);
+        }
+
+        FwdLockFile_close(fileDesc);
+    }
+
+    return mimeString;
+}
+
+int FwdLockEngine::onGetDrmObjectType(int uniqueId,
+                                      const String8& path,
+                                      const String8& mimeType) {
+    String8 mimeStr = String8(mimeType);
+
+    LOGD("FwdLockEngine::onGetDrmObjectType");
+
+    mimeStr.toLower();
+
+    /* Checks whether
+    * 1. path and mime type both are not empty strings (meaning unavailable) else content is unknown
+    * 2. if one of them is empty string and if other is known then its a DRM Content Object.
+    * 3. if both of them are available, then both may be of known type
+    *    (regardless of the relation between them to make it compatible with other DRM Engines)
+    */
+    if (((0 == path.length()) || onCanHandle(uniqueId, path)) &&
+        ((0 == mimeType.length()) || ((mimeStr == String8(FWDLOCK_MIMETYPE_FL)) ||
+        (mimeStr == String8(FWDLOCK_MIMETYPE_DM)))) && (mimeType != path) ) {
+            return DrmObjectType::CONTENT;
+    }
+
+    return DrmObjectType::UNKNOWN;
+}
+
+status_t FwdLockEngine::onRemoveRights(int uniqueId, const String8& path) {
+    // No Rights to remove
+    LOGD("FwdLockEngine::onRemoveRights");
+    return DRM_NO_ERROR;
+}
+
+status_t FwdLockEngine::onRemoveAllRights(int uniqueId) {
+    // No rights to remove
+    LOGD("FwdLockEngine::onRemoveAllRights");
+    return DRM_NO_ERROR;
+}
+
+#ifdef USE_64BIT_DRM_API
+status_t FwdLockEngine::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+                                            int playbackStatus, int64_t position) {
+#else
+status_t FwdLockEngine::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+                                            int playbackStatus, int position) {
+#endif
+    // Not used
+    LOGD("FwdLockEngine::onSetPlaybackStatus");
+    return DRM_NO_ERROR;
+}
+
+status_t FwdLockEngine::onOpenConvertSession(int uniqueId,
+                                         int convertId) {
+    status_t result = DRM_ERROR_UNKNOWN;
+    LOGD("FwdLockEngine::onOpenConvertSession");
+    if (!convertSessionMap.isCreated(convertId)) {
+        ConvertSession *newSession = new ConvertSession();
+        if (FwdLockConv_Status_OK ==
+            FwdLockConv_OpenSession(&(newSession->uniqueId), &(newSession->output))) {
+            convertSessionMap.addValue(convertId, newSession);
+            result = DRM_NO_ERROR;
+        } else {
+            LOGD("FwdLockEngine::onOpenConvertSession -- FwdLockConv_OpenSession failed.");
+            delete newSession;
+        }
+    }
+    return result;
+}
+
+DrmConvertedStatus* FwdLockEngine::onConvertData(int uniqueId,
+                                                 int convertId,
+                                                 const DrmBuffer* inputData) {
+    FwdLockConv_Status_t retStatus = FwdLockConv_Status_InvalidArgument;
+    DrmBuffer *convResult = new DrmBuffer(NULL, 0);
+    int offset = -1;
+
+    if (NULL != inputData && convertSessionMap.isCreated(convertId)) {
+        ConvertSession *convSession = convertSessionMap.getValue(convertId);
+
+        if (NULL != convSession) {
+            retStatus = FwdLockConv_ConvertData(convSession->uniqueId,
+                                                inputData->data,
+                                                inputData->length,
+                                                &(convSession->output));
+
+            if (FwdLockConv_Status_OK == retStatus) {
+                // return bytes from conversion if available
+                if (convSession->output.fromConvertData.numBytes > 0) {
+                    convResult->data = new char[convSession->output.fromConvertData.numBytes];
+
+                    if (NULL != convResult->data) {
+                        convResult->length = convSession->output.fromConvertData.numBytes;
+                        memcpy(convResult->data,
+                               (char *)convSession->output.fromConvertData.pBuffer,
+                               convResult->length);
+                    }
+                }
+            } else {
+                offset = convSession->output.fromConvertData.errorPos;
+            }
+        }
+    }
+    return new DrmConvertedStatus(getConvertedStatus(retStatus), convResult, offset);
+}
+
+DrmConvertedStatus* FwdLockEngine::onCloseConvertSession(int uniqueId,
+                                                         int convertId) {
+    FwdLockConv_Status_t retStatus = FwdLockConv_Status_InvalidArgument;
+    DrmBuffer *convResult = new DrmBuffer(NULL, 0);
+    int offset = -1;
+
+    LOGD("FwdLockEngine::onCloseConvertSession");
+
+    if (convertSessionMap.isCreated(convertId)) {
+        ConvertSession *convSession = convertSessionMap.getValue(convertId);
+
+        if (NULL != convSession) {
+            retStatus = FwdLockConv_CloseSession(convSession->uniqueId, &(convSession->output));
+
+            if (FwdLockConv_Status_OK == retStatus) {
+                offset = convSession->output.fromCloseSession.fileOffset;
+                convResult->data = new char[FWD_LOCK_SIGNATURES_SIZE];
+
+                if (NULL != convResult->data) {
+                      convResult->length = FWD_LOCK_SIGNATURES_SIZE;
+                      memcpy(convResult->data,
+                             (char *)convSession->output.fromCloseSession.signatures,
+                             convResult->length);
+                }
+            }
+        }
+        convertSessionMap.removeValue(convertId);
+    }
+    return new DrmConvertedStatus(getConvertedStatus(retStatus), convResult, offset);
+}
+
+#ifdef USE_64BIT_DRM_API
+status_t FwdLockEngine::onOpenDecryptSession(int uniqueId,
+                                             DecryptHandle* decryptHandle,
+                                             int fd,
+                                             off64_t offset,
+                                             off64_t length) {
+#else
+status_t FwdLockEngine::onOpenDecryptSession(int uniqueId,
+                                             DecryptHandle* decryptHandle,
+                                             int fd,
+                                             int offset,
+                                             int length) {
+#endif
+    status_t result = DRM_ERROR_CANNOT_HANDLE;
+    int fileDesc = -1;
+
+    LOGD("FwdLockEngine::onOpenDecryptSession");
+
+    if ((-1 < fd) &&
+        (NULL != decryptHandle) &&
+        (!decodeSessionMap.isCreated(decryptHandle->decryptId))) {
+        fileDesc = dup(fd);
+    } else {
+        LOGD("FwdLockEngine::onOpenDecryptSession parameter error");
+        return result;
+    }
+
+    if (-1 < fileDesc &&
+        -1 < ::lseek(fileDesc, offset, SEEK_SET) &&
+        -1 < FwdLockFile_attach(fileDesc)) {
+        // check for file integrity. This must be done to protect the content mangling.
+        int retVal = FwdLockFile_CheckHeaderIntegrity(fileDesc);
+        DecodeSession* decodeSession = new DecodeSession(fileDesc);
+
+        if (retVal && NULL != decodeSession) {
+            decodeSessionMap.addValue(decryptHandle->decryptId, decodeSession);
+            const char *pmime= FwdLockFile_GetContentType(fileDesc);
+            String8 contentType = String8(pmime == NULL ? "" : pmime);
+            contentType.toLower();
+            decryptHandle->mimeType = MimeTypeUtil::convertMimeType(contentType);
+            decryptHandle->decryptApiType = DecryptApiType::CONTAINER_BASED;
+            decryptHandle->status = RightsStatus::RIGHTS_VALID;
+            decryptHandle->decryptInfo = NULL;
+            result = DRM_NO_ERROR;
+        } else {
+            LOGD("FwdLockEngine::onOpenDecryptSession Integrity Check failed for the fd");
+            FwdLockFile_detach(fileDesc);
+            ::close(fileDesc);
+            delete decodeSession;
+        }
+    }
+
+    LOGD("FwdLockEngine::onOpenDecryptSession Exit. result = %d", result);
+
+    return result;
+}
+
+status_t FwdLockEngine::onOpenDecryptSession(int uniqueId,
+                                             DecryptHandle* decryptHandle,
+                                             const char* uri) {
+    status_t result = DRM_ERROR_CANNOT_HANDLE;
+    const char fileTag [] = "file://";
+
+    if (NULL != decryptHandle && NULL != uri && strlen(uri) > sizeof(fileTag)) {
+        String8 uriTag = String8(uri);
+        uriTag.toLower();
+
+        if (0 == strncmp(uriTag.string(), fileTag, sizeof(fileTag) - 1)) {
+            const char *filePath = strchr(uri + sizeof(fileTag) - 1, '/');
+            if (NULL != filePath && onCanHandle(uniqueId, String8(filePath))) {
+                int fd = open(filePath, O_RDONLY);
+
+                if (-1 < fd) {
+                    // offset is always 0 and length is not used. so any positive size.
+                    result = onOpenDecryptSession(uniqueId, decryptHandle, fd, 0, 1);
+
+                    // fd is duplicated already if success. closing the file
+                    close(fd);
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+status_t FwdLockEngine::onCloseDecryptSession(int uniqueId,
+                                              DecryptHandle* decryptHandle) {
+    status_t result = DRM_ERROR_UNKNOWN;
+    LOGD("FwdLockEngine::onCloseDecryptSession");
+
+    if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) {
+        DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
+        if (NULL != session && session->fileDesc > -1) {
+            FwdLockFile_detach(session->fileDesc);
+            ::close(session->fileDesc);
+            decodeSessionMap.removeValue(decryptHandle->decryptId);
+            result = DRM_NO_ERROR;
+        }
+    }
+
+    LOGD("FwdLockEngine::onCloseDecryptSession Exit");
+    return result;
+}
+
+status_t FwdLockEngine::onInitializeDecryptUnit(int uniqueId,
+                                                DecryptHandle* decryptHandle,
+                                                int decryptUnitId,
+                                                const DrmBuffer* headerInfo) {
+    LOGD("FwdLockEngine::onInitializeDecryptUnit");
+    return DRM_ERROR_UNKNOWN;
+}
+
+status_t FwdLockEngine::onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+    LOGD("FwdLockEngine::onDecrypt");
+    return DRM_ERROR_UNKNOWN;
+}
+
+status_t FwdLockEngine::onDecrypt(int uniqueId,
+                                  DecryptHandle* decryptHandle,
+                                  int decryptUnitId,
+                                  const DrmBuffer* encBuffer,
+                                  DrmBuffer** decBuffer) {
+    LOGD("FwdLockEngine::onDecrypt");
+    return DRM_ERROR_UNKNOWN;
+}
+
+status_t FwdLockEngine::onFinalizeDecryptUnit(int uniqueId,
+                                              DecryptHandle* decryptHandle,
+                                              int decryptUnitId) {
+    LOGD("FwdLockEngine::onFinalizeDecryptUnit");
+    return DRM_ERROR_UNKNOWN;
+}
+
+ssize_t FwdLockEngine::onRead(int uniqueId,
+                              DecryptHandle* decryptHandle,
+                              void* buffer,
+                              int numBytes) {
+    ssize_t size = -1;
+
+    if (NULL != decryptHandle &&
+       decodeSessionMap.isCreated(decryptHandle->decryptId) &&
+        NULL != buffer &&
+        numBytes > -1) {
+        DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
+        if (NULL != session && session->fileDesc > -1) {
+            size = FwdLockFile_read(session->fileDesc, buffer, numBytes);
+
+            if (0 > size) {
+                session->offset = ((off_t)-1);
+            } else {
+                session->offset += size;
+            }
+        }
+    }
+
+    return size;
+}
+
+#ifdef USE_64BIT_DRM_API
+off64_t FwdLockEngine::onLseek(int uniqueId, DecryptHandle* decryptHandle,
+                               off64_t offset, int whence) {
+#else
+off_t FwdLockEngine::onLseek(int uniqueId, DecryptHandle* decryptHandle,
+                             off_t offset, int whence) {
+#endif
+    off_t offval = -1;
+
+    if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) {
+        DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
+        if (NULL != session && session->fileDesc > -1) {
+            offval = FwdLockFile_lseek(session->fileDesc, offset, whence);
+            session->offset = offval;
+        }
+    }
+
+    return offval;
+}
+
+#ifdef USE_64BIT_DRM_API
+ssize_t FwdLockEngine::onPread(int uniqueId,
+                               DecryptHandle* decryptHandle,
+                               void* buffer,
+                               ssize_t numBytes,
+                               off64_t offset) {
+#else
+ssize_t FwdLockEngine::onPread(int uniqueId,
+                               DecryptHandle* decryptHandle,
+                               void* buffer,
+                               ssize_t numBytes,
+                               off_t offset) {
+#endif
+    ssize_t bytesRead = -1;
+
+    DecodeSession* decoderSession = NULL;
+
+    if ((NULL != decryptHandle) &&
+        (NULL != (decoderSession = decodeSessionMap.getValue(decryptHandle->decryptId))) &&
+        (NULL != buffer) &&
+        (numBytes > -1) &&
+        (offset > -1)) {
+        if (offset != decoderSession->offset) {
+            decoderSession->offset = onLseek(uniqueId, decryptHandle, offset, SEEK_SET);
+        }
+
+        if (((off_t)-1) != decoderSession->offset) {
+            bytesRead = onRead(uniqueId, decryptHandle, buffer, numBytes);
+            if (bytesRead < 0) {
+                LOGD("FwdLockEngine::onPread error reading");
+            }
+        }
+    } else {
+        LOGD("FwdLockEngine::onPread decryptId not found");
+    }
+
+    return bytesRead;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk
@@ -0,0 +1,16 @@
+#
+# 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.
+#
+include $(call all-subdir-makefiles)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk
new file mode 100644
index 0000000..6c5d3cf
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    FwdLockGlue.c
+
+LOCAL_C_INCLUDES := \
+    external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := libcrypto
+
+LOCAL_MODULE := libfwdlock-common
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c
new file mode 100644
index 0000000..92bda8ff
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <openssl/aes.h>
+
+#include "FwdLockGlue.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define KEY_SIZE 16
+#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
+
+static int isInitialized = FALSE;
+
+static const char strKeyFilename[] = "/data/drm/fwdlock/kek.dat";
+
+static AES_KEY encryptionRoundKeys;
+static AES_KEY decryptionRoundKeys;
+
+/**
+ * Creates all directories along the fully qualified path of the given file.
+ *
+ * @param[in] path A reference to the fully qualified path of a file.
+ * @param[in] mode The access mode to use for the directories being created.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+static int FwdLockGlue_CreateDirectories(const char *path, mode_t mode) {
+    int result = TRUE;
+    size_t partialPathLength = strlen(path);
+    char *partialPath = malloc(partialPathLength + 1);
+    if (partialPath == NULL) {
+        result = FALSE;
+    } else {
+        size_t i;
+        for (i = 0; i < partialPathLength; ++i) {
+            if (path[i] == '/' && i > 0) {
+                partialPath[i] = '\0';
+                if (mkdir(partialPath, mode) != 0 && errno != EEXIST) {
+                    result = FALSE;
+                    break;
+                }
+            }
+            partialPath[i] = path[i];
+        }
+        free(partialPath);
+    }
+    return result;
+}
+
+/**
+ * Initializes the round keys used for encryption and decryption of session keys. First creates a
+ * device-unique key-encryption key if none exists yet.
+ */
+static void FwdLockGlue_InitializeRoundKeys() {
+    unsigned char keyEncryptionKey[KEY_SIZE];
+    int fileDesc = open(strKeyFilename, O_RDONLY);
+    if (fileDesc >= 0) {
+        if (read(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) {
+            isInitialized = TRUE;
+        }
+        (void)close(fileDesc);
+    } else if (errno == ENOENT &&
+               FwdLockGlue_GetRandomNumber(keyEncryptionKey, KEY_SIZE) &&
+               FwdLockGlue_CreateDirectories(strKeyFilename, S_IRWXU)) {
+        fileDesc = open(strKeyFilename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR);
+        if (fileDesc >= 0) {
+            if (write(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) {
+                isInitialized = TRUE;
+            }
+            (void)close(fileDesc);
+        }
+    }
+    if (isInitialized) {
+        if (AES_set_encrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &encryptionRoundKeys) != 0 ||
+            AES_set_decrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &decryptionRoundKeys) != 0) {
+            isInitialized = FALSE;
+        }
+    }
+    memset(keyEncryptionKey, 0, KEY_SIZE); // Zero out key data.
+}
+
+/**
+ * Validates the padding of a decrypted key.
+ *
+ * @param[in] pData A reference to the buffer containing the decrypted key and padding.
+ * @param[in] decryptedKeyLength The length in bytes of the decrypted key.
+ *
+ * @return A Boolean value indicating whether the padding was valid.
+ */
+static int FwdLockGlue_ValidatePadding(const unsigned char *pData, size_t decryptedKeyLength) {
+    size_t i;
+    size_t padding = AES_BLOCK_SIZE - (decryptedKeyLength % AES_BLOCK_SIZE);
+    pData += decryptedKeyLength;
+    for (i = 0; i < padding; ++i) {
+        if ((size_t)*pData != padding) {
+            return FALSE;
+        }
+        ++pData;
+    }
+    return TRUE;
+}
+
+int FwdLockGlue_GetRandomNumber(void *pBuffer, size_t numBytes) {
+    // Generate 'cryptographically secure' random bytes by reading them from "/dev/urandom" (the
+    // non-blocking version of "/dev/random").
+    ssize_t numBytesRead = 0;
+    int fileDesc = open("/dev/urandom", O_RDONLY);
+    if (fileDesc >= 0) {
+        numBytesRead = read(fileDesc, pBuffer, numBytes);
+        (void)close(fileDesc);
+    }
+    return numBytesRead >= 0 && (size_t)numBytesRead == numBytes;
+}
+
+int FwdLockGlue_InitializeKeyEncryption() {
+    static pthread_once_t once = PTHREAD_ONCE_INIT;
+    pthread_once(&once, FwdLockGlue_InitializeRoundKeys);
+    return isInitialized;
+}
+
+size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength) {
+    return ((plaintextKeyLength / AES_BLOCK_SIZE) + 2) * AES_BLOCK_SIZE;
+}
+
+int FwdLockGlue_EncryptKey(const void *pPlaintextKey,
+                           size_t plaintextKeyLength,
+                           void *pEncryptedKey,
+                           size_t encryptedKeyLength) {
+    int result = FALSE;
+    assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(plaintextKeyLength));
+    if (FwdLockGlue_InitializeKeyEncryption()) {
+        unsigned char initVector[AES_BLOCK_SIZE];
+        if (FwdLockGlue_GetRandomNumber(initVector, AES_BLOCK_SIZE)) {
+            size_t padding = AES_BLOCK_SIZE - (plaintextKeyLength % AES_BLOCK_SIZE);
+            size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE;
+            memcpy(pEncryptedKey, pPlaintextKey, plaintextKeyLength);
+            memset((unsigned char *)pEncryptedKey + plaintextKeyLength, (int)padding, padding);
+            memcpy((unsigned char *)pEncryptedKey + dataLength, initVector, AES_BLOCK_SIZE);
+            AES_cbc_encrypt(pEncryptedKey, pEncryptedKey, dataLength, &encryptionRoundKeys,
+                            initVector, AES_ENCRYPT);
+            result = TRUE;
+        }
+    }
+    return result;
+}
+
+int FwdLockGlue_DecryptKey(const void *pEncryptedKey,
+                           size_t encryptedKeyLength,
+                           void *pDecryptedKey,
+                           size_t decryptedKeyLength) {
+    int result = FALSE;
+    assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(decryptedKeyLength));
+    if (FwdLockGlue_InitializeKeyEncryption()) {
+        size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE;
+        unsigned char *pData = malloc(dataLength);
+        if (pData != NULL) {
+            unsigned char initVector[AES_BLOCK_SIZE];
+            memcpy(pData, pEncryptedKey, dataLength);
+            memcpy(initVector, (const unsigned char *)pEncryptedKey + dataLength, AES_BLOCK_SIZE);
+            AES_cbc_encrypt(pData, pData, dataLength, &decryptionRoundKeys, initVector,
+                            AES_DECRYPT);
+            memcpy(pDecryptedKey, pData, decryptedKeyLength);
+            result = FwdLockGlue_ValidatePadding(pData, decryptedKeyLength);
+            free(pData);
+        }
+    }
+    return result;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h
new file mode 100644
index 0000000..f36f6ea
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#ifndef __FWDLOCKGLUE_H__
+#define __FWDLOCKGLUE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Generates the specified number of cryptographically secure random bytes.
+ *
+ * @param[out] pBuffer A reference to the buffer that should receive the random data.
+ * @param[in] numBytes The number of random bytes to generate.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_GetRandomNumber(void *pBuffer, size_t numBytes);
+
+/**
+ * Performs initialization of the key-encryption key. Should be called once during startup to
+ * facilitate encryption and decryption of session keys.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_InitializeKeyEncryption();
+
+/**
+ * Returns the length of the encrypted key, given the length of the plaintext key.
+ *
+ * @param[in] plaintextKeyLength The length in bytes of the plaintext key.
+ *
+ * @return The length in bytes of the encrypted key.
+ */
+size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength);
+
+/**
+ * Encrypts the given session key using a key-encryption key unique to this device.
+ *
+ * @param[in] pPlaintextKey A reference to the buffer containing the plaintext key.
+ * @param[in] plaintextKeyLength The length in bytes of the plaintext key.
+ * @param[out] pEncryptedKey A reference to the buffer containing the encrypted key.
+ * @param[in] encryptedKeyLength The length in bytes of the encrypted key.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_EncryptKey(const void *pPlaintextKey,
+                           size_t plaintextKeyLength,
+                           void *pEncryptedKey,
+                           size_t encryptedKeyLength);
+
+/**
+ * Decrypts the given session key using a key-encryption key unique to this device.
+ *
+ * @param[in] pEncryptedKey A reference to the buffer containing the encrypted key.
+ * @param[in] encryptedKeyLength The length in bytes of the encrypted key.
+ * @param[out] pDecryptedKey A reference to the buffer containing the decrypted key.
+ * @param[in] decryptedKeyLength The length in bytes of the decrypted key.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_DecryptKey(const void *pEncryptedKey,
+                           size_t encryptedKeyLength,
+                           void *pDecryptedKey,
+                           size_t decryptedKeyLength);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __FWDLOCKGLUE_H__
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk
new file mode 100644
index 0000000..00bb788
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk
@@ -0,0 +1,37 @@
+#
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    FwdLockConv.c
+
+LOCAL_C_INCLUDES := \
+    frameworks/base/drm/libdrmframework/plugins/forward-lock/internal-format/common \
+    external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := libcrypto
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_MODULE := libfwdlock-converter
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
new file mode 100644
index 0000000..14ea9e9
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
@@ -0,0 +1,1339 @@
+/*
+ * 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.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <openssl/aes.h>
+#include <openssl/hmac.h>
+
+#include "FwdLockConv.h"
+#include "FwdLockGlue.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define INVALID_OFFSET ((off64_t)-1)
+
+#define MAX_NUM_SESSIONS 32
+
+#define OUTPUT_BUFFER_SIZE_INCREMENT 1024
+#define READ_BUFFER_SIZE 1024
+
+#define MAX_BOUNDARY_LENGTH 70
+#define MAX_DELIMITER_LENGTH (MAX_BOUNDARY_LENGTH + 4)
+
+#define STRING_LENGTH_INCREMENT 25
+
+#define KEY_SIZE AES_BLOCK_SIZE
+#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
+
+#define SHA1_HASH_SIZE 20
+
+#define FWD_LOCK_VERSION 0
+#define FWD_LOCK_SUBFORMAT 0
+#define USAGE_RESTRICTION_FLAGS 0
+#define CONTENT_TYPE_LENGTH_POS 7
+#define TOP_HEADER_SIZE 8
+
+/**
+ * Data type for the parser states of the converter.
+ */
+typedef enum FwdLockConv_ParserState {
+    FwdLockConv_ParserState_WantsOpenDelimiter,
+    FwdLockConv_ParserState_WantsMimeHeaders,
+    FwdLockConv_ParserState_WantsBinaryEncodedData,
+    FwdLockConv_ParserState_WantsBase64EncodedData,
+    FwdLockConv_ParserState_Done
+} FwdLockConv_ParserState_t;
+
+/**
+ * Data type for the scanner states of the converter.
+ */
+typedef enum FwdLockConv_ScannerState {
+    FwdLockConv_ScannerState_WantsFirstDash,
+    FwdLockConv_ScannerState_WantsSecondDash,
+    FwdLockConv_ScannerState_WantsCR,
+    FwdLockConv_ScannerState_WantsLF,
+    FwdLockConv_ScannerState_WantsBoundary,
+    FwdLockConv_ScannerState_WantsBoundaryEnd,
+    FwdLockConv_ScannerState_WantsMimeHeaderNameStart,
+    FwdLockConv_ScannerState_WantsMimeHeaderName,
+    FwdLockConv_ScannerState_WantsMimeHeaderNameEnd,
+    FwdLockConv_ScannerState_WantsContentTypeStart,
+    FwdLockConv_ScannerState_WantsContentType,
+    FwdLockConv_ScannerState_WantsContentTransferEncodingStart,
+    FwdLockConv_ScannerState_Wants_A_OR_I,
+    FwdLockConv_ScannerState_Wants_N,
+    FwdLockConv_ScannerState_Wants_A,
+    FwdLockConv_ScannerState_Wants_R,
+    FwdLockConv_ScannerState_Wants_Y,
+    FwdLockConv_ScannerState_Wants_S,
+    FwdLockConv_ScannerState_Wants_E,
+    FwdLockConv_ScannerState_Wants_6,
+    FwdLockConv_ScannerState_Wants_4,
+    FwdLockConv_ScannerState_Wants_B,
+    FwdLockConv_ScannerState_Wants_I,
+    FwdLockConv_ScannerState_Wants_T,
+    FwdLockConv_ScannerState_WantsContentTransferEncodingEnd,
+    FwdLockConv_ScannerState_WantsMimeHeaderValueEnd,
+    FwdLockConv_ScannerState_WantsMimeHeadersEnd,
+    FwdLockConv_ScannerState_WantsByte1,
+    FwdLockConv_ScannerState_WantsByte1_AfterCRLF,
+    FwdLockConv_ScannerState_WantsByte2,
+    FwdLockConv_ScannerState_WantsByte3,
+    FwdLockConv_ScannerState_WantsByte4,
+    FwdLockConv_ScannerState_WantsPadding,
+    FwdLockConv_ScannerState_WantsWhitespace,
+    FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF,
+    FwdLockConv_ScannerState_WantsDelimiter
+} FwdLockConv_ScannerState_t;
+
+/**
+ * Data type for the content transfer encoding.
+ */
+typedef enum FwdLockConv_ContentTransferEncoding {
+    FwdLockConv_ContentTransferEncoding_Undefined,
+    FwdLockConv_ContentTransferEncoding_Binary,
+    FwdLockConv_ContentTransferEncoding_Base64
+} FwdLockConv_ContentTransferEncoding_t;
+
+/**
+ * Data type for a dynamically growing string.
+ */
+typedef struct FwdLockConv_String {
+    char *ptr;
+    size_t length;
+    size_t maxLength;
+    size_t lengthIncrement;
+} FwdLockConv_String_t;
+
+/**
+ * Data type for the per-file state information needed by the converter.
+ */
+typedef struct FwdLockConv_Session {
+    FwdLockConv_ParserState_t parserState;
+    FwdLockConv_ScannerState_t scannerState;
+    FwdLockConv_ScannerState_t savedScannerState;
+    off64_t numCharsConsumed;
+    char delimiter[MAX_DELIMITER_LENGTH];
+    size_t delimiterLength;
+    size_t delimiterMatchPos;
+    FwdLockConv_String_t mimeHeaderName;
+    FwdLockConv_String_t contentType;
+    FwdLockConv_ContentTransferEncoding_t contentTransferEncoding;
+    unsigned char sessionKey[KEY_SIZE];
+    void *pEncryptedSessionKey;
+    size_t encryptedSessionKeyLength;
+    AES_KEY encryptionRoundKeys;
+    HMAC_CTX signingContext;
+    unsigned char topHeader[TOP_HEADER_SIZE];
+    unsigned char counter[AES_BLOCK_SIZE];
+    unsigned char keyStream[AES_BLOCK_SIZE];
+    int keyStreamIndex;
+    unsigned char ch;
+    size_t outputBufferSize;
+    size_t dataOffset;
+    size_t numDataBytes;
+} FwdLockConv_Session_t;
+
+static FwdLockConv_Session_t *sessionPtrs[MAX_NUM_SESSIONS] = { NULL };
+
+static pthread_mutex_t sessionAcquisitionMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static const FwdLockConv_String_t nullString = { NULL, 0, 0, STRING_LENGTH_INCREMENT };
+
+static const unsigned char topHeaderTemplate[] =
+    { 'F', 'W', 'L', 'K', FWD_LOCK_VERSION, FWD_LOCK_SUBFORMAT, USAGE_RESTRICTION_FLAGS };
+
+static const char strContent[] = "content-";
+static const char strType[] = "type";
+static const char strTransferEncoding[] = "transfer-encoding";
+static const char strTextPlain[] = "text/plain";
+static const char strApplicationVndOmaDrmRightsXml[] = "application/vnd.oma.drm.rights+xml";
+static const char strApplicationVndOmaDrmContent[] = "application/vnd.oma.drm.content";
+
+static const size_t strlenContent = sizeof strContent - 1;
+static const size_t strlenTextPlain = sizeof strTextPlain - 1;
+
+static const signed char base64Values[] = {
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
+    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+};
+
+/**
+ * Acquires an unused converter session.
+ *
+ * @return A session ID.
+ */
+static int FwdLockConv_AcquireSession() {
+    int sessionId = -1;
+    int i;
+    pthread_mutex_lock(&sessionAcquisitionMutex);
+    for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
+        if (sessionPtrs[i] == NULL) {
+            sessionPtrs[i] = malloc(sizeof *sessionPtrs[i]);
+            if (sessionPtrs[i] != NULL) {
+                sessionId = i;
+            }
+            break;
+        }
+    }
+    pthread_mutex_unlock(&sessionAcquisitionMutex);
+    return sessionId;
+}
+
+/**
+ * Checks whether a session ID is in range and currently in use.
+ *
+ * @param[in] sessionID A session ID.
+ *
+ * @return A Boolean value indicating whether the session ID is in range and currently in use.
+ */
+static int FwdLockConv_IsValidSession(int sessionId) {
+    return 0 <= sessionId && sessionId < MAX_NUM_SESSIONS && sessionPtrs[sessionId] != NULL;
+}
+
+/**
+ * Releases a converter session.
+ *
+ * @param[in] sessionID A session ID.
+ */
+static void FwdLockConv_ReleaseSession(int sessionId) {
+    pthread_mutex_lock(&sessionAcquisitionMutex);
+    assert(FwdLockConv_IsValidSession(sessionId));
+    memset(sessionPtrs[sessionId], 0, sizeof *sessionPtrs[sessionId]); // Zero out key data.
+    free(sessionPtrs[sessionId]);
+    sessionPtrs[sessionId] = NULL;
+    pthread_mutex_unlock(&sessionAcquisitionMutex);
+}
+
+/**
+ * Derives cryptographically independent keys for encryption and signing from the session key.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static int FwdLockConv_DeriveKeys(FwdLockConv_Session_t *pSession) {
+    FwdLockConv_Status_t status;
+    struct FwdLockConv_DeriveKeys_Data {
+        AES_KEY sessionRoundKeys;
+        unsigned char value[KEY_SIZE];
+        unsigned char key[KEY_SIZE];
+    } *pData = malloc(sizeof *pData);
+    if (pData == NULL) {
+        status = FwdLockConv_Status_OutOfMemory;
+    } else {
+        if (AES_set_encrypt_key(pSession->sessionKey, KEY_SIZE_IN_BITS,
+                                &pData->sessionRoundKeys) != 0) {
+            status = FwdLockConv_Status_ProgramError;
+        } else {
+            // Encrypt the 16-byte value {0, 0, ..., 0} to produce the encryption key.
+            memset(pData->value, 0, KEY_SIZE);
+            AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+            if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS,
+                                    &pSession->encryptionRoundKeys) != 0) {
+                status = FwdLockConv_Status_ProgramError;
+            } else {
+                // Encrypt the 16-byte value {1, 0, ..., 0} to produce the signing key.
+                ++pData->value[0];
+                AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+                HMAC_CTX_init(&pSession->signingContext);
+                HMAC_Init_ex(&pSession->signingContext, pData->key, KEY_SIZE, EVP_sha1(), NULL);
+                status = FwdLockConv_Status_OK;
+            }
+        }
+        memset(pData, 0, sizeof pData); // Zero out key data.
+        free(pData);
+    }
+    return status;
+}
+
+/**
+ * Checks whether a given character is valid in a boundary. Note that the boundary may contain
+ * leading and internal spaces.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in a boundary.
+ */
+static int FwdLockConv_IsBoundaryChar(int ch) {
+    return isalnum(ch) || ch == '\'' ||
+            ch == '(' || ch == ')' || ch == '+' || ch == '_' || ch == ',' || ch == '-' ||
+            ch == '.' || ch == '/' || ch == ':' || ch == '=' || ch == '?' || ch == ' ';
+}
+
+/**
+ * Checks whether a given character should be considered whitespace, using a narrower definition
+ * than the standard-library isspace() function.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character should be considered whitespace.
+ */
+static int FwdLockConv_IsWhitespace(int ch) {
+    return ch == ' ' || ch == '\t';
+}
+
+/**
+ * Removes trailing spaces from the delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_RightTrimDelimiter(FwdLockConv_Session_t *pSession) {
+    while (pSession->delimiterLength > 4 &&
+           pSession->delimiter[pSession->delimiterLength - 1] == ' ') {
+        --pSession->delimiterLength;
+    }
+    if (pSession->delimiterLength > 4) {
+        return FwdLockConv_Status_OK;
+    }
+    return FwdLockConv_Status_SyntaxError;
+}
+
+/**
+ * Matches the open delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchOpenDelimiter(FwdLockConv_Session_t *pSession,
+                                                           int ch) {
+    FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+    switch (pSession->scannerState) {
+    case FwdLockConv_ScannerState_WantsFirstDash:
+        if (ch == '-') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
+        } else if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsSecondDash:
+        if (ch == '-') {
+            // The delimiter starts with "\r\n--" (the open delimiter may omit the initial "\r\n").
+            // The rest is the user-defined boundary that should come next.
+            pSession->delimiter[0] = '\r';
+            pSession->delimiter[1] = '\n';
+            pSession->delimiter[2] = '-';
+            pSession->delimiter[3] = '-';
+            pSession->delimiterLength = 4;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsBoundary;
+        } else if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsCR:
+        if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsLF:
+        if (ch == '\n') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+        } else if (ch != '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsBoundary:
+        if (FwdLockConv_IsBoundaryChar(ch)) {
+            // The boundary may contain leading and internal spaces, so trailing spaces will also be
+            // matched here. These will be removed later.
+            if (pSession->delimiterLength < MAX_DELIMITER_LENGTH) {
+                pSession->delimiter[pSession->delimiterLength++] = ch;
+            } else if (ch != ' ') {
+                status = FwdLockConv_Status_SyntaxError;
+            }
+        } else if (ch == '\r') {
+            status = FwdLockConv_RightTrimDelimiter(pSession);
+            if (status == FwdLockConv_Status_OK) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsBoundaryEnd;
+            }
+        } else if (ch == '\t') {
+            status = FwdLockConv_RightTrimDelimiter(pSession);
+            if (status == FwdLockConv_Status_OK) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+            }
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsWhitespace:
+        if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsBoundaryEnd;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsBoundaryEnd:
+        if (ch == '\n') {
+            pSession->parserState = FwdLockConv_ParserState_WantsMimeHeaders;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameStart;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    default:
+        status = FwdLockConv_Status_ProgramError;
+        break;
+    }
+    return status;
+}
+
+/**
+ * Checks whether a given character is valid in a MIME header name.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in a MIME header name.
+ */
+static int FwdLockConv_IsMimeHeaderNameChar(int ch) {
+    return isgraph(ch) && ch != ':';
+}
+
+/**
+ * Checks whether a given character is valid in a MIME header value.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in a MIME header value.
+ */
+static int FwdLockConv_IsMimeHeaderValueChar(int ch) {
+    return isgraph(ch) && ch != ';';
+}
+
+/**
+ * Appends a character to the specified dynamically growing string.
+ *
+ * @param[in,out] pString A reference to a dynamically growing string.
+ * @param[in] ch The character to append.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_StringAppend(FwdLockConv_String_t *pString, int ch) {
+    if (pString->length == pString->maxLength) {
+        size_t newMaxLength = pString->maxLength + pString->lengthIncrement;
+        char *newPtr = realloc(pString->ptr, newMaxLength + 1);
+        if (newPtr == NULL) {
+            return FwdLockConv_Status_OutOfMemory;
+        }
+        pString->ptr = newPtr;
+        pString->maxLength = newMaxLength;
+    }
+    pString->ptr[pString->length++] = ch;
+    pString->ptr[pString->length] = '\0';
+    return FwdLockConv_Status_OK;
+}
+
+/**
+ * Attempts to recognize the MIME header name and changes the scanner state accordingly.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_RecognizeMimeHeaderName(FwdLockConv_Session_t *pSession) {
+    FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+    if (strncmp(pSession->mimeHeaderName.ptr, strContent, strlenContent) == 0) {
+        if (strcmp(pSession->mimeHeaderName.ptr + strlenContent, strType) == 0) {
+            if (pSession->contentType.ptr == NULL) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsContentTypeStart;
+            } else {
+                status = FwdLockConv_Status_SyntaxError;
+            }
+        } else if (strcmp(pSession->mimeHeaderName.ptr + strlenContent, strTransferEncoding) == 0) {
+            if (pSession->contentTransferEncoding ==
+                    FwdLockConv_ContentTransferEncoding_Undefined) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingStart;
+            } else {
+                status = FwdLockConv_Status_SyntaxError;
+            }
+        } else {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        }
+    } else {
+        pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+    }
+    return status;
+}
+
+/**
+ * Applies defaults to missing MIME header values.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_ApplyDefaults(FwdLockConv_Session_t *pSession) {
+    if (pSession->contentType.ptr == NULL) {
+        // Content type is missing: default to "text/plain".
+        pSession->contentType.ptr = malloc(sizeof strTextPlain);
+        if (pSession->contentType.ptr == NULL) {
+            return FwdLockConv_Status_OutOfMemory;
+        }
+        memcpy(pSession->contentType.ptr, strTextPlain, sizeof strTextPlain);
+        pSession->contentType.length = strlenTextPlain;
+        pSession->contentType.maxLength = strlenTextPlain;
+    }
+    if (pSession->contentTransferEncoding == FwdLockConv_ContentTransferEncoding_Undefined) {
+        // Content transfer encoding is missing: default to binary.
+        pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
+    }
+    return FwdLockConv_Status_OK;
+}
+
+/**
+ * Verifies that the content type is supported.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_VerifyContentType(FwdLockConv_Session_t *pSession) {
+    FwdLockConv_Status_t status;
+    if (pSession->contentType.ptr == NULL) {
+        status = FwdLockConv_Status_ProgramError;
+    } else if (strcmp(pSession->contentType.ptr, strApplicationVndOmaDrmRightsXml) == 0 ||
+               strcmp(pSession->contentType.ptr, strApplicationVndOmaDrmContent) == 0) {
+        status = FwdLockConv_Status_UnsupportedFileFormat;
+    } else {
+        status = FwdLockConv_Status_OK;
+    }
+    return status;
+}
+
+/**
+ * Writes the header of the output file.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_WriteHeader(FwdLockConv_Session_t *pSession,
+                                                    FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status;
+    if (pSession->contentType.length > UCHAR_MAX) {
+        status = FwdLockConv_Status_SyntaxError;
+    } else {
+        pSession->outputBufferSize = OUTPUT_BUFFER_SIZE_INCREMENT;
+        pOutput->fromConvertData.pBuffer = malloc(pSession->outputBufferSize);
+        if (pOutput->fromConvertData.pBuffer == NULL) {
+            status = FwdLockConv_Status_OutOfMemory;
+        } else {
+            size_t encryptedSessionKeyPos = TOP_HEADER_SIZE + pSession->contentType.length;
+            size_t dataSignaturePos = encryptedSessionKeyPos + pSession->encryptedSessionKeyLength;
+            size_t headerSignaturePos = dataSignaturePos + SHA1_HASH_SIZE;
+            pSession->dataOffset = headerSignaturePos + SHA1_HASH_SIZE;
+            memcpy(pSession->topHeader, topHeaderTemplate, sizeof topHeaderTemplate);
+            pSession->topHeader[CONTENT_TYPE_LENGTH_POS] =
+                    (unsigned char)pSession->contentType.length;
+            memcpy(pOutput->fromConvertData.pBuffer, pSession->topHeader, TOP_HEADER_SIZE);
+            memcpy((char *)pOutput->fromConvertData.pBuffer + TOP_HEADER_SIZE,
+                   pSession->contentType.ptr, pSession->contentType.length);
+            memcpy((char *)pOutput->fromConvertData.pBuffer + encryptedSessionKeyPos,
+                   pSession->pEncryptedSessionKey, pSession->encryptedSessionKeyLength);
+
+            // Set the signatures to all zeros for now; they will have to be updated later.
+            memset((char *)pOutput->fromConvertData.pBuffer + dataSignaturePos, 0,
+                   SHA1_HASH_SIZE);
+            memset((char *)pOutput->fromConvertData.pBuffer + headerSignaturePos, 0,
+                   SHA1_HASH_SIZE);
+
+            pOutput->fromConvertData.numBytes = pSession->dataOffset;
+            status = FwdLockConv_Status_OK;
+        }
+    }
+    return status;
+}
+
+/**
+ * Matches the MIME headers.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchMimeHeaders(FwdLockConv_Session_t *pSession,
+                                                         int ch,
+                                                         FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+    switch (pSession->scannerState) {
+    case FwdLockConv_ScannerState_WantsMimeHeaderNameStart:
+        if (FwdLockConv_IsMimeHeaderNameChar(ch)) {
+            pSession->mimeHeaderName.length = 0;
+            status = FwdLockConv_StringAppend(&pSession->mimeHeaderName, tolower(ch));
+            if (status == FwdLockConv_Status_OK) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderName;
+            }
+        } else if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeadersEnd;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsMimeHeaderName:
+        if (FwdLockConv_IsMimeHeaderNameChar(ch)) {
+            status = FwdLockConv_StringAppend(&pSession->mimeHeaderName, tolower(ch));
+        } else if (ch == ':') {
+            status = FwdLockConv_RecognizeMimeHeaderName(pSession);
+        } else if (FwdLockConv_IsWhitespace(ch)) {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameEnd;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsMimeHeaderNameEnd:
+        if (ch == ':') {
+            status = FwdLockConv_RecognizeMimeHeaderName(pSession);
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsContentTypeStart:
+        if (FwdLockConv_IsMimeHeaderValueChar(ch)) {
+            status = FwdLockConv_StringAppend(&pSession->contentType, tolower(ch));
+            if (status == FwdLockConv_Status_OK) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsContentType;
+            }
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsContentType:
+        if (FwdLockConv_IsMimeHeaderValueChar(ch)) {
+            status = FwdLockConv_StringAppend(&pSession->contentType, tolower(ch));
+        } else if (ch == ';') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        } else if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (FwdLockConv_IsWhitespace(ch)) {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderValueEnd;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsContentTransferEncodingStart:
+        if (ch == 'b' || ch == 'B') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_A_OR_I;
+        } else if (ch == '7' || ch == '8') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_B;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_A_OR_I:
+        if (ch == 'i' || ch == 'I') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_N;
+        } else if (ch == 'a' || ch == 'A') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_S;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_N:
+        if (ch == 'n' || ch == 'N') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_A;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_A:
+        if (ch == 'a' || ch == 'A') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_R;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_R:
+        if (ch == 'r' || ch == 'R') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_Y;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_Y:
+        if (ch == 'y' || ch == 'Y') {
+            pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_S:
+        if (ch == 's' || ch == 'S') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_E;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_E:
+        if (ch == 'e' || ch == 'E') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_6;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_6:
+        if (ch == '6') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_4;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_4:
+        if (ch == '4') {
+            pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Base64;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_B:
+        if (ch == 'b' || ch == 'B') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_I;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_I:
+        if (ch == 'i' || ch == 'I') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_T;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_T:
+        if (ch == 't' || ch == 'T') {
+            pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsContentTransferEncodingEnd:
+        if (ch == ';') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        } else if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (FwdLockConv_IsWhitespace(ch)) {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderValueEnd;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsMimeHeaderValueEnd:
+        if (ch == ';') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        } else if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsCR:
+        if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsLF:
+        if (ch == '\n') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameStart;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsMimeHeadersEnd:
+        if (ch == '\n') {
+            status = FwdLockConv_ApplyDefaults(pSession);
+            if (status == FwdLockConv_Status_OK) {
+                status = FwdLockConv_VerifyContentType(pSession);
+            }
+            if (status == FwdLockConv_Status_OK) {
+                status = FwdLockConv_WriteHeader(pSession, pOutput);
+            }
+            if (status == FwdLockConv_Status_OK) {
+                if (pSession->contentTransferEncoding ==
+                        FwdLockConv_ContentTransferEncoding_Binary) {
+                    pSession->parserState = FwdLockConv_ParserState_WantsBinaryEncodedData;
+                } else {
+                    pSession->parserState = FwdLockConv_ParserState_WantsBase64EncodedData;
+                }
+                pSession->scannerState = FwdLockConv_ScannerState_WantsByte1;
+            }
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    default:
+        status = FwdLockConv_Status_ProgramError;
+        break;
+    }
+    return status;
+}
+
+/**
+ * Increments the counter, treated as a 16-byte little-endian number, by one.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ */
+static void FwdLockConv_IncrementCounter(FwdLockConv_Session_t *pSession) {
+    size_t i = 0;
+    while ((++pSession->counter[i] == 0) && (++i < AES_BLOCK_SIZE))
+        ;
+}
+
+/**
+ * Encrypts the given character and writes it to the output buffer.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch The character to encrypt and write.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_WriteEncryptedChar(FwdLockConv_Session_t *pSession,
+                                                           unsigned char ch,
+                                                           FwdLockConv_Output_t *pOutput) {
+    if (pOutput->fromConvertData.numBytes == pSession->outputBufferSize) {
+        void *pBuffer;
+        pSession->outputBufferSize += OUTPUT_BUFFER_SIZE_INCREMENT;
+        pBuffer = realloc(pOutput->fromConvertData.pBuffer, pSession->outputBufferSize);
+        if (pBuffer == NULL) {
+            return FwdLockConv_Status_OutOfMemory;
+        }
+        pOutput->fromConvertData.pBuffer = pBuffer;
+    }
+    if (++pSession->keyStreamIndex == AES_BLOCK_SIZE) {
+        FwdLockConv_IncrementCounter(pSession);
+        pSession->keyStreamIndex = 0;
+    }
+    if (pSession->keyStreamIndex == 0) {
+        AES_encrypt(pSession->counter, pSession->keyStream, &pSession->encryptionRoundKeys);
+    }
+    ch ^= pSession->keyStream[pSession->keyStreamIndex];
+    ((unsigned char *)pOutput->fromConvertData.pBuffer)[pOutput->fromConvertData.numBytes++] = ch;
+    ++pSession->numDataBytes;
+    return FwdLockConv_Status_OK;
+}
+
+/**
+ * Matches binary-encoded content data and encrypts it, while looking out for the close delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchBinaryEncodedData(FwdLockConv_Session_t *pSession,
+                                                               int ch,
+                                                               FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+    switch (pSession->scannerState) {
+    case FwdLockConv_ScannerState_WantsByte1:
+        if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
+            // The partial match of the delimiter turned out to be spurious. Flush the matched bytes
+            // to the output buffer and start over.
+            size_t i;
+            for (i = 0; i < pSession->delimiterMatchPos; ++i) {
+                status = FwdLockConv_WriteEncryptedChar(pSession, pSession->delimiter[i], pOutput);
+                if (status != FwdLockConv_Status_OK) {
+                    return status;
+                }
+            }
+            pSession->delimiterMatchPos = 0;
+        }
+        if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
+            // The current character isn't part of the delimiter. Write it to the output buffer.
+            status = FwdLockConv_WriteEncryptedChar(pSession, ch, pOutput);
+        } else if (++pSession->delimiterMatchPos == pSession->delimiterLength) {
+            // The entire delimiter has been matched. The only valid characters now are the "--"
+            // that complete the close delimiter (no more message parts are expected).
+            pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsFirstDash:
+        if (ch == '-') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsSecondDash:
+        if (ch == '-') {
+            pSession->parserState = FwdLockConv_ParserState_Done;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    default:
+        status = FwdLockConv_Status_ProgramError;
+        break;
+    }
+    return status;
+}
+
+/**
+ * Checks whether a given character is valid in base64-encoded data.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in base64-encoded data.
+ */
+static int FwdLockConv_IsBase64Char(int ch) {
+    return 0 <= ch && ch <= 'z' && base64Values[ch] >= 0;
+}
+
+/**
+ * Matches base64-encoded content data and encrypts it, while looking out for the close delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchBase64EncodedData(FwdLockConv_Session_t *pSession,
+                                                               int ch,
+                                                               FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+    switch (pSession->scannerState) {
+    case FwdLockConv_ScannerState_WantsByte1:
+    case FwdLockConv_ScannerState_WantsByte1_AfterCRLF:
+        if (FwdLockConv_IsBase64Char(ch)) {
+            pSession->ch = base64Values[ch] << 2;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsByte2;
+        } else if (ch == '\r') {
+            pSession->savedScannerState = FwdLockConv_ScannerState_WantsByte1_AfterCRLF;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (ch == '-') {
+            if (pSession->scannerState == FwdLockConv_ScannerState_WantsByte1_AfterCRLF) {
+                pSession->delimiterMatchPos = 3;
+                pSession->scannerState = FwdLockConv_ScannerState_WantsDelimiter;
+            } else {
+                status = FwdLockConv_Status_SyntaxError;
+            }
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsByte2:
+        if (FwdLockConv_IsBase64Char(ch)) {
+            pSession->ch |= base64Values[ch] >> 4;
+            status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
+            if (status == FwdLockConv_Status_OK) {
+                pSession->ch = base64Values[ch] << 4;
+                pSession->scannerState = FwdLockConv_ScannerState_WantsByte3;
+            }
+        } else if (ch == '\r') {
+            pSession->savedScannerState = pSession->scannerState;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsByte3:
+        if (FwdLockConv_IsBase64Char(ch)) {
+            pSession->ch |= base64Values[ch] >> 2;
+            status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
+            if (status == FwdLockConv_Status_OK) {
+                pSession->ch = base64Values[ch] << 6;
+                pSession->scannerState = FwdLockConv_ScannerState_WantsByte4;
+            }
+        } else if (ch == '\r') {
+            pSession->savedScannerState = pSession->scannerState;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (ch == '=') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsPadding;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsByte4:
+        if (FwdLockConv_IsBase64Char(ch)) {
+            pSession->ch |= base64Values[ch];
+            status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
+            if (status == FwdLockConv_Status_OK) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsByte1;
+            }
+        } else if (ch == '\r') {
+            pSession->savedScannerState = pSession->scannerState;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (ch == '=') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsLF:
+        if (ch == '\n') {
+            pSession->scannerState = pSession->savedScannerState;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsPadding:
+        if (ch == '=') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsWhitespace:
+    case FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF:
+        if (ch == '\r') {
+            pSession->savedScannerState = FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (ch == '-') {
+            if (pSession->scannerState == FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF) {
+                pSession->delimiterMatchPos = 3;
+                pSession->scannerState = FwdLockConv_ScannerState_WantsDelimiter;
+            } else {
+                status = FwdLockConv_Status_SyntaxError;
+            }
+        } else if (FwdLockConv_IsWhitespace(ch)) {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsDelimiter:
+        if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
+            status = FwdLockConv_Status_SyntaxError;
+        } else if (++pSession->delimiterMatchPos == pSession->delimiterLength) {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsFirstDash:
+        if (ch == '-') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsSecondDash:
+        if (ch == '-') {
+            pSession->parserState = FwdLockConv_ParserState_Done;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    default:
+        status = FwdLockConv_Status_ProgramError;
+        break;
+    }
+    return status;
+}
+
+/**
+ * Pushes a single character into the converter's state machine.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_PushChar(FwdLockConv_Session_t *pSession,
+                                                 int ch,
+                                                 FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status;
+    ++pSession->numCharsConsumed;
+    switch (pSession->parserState) {
+    case FwdLockConv_ParserState_WantsOpenDelimiter:
+        status = FwdLockConv_MatchOpenDelimiter(pSession, ch);
+        break;
+    case FwdLockConv_ParserState_WantsMimeHeaders:
+        status = FwdLockConv_MatchMimeHeaders(pSession, ch, pOutput);
+        break;
+    case FwdLockConv_ParserState_WantsBinaryEncodedData:
+        status = FwdLockConv_MatchBinaryEncodedData(pSession, ch, pOutput);
+        break;
+    case FwdLockConv_ParserState_WantsBase64EncodedData:
+        status = FwdLockConv_MatchBase64EncodedData(pSession, ch, pOutput);
+        break;
+    case FwdLockConv_ParserState_Done:
+        status = FwdLockConv_Status_OK;
+        break;
+    default:
+        status = FwdLockConv_Status_ProgramError;
+        break;
+    }
+    return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_OpenSession(int *pSessionId, FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status;
+    if (pSessionId == NULL || pOutput == NULL) {
+        status = FwdLockConv_Status_InvalidArgument;
+    } else {
+        *pSessionId = FwdLockConv_AcquireSession();
+        if (*pSessionId < 0) {
+            status = FwdLockConv_Status_TooManySessions;
+        } else {
+            FwdLockConv_Session_t *pSession = sessionPtrs[*pSessionId];
+            pSession->encryptedSessionKeyLength = FwdLockGlue_GetEncryptedKeyLength(KEY_SIZE);
+            if (pSession->encryptedSessionKeyLength < AES_BLOCK_SIZE) {
+                // The encrypted session key is used as the CTR-mode nonce, so it must be at least
+                // the size of a single AES block.
+                status = FwdLockConv_Status_ProgramError;
+            } else {
+                pSession->pEncryptedSessionKey = malloc(pSession->encryptedSessionKeyLength);
+                if (pSession->pEncryptedSessionKey == NULL) {
+                    status = FwdLockConv_Status_OutOfMemory;
+                } else {
+                    if (!FwdLockGlue_GetRandomNumber(pSession->sessionKey, KEY_SIZE)) {
+                        status = FwdLockConv_Status_RandomNumberGenerationFailed;
+                    } else if (!FwdLockGlue_EncryptKey(pSession->sessionKey, KEY_SIZE,
+                                                       pSession->pEncryptedSessionKey,
+                                                       pSession->encryptedSessionKeyLength)) {
+                        status = FwdLockConv_Status_KeyEncryptionFailed;
+                    } else {
+                        status = FwdLockConv_DeriveKeys(pSession);
+                    }
+                    if (status == FwdLockConv_Status_OK) {
+                        memset(pSession->sessionKey, 0, KEY_SIZE); // Zero out key data.
+                        memcpy(pSession->counter, pSession->pEncryptedSessionKey, AES_BLOCK_SIZE);
+                        pSession->parserState = FwdLockConv_ParserState_WantsOpenDelimiter;
+                        pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+                        pSession->numCharsConsumed = 0;
+                        pSession->delimiterMatchPos = 0;
+                        pSession->mimeHeaderName = nullString;
+                        pSession->contentType = nullString;
+                        pSession->contentTransferEncoding =
+                                FwdLockConv_ContentTransferEncoding_Undefined;
+                        pSession->keyStreamIndex = -1;
+                        pOutput->fromConvertData.pBuffer = NULL;
+                        pOutput->fromConvertData.errorPos = INVALID_OFFSET;
+                    } else {
+                        free(pSession->pEncryptedSessionKey);
+                    }
+                }
+            }
+            if (status != FwdLockConv_Status_OK) {
+                FwdLockConv_ReleaseSession(*pSessionId);
+                *pSessionId = -1;
+            }
+        }
+    }
+    return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_ConvertData(int sessionId,
+                                             const void *pBuffer,
+                                             size_t numBytes,
+                                             FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status;
+    if (!FwdLockConv_IsValidSession(sessionId) || pBuffer == NULL || pOutput == NULL) {
+        status = FwdLockConv_Status_InvalidArgument;
+    } else {
+        size_t i;
+        FwdLockConv_Session_t *pSession = sessionPtrs[sessionId];
+        pSession->dataOffset = 0;
+        pSession->numDataBytes = 0;
+        pOutput->fromConvertData.numBytes = 0;
+        status = FwdLockConv_Status_OK;
+
+        for (i = 0; i < numBytes; ++i) {
+            status = FwdLockConv_PushChar(pSession, ((char *)pBuffer)[i], pOutput);
+            if (status != FwdLockConv_Status_OK) {
+                break;
+            }
+        }
+        if (status == FwdLockConv_Status_OK) {
+            // Update the data signature.
+            HMAC_Update(&pSession->signingContext,
+                        &((unsigned char *)pOutput->fromConvertData.pBuffer)[pSession->dataOffset],
+                        pSession->numDataBytes);
+        } else if (status == FwdLockConv_Status_SyntaxError) {
+            pOutput->fromConvertData.errorPos = pSession->numCharsConsumed;
+        }
+    }
+    return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_CloseSession(int sessionId, FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status;
+    if (!FwdLockConv_IsValidSession(sessionId) || pOutput == NULL) {
+        status = FwdLockConv_Status_InvalidArgument;
+    } else {
+        FwdLockConv_Session_t *pSession = sessionPtrs[sessionId];
+        free(pOutput->fromConvertData.pBuffer);
+        if (pSession->parserState != FwdLockConv_ParserState_Done) {
+            pOutput->fromCloseSession.errorPos = pSession->numCharsConsumed;
+            status = FwdLockConv_Status_SyntaxError;
+        } else {
+            // Finalize the data signature.
+            size_t signatureSize;
+            HMAC_Final(&pSession->signingContext, pOutput->fromCloseSession.signatures,
+                       &signatureSize);
+            if (signatureSize != SHA1_HASH_SIZE) {
+                status = FwdLockConv_Status_ProgramError;
+            } else {
+                // Calculate the header signature, which is a signature of the rest of the header
+                // including the data signature.
+                HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
+                HMAC_Update(&pSession->signingContext, pSession->topHeader, TOP_HEADER_SIZE);
+                HMAC_Update(&pSession->signingContext, (unsigned char *)pSession->contentType.ptr,
+                            pSession->contentType.length);
+                HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey,
+                            pSession->encryptedSessionKeyLength);
+                HMAC_Update(&pSession->signingContext, pOutput->fromCloseSession.signatures,
+                            signatureSize);
+                HMAC_Final(&pSession->signingContext, &pOutput->fromCloseSession.
+                           signatures[signatureSize], &signatureSize);
+                if (signatureSize != SHA1_HASH_SIZE) {
+                    status = FwdLockConv_Status_ProgramError;
+                } else {
+                    pOutput->fromCloseSession.fileOffset = TOP_HEADER_SIZE +
+                            pSession->contentType.length + pSession->encryptedSessionKeyLength;
+                    status = FwdLockConv_Status_OK;
+                }
+            }
+            pOutput->fromCloseSession.errorPos = INVALID_OFFSET;
+        }
+        free(pSession->mimeHeaderName.ptr);
+        free(pSession->contentType.ptr);
+        free(pSession->pEncryptedSessionKey);
+        HMAC_CTX_cleanup(&pSession->signingContext);
+        FwdLockConv_ReleaseSession(sessionId);
+    }
+    return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_ConvertOpenFile(int inputFileDesc,
+                                                 FwdLockConv_ReadFunc_t *fpReadFunc,
+                                                 int outputFileDesc,
+                                                 FwdLockConv_WriteFunc_t *fpWriteFunc,
+                                                 FwdLockConv_LSeekFunc_t *fpLSeekFunc,
+                                                 off64_t *pErrorPos) {
+    FwdLockConv_Status_t status;
+    if (pErrorPos != NULL) {
+        *pErrorPos = INVALID_OFFSET;
+    }
+    if (fpReadFunc == NULL || fpWriteFunc == NULL || fpLSeekFunc == NULL || inputFileDesc < 0 ||
+        outputFileDesc < 0) {
+        status = FwdLockConv_Status_InvalidArgument;
+    } else {
+        char *pReadBuffer = malloc(READ_BUFFER_SIZE);
+        if (pReadBuffer == NULL) {
+            status = FwdLockConv_Status_OutOfMemory;
+        } else {
+            int sessionId;
+            FwdLockConv_Output_t output;
+            status = FwdLockConv_OpenSession(&sessionId, &output);
+            if (status == FwdLockConv_Status_OK) {
+                ssize_t numBytesRead;
+                FwdLockConv_Status_t closeStatus;
+                while ((numBytesRead =
+                        fpReadFunc(inputFileDesc, pReadBuffer, READ_BUFFER_SIZE)) > 0) {
+                    status = FwdLockConv_ConvertData(sessionId, pReadBuffer, (size_t)numBytesRead,
+                                                     &output);
+                    if (status == FwdLockConv_Status_OK) {
+                        if (output.fromConvertData.pBuffer != NULL &&
+                            output.fromConvertData.numBytes > 0) {
+                            ssize_t numBytesWritten = fpWriteFunc(outputFileDesc,
+                                                                  output.fromConvertData.pBuffer,
+                                                                  output.fromConvertData.numBytes);
+                            if (numBytesWritten != (ssize_t)output.fromConvertData.numBytes) {
+                                status = FwdLockConv_Status_FileWriteError;
+                                break;
+                            }
+                        }
+                    } else {
+                        if (status == FwdLockConv_Status_SyntaxError && pErrorPos != NULL) {
+                            *pErrorPos = output.fromConvertData.errorPos;
+                        }
+                        break;
+                    }
+                } // end while
+                if (numBytesRead < 0) {
+                    status = FwdLockConv_Status_FileReadError;
+                }
+                closeStatus = FwdLockConv_CloseSession(sessionId, &output);
+                if (status == FwdLockConv_Status_OK) {
+                    if (closeStatus != FwdLockConv_Status_OK) {
+                        if (closeStatus == FwdLockConv_Status_SyntaxError && pErrorPos != NULL) {
+                            *pErrorPos = output.fromCloseSession.errorPos;
+                        }
+                        status = closeStatus;
+                    } else if (fpLSeekFunc(outputFileDesc, output.fromCloseSession.fileOffset,
+                                           SEEK_SET) < 0) {
+                        status = FwdLockConv_Status_FileSeekError;
+                    } else if (fpWriteFunc(outputFileDesc, output.fromCloseSession.signatures,
+                                           FWD_LOCK_SIGNATURES_SIZE) != FWD_LOCK_SIGNATURES_SIZE) {
+                        status = FwdLockConv_Status_FileWriteError;
+                    }
+                }
+            }
+            free(pReadBuffer);
+        }
+    }
+    return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_ConvertFile(const char *pInputFilename,
+                                             const char *pOutputFilename,
+                                             off64_t *pErrorPos) {
+    FwdLockConv_Status_t status;
+    if (pErrorPos != NULL) {
+        *pErrorPos = INVALID_OFFSET;
+    }
+    if (pInputFilename == NULL || pOutputFilename == NULL) {
+        status = FwdLockConv_Status_InvalidArgument;
+    } else {
+        int inputFileDesc = open(pInputFilename, O_RDONLY);
+        if (inputFileDesc < 0) {
+            status = FwdLockConv_Status_FileNotFound;
+        } else {
+            int outputFileDesc = open(pOutputFilename, O_CREAT | O_TRUNC | O_WRONLY,
+                                      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+            if (outputFileDesc < 0) {
+                status = FwdLockConv_Status_FileCreationFailed;
+            } else {
+                status = FwdLockConv_ConvertOpenFile(inputFileDesc, read, outputFileDesc, write,
+                                                     lseek64, pErrorPos);
+                if (close(outputFileDesc) == 0 && status != FwdLockConv_Status_OK) {
+                    remove(pOutputFilename);
+                }
+            }
+            (void)close(inputFileDesc);
+        }
+    }
+    return status;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h
new file mode 100644
index 0000000..e20c0c3
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h
@@ -0,0 +1,282 @@
+/*
+ * 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.
+ */
+
+#ifndef __FWDLOCKCONV_H__
+#define __FWDLOCKCONV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/**
+ * The size of the data and header signatures combined. The signatures are adjacent to each other in
+ * the produced output file.
+ */
+#define FWD_LOCK_SIGNATURES_SIZE (2 * 20)
+
+/**
+ * Data type for the output from FwdLockConv_ConvertData.
+ */
+typedef struct FwdLockConv_ConvertData_Output {
+    /// The converted data.
+    void *pBuffer;
+
+    /// The size of the converted data.
+    size_t numBytes;
+
+    /// The file position where the error occurred, in the case of a syntax error.
+    off64_t errorPos;
+} FwdLockConv_ConvertData_Output_t;
+
+/**
+ * Data type for the output from FwdLockConv_CloseSession.
+ */
+typedef struct FwdLockConv_CloseSession_Output {
+    /// The final set of signatures.
+    unsigned char signatures[FWD_LOCK_SIGNATURES_SIZE];
+
+    /// The offset in the produced output file where the signatures are located.
+    off64_t fileOffset;
+
+    /// The file position where the error occurred, in the case of a syntax error.
+    off64_t errorPos;
+} FwdLockConv_CloseSession_Output_t;
+
+/**
+ * Data type for the output from the conversion process.
+ */
+typedef union FwdLockConv_Output {
+    FwdLockConv_ConvertData_Output_t fromConvertData;
+    FwdLockConv_CloseSession_Output_t fromCloseSession;
+} FwdLockConv_Output_t;
+
+/**
+ * Data type for the Posix-style read function used by the converter in pull mode.
+ *
+ * @param[in] fileDesc The file descriptor of a file opened for reading.
+ * @param[out] pBuffer A reference to the buffer that should receive the read data.
+ * @param[in] numBytes The number of bytes to read.
+ *
+ * @return The number of bytes read.
+ * @retval -1 Failure.
+ */
+typedef ssize_t FwdLockConv_ReadFunc_t(int fileDesc, void *pBuffer, size_t numBytes);
+
+/**
+ * Data type for the Posix-style write function used by the converter in pull mode.
+ *
+ * @param[in] fileDesc The file descriptor of a file opened for writing.
+ * @param[in] pBuffer A reference to the buffer containing the data to be written.
+ * @param[in] numBytes The number of bytes to write.
+ *
+ * @return The number of bytes written.
+ * @retval -1 Failure.
+ */
+typedef ssize_t FwdLockConv_WriteFunc_t(int fileDesc, const void *pBuffer, size_t numBytes);
+
+/**
+ * Data type for the Posix-style lseek function used by the converter in pull mode.
+ *
+ * @param[in] fileDesc The file descriptor of a file opened for writing.
+ * @param[in] offset The offset with which to update the file position.
+ * @param[in] whence One of SEEK_SET, SEEK_CUR, and SEEK_END.
+ *
+ * @return The new file position.
+ * @retval ((off64_t)-1) Failure.
+ */
+typedef off64_t FwdLockConv_LSeekFunc_t(int fileDesc, off64_t offset, int whence);
+
+/**
+ * The status codes returned by the converter functions.
+ */
+typedef enum FwdLockConv_Status {
+    /// The operation was successful.
+    FwdLockConv_Status_OK = 0,
+
+    /// An actual argument to the function is invalid (a program error on the caller's part).
+    FwdLockConv_Status_InvalidArgument = 1,
+
+    /// There is not enough free dynamic memory to complete the operation.
+    FwdLockConv_Status_OutOfMemory = 2,
+
+    /// An error occurred while opening the input file.
+    FwdLockConv_Status_FileNotFound = 3,
+
+    /// An error occurred while creating the output file.
+    FwdLockConv_Status_FileCreationFailed = 4,
+
+    /// An error occurred while reading from the input file.
+    FwdLockConv_Status_FileReadError = 5,
+
+    /// An error occurred while writing to the output file.
+    FwdLockConv_Status_FileWriteError = 6,
+
+    /// An error occurred while seeking to a new file position within the output file.
+    FwdLockConv_Status_FileSeekError = 7,
+
+    /// The input file is not a syntactically correct OMA DRM v1 Forward Lock file.
+    FwdLockConv_Status_SyntaxError = 8,
+
+    /// Support for this DRM file format has been disabled in the current product configuration.
+    FwdLockConv_Status_UnsupportedFileFormat = 9,
+
+    /// The content transfer encoding is not one of "binary", "base64", "7bit", or "8bit"
+    /// (case-insensitive).
+    FwdLockConv_Status_UnsupportedContentTransferEncoding = 10,
+
+    /// The generation of a random number failed.
+    FwdLockConv_Status_RandomNumberGenerationFailed = 11,
+
+    /// Key encryption failed.
+    FwdLockConv_Status_KeyEncryptionFailed = 12,
+
+    /// The calculation of a keyed hash for integrity protection failed.
+    FwdLockConv_Status_IntegrityProtectionFailed = 13,
+
+    /// There are too many ongoing sessions for another one to be opened.
+    FwdLockConv_Status_TooManySessions = 14,
+
+    /// An unexpected error occurred.
+    FwdLockConv_Status_ProgramError = 15
+} FwdLockConv_Status_t;
+
+/**
+ * Opens a session for converting an OMA DRM v1 Forward Lock file to the internal Forward Lock file
+ * format.
+ *
+ * @param[out] pSessionId The session ID.
+ * @param[out] pOutput The output from the conversion process (initialized).
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_TooManySessions
+ */
+FwdLockConv_Status_t FwdLockConv_OpenSession(int *pSessionId, FwdLockConv_Output_t *pOutput);
+
+/**
+ * Supplies the converter with data to convert. The caller is expected to write the converted data
+ * to file. Can be called an arbitrary number of times.
+ *
+ * @param[in] sessionId The session ID.
+ * @param[in] pBuffer A reference to a buffer containing the data to convert.
+ * @param[in] numBytes The number of bytes to convert.
+ * @param[in,out] pOutput The output from the conversion process (allocated/reallocated).
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_SyntaxError
+ * @retval FwdLockConv_Status_UnsupportedFileFormat
+ * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding
+ * @retval FwdLockConv_Status_RandomNumberGenerationFailed
+ * @retval FwdLockConv_Status_KeyEncryptionFailed
+ * @retval FwdLockConv_Status_DataEncryptionFailed
+ */
+FwdLockConv_Status_t FwdLockConv_ConvertData(int sessionId,
+                                             const void *pBuffer,
+                                             size_t numBytes,
+                                             FwdLockConv_Output_t *pOutput);
+
+/**
+ * Closes a session for converting an OMA DRM v1 Forward Lock file to the internal Forward Lock
+ * file format. The caller must update the produced output file at the indicated file offset with
+ * the final set of signatures.
+ *
+ * @param[in] sessionId The session ID.
+ * @param[in,out] pOutput The output from the conversion process (deallocated and overwritten).
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_IntegrityProtectionFailed
+ */
+FwdLockConv_Status_t FwdLockConv_CloseSession(int sessionId, FwdLockConv_Output_t *pOutput);
+
+/**
+ * Converts an open OMA DRM v1 Forward Lock file to the internal Forward Lock file format in pull
+ * mode.
+ *
+ * @param[in] inputFileDesc The file descriptor of the open input file.
+ * @param[in] fpReadFunc A reference to a read function that can operate on the open input file.
+ * @param[in] outputFileDesc The file descriptor of the open output file.
+ * @param[in] fpWriteFunc A reference to a write function that can operate on the open output file.
+ * @param[in] fpLSeekFunc A reference to an lseek function that can operate on the open output file.
+ * @param[out] pErrorPos
+ *   The file position where the error occurred, in the case of a syntax error. May be NULL.
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_FileReadError
+ * @retval FwdLockConv_Status_FileWriteError
+ * @retval FwdLockConv_Status_FileSeekError
+ * @retval FwdLockConv_Status_SyntaxError
+ * @retval FwdLockConv_Status_UnsupportedFileFormat
+ * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding
+ * @retval FwdLockConv_Status_RandomNumberGenerationFailed
+ * @retval FwdLockConv_Status_KeyEncryptionFailed
+ * @retval FwdLockConv_Status_DataEncryptionFailed
+ * @retval FwdLockConv_Status_IntegrityProtectionFailed
+ * @retval FwdLockConv_Status_TooManySessions
+ */
+FwdLockConv_Status_t FwdLockConv_ConvertOpenFile(int inputFileDesc,
+                                                 FwdLockConv_ReadFunc_t *fpReadFunc,
+                                                 int outputFileDesc,
+                                                 FwdLockConv_WriteFunc_t *fpWriteFunc,
+                                                 FwdLockConv_LSeekFunc_t *fpLSeekFunc,
+                                                 off64_t *pErrorPos);
+
+/**
+ * Converts an OMA DRM v1 Forward Lock file to the internal Forward Lock file format in pull mode.
+ *
+ * @param[in] pInputFilename A reference to the input filename.
+ * @param[in] pOutputFilename A reference to the output filename.
+ * @param[out] pErrorPos
+ *   The file position where the error occurred, in the case of a syntax error. May be NULL.
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_FileNotFound
+ * @retval FwdLockConv_Status_FileCreationFailed
+ * @retval FwdLockConv_Status_FileReadError
+ * @retval FwdLockConv_Status_FileWriteError
+ * @retval FwdLockConv_Status_FileSeekError
+ * @retval FwdLockConv_Status_SyntaxError
+ * @retval FwdLockConv_Status_UnsupportedFileFormat
+ * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding
+ * @retval FwdLockConv_Status_RandomNumberGenerationFailed
+ * @retval FwdLockConv_Status_KeyEncryptionFailed
+ * @retval FwdLockConv_Status_DataEncryptionFailed
+ * @retval FwdLockConv_Status_IntegrityProtectionFailed
+ * @retval FwdLockConv_Status_TooManySessions
+ */
+FwdLockConv_Status_t FwdLockConv_ConvertFile(const char *pInputFilename,
+                                             const char *pOutputFilename,
+                                             off64_t *pErrorPos);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __FWDLOCKCONV_H__
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk
new file mode 100644
index 0000000..b625edf
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk
@@ -0,0 +1,37 @@
+#
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    FwdLockFile.c
+
+LOCAL_C_INCLUDES := \
+    frameworks/base/drm/libdrmframework/plugins/forward-lock/internal-format/common \
+    external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := libcrypto
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_MODULE := libfwdlock-decoder
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
new file mode 100644
index 0000000..98284e72
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
@@ -0,0 +1,447 @@
+/*
+ * 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.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <openssl/aes.h>
+#include <openssl/hmac.h>
+
+#include "FwdLockFile.h"
+#include "FwdLockGlue.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define INVALID_OFFSET ((off64_t)-1)
+
+#define INVALID_BLOCK_INDEX ((uint64_t)-1)
+
+#define MAX_NUM_SESSIONS 128
+
+#define KEY_SIZE AES_BLOCK_SIZE
+#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
+
+#define SHA1_HASH_SIZE 20
+#define SHA1_BLOCK_SIZE 64
+
+#define FWD_LOCK_VERSION 0
+#define FWD_LOCK_SUBFORMAT 0
+#define USAGE_RESTRICTION_FLAGS 0
+#define CONTENT_TYPE_LENGTH_POS 7
+#define TOP_HEADER_SIZE 8
+
+#define SIG_CALC_BUFFER_SIZE (16 * SHA1_BLOCK_SIZE)
+
+/**
+ * Data type for the per-file state information needed by the decoder.
+ */
+typedef struct FwdLockFile_Session {
+    int fileDesc;
+    unsigned char topHeader[TOP_HEADER_SIZE];
+    char *pContentType;
+    size_t contentTypeLength;
+    void *pEncryptedSessionKey;
+    size_t encryptedSessionKeyLength;
+    unsigned char dataSignature[SHA1_HASH_SIZE];
+    unsigned char headerSignature[SHA1_HASH_SIZE];
+    off64_t dataOffset;
+    off64_t filePos;
+    AES_KEY encryptionRoundKeys;
+    HMAC_CTX signingContext;
+    unsigned char keyStream[AES_BLOCK_SIZE];
+    uint64_t blockIndex;
+} FwdLockFile_Session_t;
+
+static FwdLockFile_Session_t *sessionPtrs[MAX_NUM_SESSIONS] = { NULL };
+
+static pthread_mutex_t sessionAcquisitionMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static const unsigned char topHeaderTemplate[] =
+    { 'F', 'W', 'L', 'K', FWD_LOCK_VERSION, FWD_LOCK_SUBFORMAT, USAGE_RESTRICTION_FLAGS };
+
+/**
+ * Acquires an unused file session for the given file descriptor.
+ *
+ * @param[in] fileDesc A file descriptor.
+ *
+ * @return A session ID.
+ */
+static int FwdLockFile_AcquireSession(int fileDesc) {
+    int sessionId = -1;
+    if (fileDesc < 0) {
+        errno = EBADF;
+    } else {
+        int i;
+        pthread_mutex_lock(&sessionAcquisitionMutex);
+        for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
+            int candidateSessionId = (fileDesc + i) % MAX_NUM_SESSIONS;
+            if (sessionPtrs[candidateSessionId] == NULL) {
+                sessionPtrs[candidateSessionId] = malloc(sizeof **sessionPtrs);
+                if (sessionPtrs[candidateSessionId] != NULL) {
+                    sessionPtrs[candidateSessionId]->fileDesc = fileDesc;
+                    sessionPtrs[candidateSessionId]->pContentType = NULL;
+                    sessionPtrs[candidateSessionId]->pEncryptedSessionKey = NULL;
+                    sessionId = candidateSessionId;
+                }
+                break;
+            }
+        }
+        pthread_mutex_unlock(&sessionAcquisitionMutex);
+        if (i == MAX_NUM_SESSIONS) {
+            errno = ENFILE;
+        }
+    }
+    return sessionId;
+}
+
+/**
+ * Finds the file session associated to the given file descriptor.
+ *
+ * @param[in] fileDesc A file descriptor.
+ *
+ * @return A session ID.
+ */
+static int FwdLockFile_FindSession(int fileDesc) {
+    int sessionId = -1;
+    if (fileDesc < 0) {
+        errno = EBADF;
+    } else {
+        int i;
+        pthread_mutex_lock(&sessionAcquisitionMutex);
+        for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
+            int candidateSessionId = (fileDesc + i) % MAX_NUM_SESSIONS;
+            if (sessionPtrs[candidateSessionId] != NULL &&
+                sessionPtrs[candidateSessionId]->fileDesc == fileDesc) {
+                sessionId = candidateSessionId;
+                break;
+            }
+        }
+        pthread_mutex_unlock(&sessionAcquisitionMutex);
+        if (i == MAX_NUM_SESSIONS) {
+            errno = EBADF;
+        }
+    }
+    return sessionId;
+}
+
+/**
+ * Releases a file session.
+ *
+ * @param[in] sessionID A session ID.
+ */
+static void FwdLockFile_ReleaseSession(int sessionId) {
+    pthread_mutex_lock(&sessionAcquisitionMutex);
+    assert(0 <= sessionId && sessionId < MAX_NUM_SESSIONS && sessionPtrs[sessionId] != NULL);
+    free(sessionPtrs[sessionId]->pContentType);
+    free(sessionPtrs[sessionId]->pEncryptedSessionKey);
+    memset(sessionPtrs[sessionId], 0, sizeof *sessionPtrs[sessionId]); // Zero out key data.
+    free(sessionPtrs[sessionId]);
+    sessionPtrs[sessionId] = NULL;
+    pthread_mutex_unlock(&sessionAcquisitionMutex);
+}
+
+/**
+ * Derives keys for encryption and signing from the encrypted session key.
+ *
+ * @param[in,out] pSession A reference to a file session.
+ *
+ * @return A Boolean value indicating whether key derivation was successful.
+ */
+static int FwdLockFile_DeriveKeys(FwdLockFile_Session_t * pSession) {
+    int result;
+    struct FwdLockFile_DeriveKeys_Data {
+        AES_KEY sessionRoundKeys;
+        unsigned char value[KEY_SIZE];
+        unsigned char key[KEY_SIZE];
+    } *pData = malloc(sizeof *pData);
+    if (pData == NULL) {
+        result = FALSE;
+    } else {
+        result = FwdLockGlue_DecryptKey(pSession->pEncryptedSessionKey,
+                                        pSession->encryptedSessionKeyLength, pData->key, KEY_SIZE);
+        if (result) {
+            if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS, &pData->sessionRoundKeys) != 0) {
+                result = FALSE;
+            } else {
+                // Encrypt the 16-byte value {0, 0, ..., 0} to produce the encryption key.
+                memset(pData->value, 0, KEY_SIZE);
+                AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+                if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS,
+                                        &pSession->encryptionRoundKeys) != 0) {
+                    result = FALSE;
+                } else {
+                    // Encrypt the 16-byte value {1, 0, ..., 0} to produce the signing key.
+                    ++pData->value[0];
+                    AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+                    HMAC_CTX_init(&pSession->signingContext);
+                    HMAC_Init_ex(&pSession->signingContext, pData->key, KEY_SIZE, EVP_sha1(), NULL);
+                }
+            }
+        }
+        if (!result) {
+            errno = ENOSYS;
+        }
+        memset(pData, 0, sizeof pData); // Zero out key data.
+        free(pData);
+    }
+    return result;
+}
+
+/**
+ * Calculates the counter, treated as a 16-byte little-endian number, used to generate the keystream
+ * for the given block.
+ *
+ * @param[in] pNonce A reference to the nonce.
+ * @param[in] blockIndex The index number of the block.
+ * @param[out] pCounter A reference to the counter.
+ */
+static void FwdLockFile_CalculateCounter(const unsigned char *pNonce,
+                                         uint64_t blockIndex,
+                                         unsigned char *pCounter) {
+    unsigned char carry = 0;
+    size_t i = 0;
+    for (; i < sizeof blockIndex; ++i) {
+        unsigned char part = pNonce[i] + (unsigned char)(blockIndex >> (i * CHAR_BIT));
+        pCounter[i] = part + carry;
+        carry = (part < pNonce[i] || pCounter[i] < part) ? 1 : 0;
+    }
+    for (; i < AES_BLOCK_SIZE; ++i) {
+        pCounter[i] = pNonce[i] + carry;
+        carry = (pCounter[i] < pNonce[i]) ? 1 : 0;
+    }
+}
+
+/**
+ * Decrypts the byte at the current file position using AES-128-CTR. In CTR (or "counter") mode,
+ * encryption and decryption are performed using the same algorithm.
+ *
+ * @param[in,out] pSession A reference to a file session.
+ * @param[in] pByte The byte to decrypt.
+ */
+void FwdLockFile_DecryptByte(FwdLockFile_Session_t * pSession, unsigned char *pByte) {
+    uint64_t blockIndex = pSession->filePos / AES_BLOCK_SIZE;
+    uint64_t blockOffset = pSession->filePos % AES_BLOCK_SIZE;
+    if (blockIndex != pSession->blockIndex) {
+        // The first 16 bytes of the encrypted session key is used as the nonce.
+        unsigned char counter[AES_BLOCK_SIZE];
+        FwdLockFile_CalculateCounter(pSession->pEncryptedSessionKey, blockIndex, counter);
+        AES_encrypt(counter, pSession->keyStream, &pSession->encryptionRoundKeys);
+        pSession->blockIndex = blockIndex;
+    }
+    *pByte ^= pSession->keyStream[blockOffset];
+}
+
+int FwdLockFile_attach(int fileDesc) {
+    int sessionId = FwdLockFile_AcquireSession(fileDesc);
+    if (sessionId >= 0) {
+        FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+        int isSuccess = FALSE;
+        if (read(fileDesc, pSession->topHeader, TOP_HEADER_SIZE) == TOP_HEADER_SIZE &&
+                memcmp(pSession->topHeader, topHeaderTemplate, sizeof topHeaderTemplate) == 0) {
+            pSession->contentTypeLength = pSession->topHeader[CONTENT_TYPE_LENGTH_POS];
+            assert(pSession->contentTypeLength <= UCHAR_MAX); // Untaint scalar for code checkers.
+            pSession->pContentType = malloc(pSession->contentTypeLength + 1);
+            if (pSession->pContentType != NULL &&
+                    read(fileDesc, pSession->pContentType, pSession->contentTypeLength) ==
+                            (ssize_t)pSession->contentTypeLength) {
+                pSession->pContentType[pSession->contentTypeLength] = '\0';
+                pSession->encryptedSessionKeyLength = FwdLockGlue_GetEncryptedKeyLength(KEY_SIZE);
+                pSession->pEncryptedSessionKey = malloc(pSession->encryptedSessionKeyLength);
+                if (pSession->pEncryptedSessionKey != NULL &&
+                        read(fileDesc, pSession->pEncryptedSessionKey,
+                             pSession->encryptedSessionKeyLength) ==
+                                (ssize_t)pSession->encryptedSessionKeyLength &&
+                        read(fileDesc, pSession->dataSignature, SHA1_HASH_SIZE) ==
+                                SHA1_HASH_SIZE &&
+                        read(fileDesc, pSession->headerSignature, SHA1_HASH_SIZE) ==
+                                SHA1_HASH_SIZE) {
+                    isSuccess = FwdLockFile_DeriveKeys(pSession);
+                }
+            }
+        }
+        if (isSuccess) {
+            pSession->dataOffset = pSession->contentTypeLength +
+                    pSession->encryptedSessionKeyLength + TOP_HEADER_SIZE + 2 * SHA1_HASH_SIZE;
+            pSession->filePos = 0;
+            pSession->blockIndex = INVALID_BLOCK_INDEX;
+        } else {
+            FwdLockFile_ReleaseSession(sessionId);
+            sessionId = -1;
+        }
+    }
+    return (sessionId >= 0) ? 0 : -1;
+}
+
+int FwdLockFile_open(const char *pFilename) {
+    int fileDesc = open(pFilename, O_RDONLY);
+    if (fileDesc >= 0 && FwdLockFile_attach(fileDesc) < 0) {
+        (void)close(fileDesc);
+        fileDesc = -1;
+    }
+    return fileDesc;
+}
+
+ssize_t FwdLockFile_read(int fileDesc, void *pBuffer, size_t numBytes) {
+    ssize_t numBytesRead;
+    int sessionId = FwdLockFile_FindSession(fileDesc);
+    if (sessionId < 0) {
+        numBytesRead = -1;
+    } else {
+        FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+        ssize_t i;
+        numBytesRead = read(pSession->fileDesc, pBuffer, numBytes);
+        for (i = 0; i < numBytesRead; ++i) {
+            FwdLockFile_DecryptByte(pSession, &((unsigned char *)pBuffer)[i]);
+            ++pSession->filePos;
+        }
+    }
+    return numBytesRead;
+}
+
+off64_t FwdLockFile_lseek(int fileDesc, off64_t offset, int whence) {
+    off64_t newFilePos;
+    int sessionId = FwdLockFile_FindSession(fileDesc);
+    if (sessionId < 0) {
+        newFilePos = INVALID_OFFSET;
+    } else {
+        FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+        switch (whence) {
+        case SEEK_SET:
+            newFilePos = lseek64(pSession->fileDesc, pSession->dataOffset + offset, whence);
+            break;
+        case SEEK_CUR:
+        case SEEK_END:
+            newFilePos = lseek64(pSession->fileDesc, offset, whence);
+            break;
+        default:
+            errno = EINVAL;
+            newFilePos = INVALID_OFFSET;
+            break;
+        }
+        if (newFilePos != INVALID_OFFSET) {
+            if (newFilePos < pSession->dataOffset) {
+                // The new file position is illegal for an internal Forward Lock file. Restore the
+                // original file position.
+                (void)lseek64(pSession->fileDesc, pSession->dataOffset + pSession->filePos,
+                              SEEK_SET);
+                errno = EINVAL;
+                newFilePos = INVALID_OFFSET;
+            } else {
+                // The return value should be the file position that lseek64() would have returned
+                // for the embedded content file.
+                pSession->filePos = newFilePos - pSession->dataOffset;
+                newFilePos = pSession->filePos;
+            }
+        }
+    }
+    return newFilePos;
+}
+
+int FwdLockFile_detach(int fileDesc) {
+    int sessionId = FwdLockFile_FindSession(fileDesc);
+    if (sessionId < 0) {
+        return -1;
+    }
+    HMAC_CTX_cleanup(&sessionPtrs[sessionId]->signingContext);
+    FwdLockFile_ReleaseSession(sessionId);
+    return 0;
+}
+
+int FwdLockFile_close(int fileDesc) {
+    return (FwdLockFile_detach(fileDesc) == 0) ? close(fileDesc) : -1;
+}
+
+int FwdLockFile_CheckDataIntegrity(int fileDesc) {
+    int result;
+    int sessionId = FwdLockFile_FindSession(fileDesc);
+    if (sessionId < 0) {
+        result = FALSE;
+    } else {
+        struct FwdLockFile_CheckDataIntegrity_Data {
+            unsigned char signature[SHA1_HASH_SIZE];
+            unsigned char buffer[SIG_CALC_BUFFER_SIZE];
+        } *pData = malloc(sizeof *pData);
+        if (pData == NULL) {
+            result = FALSE;
+        } else {
+            FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+            if (lseek64(pSession->fileDesc, pSession->dataOffset, SEEK_SET) !=
+                    pSession->dataOffset) {
+                result = FALSE;
+            } else {
+                ssize_t numBytesRead;
+                size_t signatureSize = SHA1_HASH_SIZE;
+                while ((numBytesRead =
+                        read(pSession->fileDesc, pData->buffer, SIG_CALC_BUFFER_SIZE)) > 0) {
+                    HMAC_Update(&pSession->signingContext, pData->buffer, (size_t)numBytesRead);
+                }
+                if (numBytesRead < 0) {
+                    result = FALSE;
+                } else {
+                    HMAC_Final(&pSession->signingContext, pData->signature, &signatureSize);
+                    assert(signatureSize == SHA1_HASH_SIZE);
+                    result = memcmp(pData->signature, pSession->dataSignature, signatureSize) == 0;
+                }
+                HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
+                (void)lseek64(pSession->fileDesc, pSession->dataOffset + pSession->filePos,
+                              SEEK_SET);
+            }
+            free(pData);
+        }
+    }
+    return result;
+}
+
+int FwdLockFile_CheckHeaderIntegrity(int fileDesc) {
+    int result;
+    int sessionId = FwdLockFile_FindSession(fileDesc);
+    if (sessionId < 0) {
+        result = FALSE;
+    } else {
+        FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+        unsigned char signature[SHA1_HASH_SIZE];
+        size_t signatureSize = SHA1_HASH_SIZE;
+        HMAC_Update(&pSession->signingContext, pSession->topHeader, TOP_HEADER_SIZE);
+        HMAC_Update(&pSession->signingContext, (unsigned char *)pSession->pContentType,
+                    pSession->contentTypeLength);
+        HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey,
+                    pSession->encryptedSessionKeyLength);
+        HMAC_Update(&pSession->signingContext, pSession->dataSignature, signatureSize);
+        HMAC_Final(&pSession->signingContext, signature, &signatureSize);
+        assert(signatureSize == SHA1_HASH_SIZE);
+        result = memcmp(signature, pSession->headerSignature, signatureSize) == 0;
+        HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
+    }
+    return result;
+}
+
+int FwdLockFile_CheckIntegrity(int fileDesc) {
+    return FwdLockFile_CheckHeaderIntegrity(fileDesc) && FwdLockFile_CheckDataIntegrity(fileDesc);
+}
+
+const char *FwdLockFile_GetContentType(int fileDesc) {
+    int sessionId = FwdLockFile_FindSession(fileDesc);
+    if (sessionId < 0) {
+        return NULL;
+    }
+    return sessionPtrs[sessionId]->pContentType;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h
new file mode 100644
index 0000000..fc64050
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+#ifndef __FWDLOCKFILE_H__
+#define __FWDLOCKFILE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/**
+ * Attaches to an open Forward Lock file. The file position is assumed to be at the beginning of the
+ * file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A status code.
+ * @retval 0 Success.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_attach(int fileDesc);
+
+/**
+ * Opens a Forward Lock file for reading.
+ *
+ * @param[in] pFilename A reference to a filename.
+ *
+ * @return A file descriptor.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_open(const char *pFilename);
+
+/**
+ * Reads the specified number of bytes from an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ * @param[out] pBuffer A reference to the buffer that should receive the read data.
+ * @param[in] numBytes The number of bytes to read.
+ *
+ * @return The number of bytes read.
+ * @retval -1 Failure.
+ */
+ssize_t FwdLockFile_read(int fileDesc, void *pBuffer, size_t numBytes);
+
+/**
+ * Updates the file position within an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ * @param[in] offset The offset with which to update the file position.
+ * @param[in] whence One of SEEK_SET, SEEK_CUR, and SEEK_END.
+ *
+ * @return The new file position.
+ * @retval ((off64_t)-1) Failure.
+ */
+off64_t FwdLockFile_lseek(int fileDesc, off64_t offset, int whence);
+
+/**
+ * Detaches from an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A status code.
+ * @retval 0 Success.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_detach(int fileDesc);
+
+/**
+ * Closes an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A status code.
+ * @retval 0 Success.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_close(int fileDesc);
+
+/**
+ * Checks the data integrity of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A Boolean value indicating whether the integrity check was successful.
+ */
+int FwdLockFile_CheckDataIntegrity(int fileDesc);
+
+/**
+ * Checks the header integrity of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A Boolean value indicating whether the integrity check was successful.
+ */
+int FwdLockFile_CheckHeaderIntegrity(int fileDesc);
+
+/**
+ * Checks both the data and header integrity of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A Boolean value indicating whether the integrity check was successful.
+ */
+int FwdLockFile_CheckIntegrity(int fileDesc);
+
+/**
+ * Returns the content type of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return
+ *   A reference to the content type. The reference remains valid as long as the file is kept open.
+ */
+const char *FwdLockFile_GetContentType(int fileDesc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __FWDLOCKFILE_H__
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html
new file mode 100755
index 0000000..8f95cd2
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html
@@ -0,0 +1,1039 @@
+<html>
+
+<head>
+<meta http-equiv=Content-Type content="text/html; charset=windows-1252">
+<meta name=Generator content="Microsoft Word 12 (filtered)">
+<title>Forward Lock Converter and Decoder</title>
+<style>
+<!--
+ /* Font Definitions */
+ @font-face
+	{font-family:SimSun;
+	panose-1:2 1 6 0 3 1 1 1 1 1;}
+@font-face
+	{font-family:"Cambria Math";
+	panose-1:2 4 5 3 5 4 6 3 2 4;}
+@font-face
+	{font-family:Tahoma;
+	panose-1:2 11 6 4 3 5 4 4 2 4;}
+@font-face
+	{font-family:"Lucida Console","DejaVu Sans Mono";
+	panose-1:2 11 6 9 4 5 4 2 2 4;}
+@font-face
+	{font-family:"\@SimSun";
+	panose-1:2 1 6 0 3 1 1 1 1 1;}
+ /* Style Definitions */
+ p.MsoNormal, li.MsoNormal, div.MsoNormal
+	{margin:0cm;
+	margin-bottom:.0001pt;
+	font-size:12.0pt;
+	font-family:"Times New Roman","serif";}
+h1
+	{margin-right:0cm;
+	margin-left:21.6pt;
+	text-indent:-21.6pt;
+	page-break-after:avoid;
+	font-size:16.0pt;
+	font-family:"Arial","sans-serif";}
+h2
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:28.8pt;
+	text-indent:-28.8pt;
+	page-break-after:avoid;
+	font-size:14.0pt;
+	font-family:"Arial","sans-serif";
+	font-style:italic;}
+h3
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:36.0pt;
+	text-indent:-36.0pt;
+	page-break-after:avoid;
+	font-size:13.0pt;
+	font-family:"Arial","sans-serif";}
+h4
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:43.2pt;
+	text-indent:-43.2pt;
+	page-break-after:avoid;
+	font-size:14.0pt;
+	font-family:"Times New Roman","serif";}
+h5
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:50.4pt;
+	text-indent:-50.4pt;
+	font-size:13.0pt;
+	font-family:"Times New Roman","serif";
+	font-style:italic;}
+h6
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:57.6pt;
+	text-indent:-57.6pt;
+	font-size:11.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoHeading7, li.MsoHeading7, div.MsoHeading7
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:64.8pt;
+	text-indent:-64.8pt;
+	font-size:12.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoHeading8, li.MsoHeading8, div.MsoHeading8
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:72.0pt;
+	text-indent:-72.0pt;
+	font-size:12.0pt;
+	font-family:"Times New Roman","serif";
+	font-style:italic;}
+p.MsoHeading9, li.MsoHeading9, div.MsoHeading9
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:79.2pt;
+	text-indent:-79.2pt;
+	font-size:11.0pt;
+	font-family:"Arial","sans-serif";}
+p.MsoToc1, li.MsoToc1, div.MsoToc1
+	{margin-top:6.0pt;
+	margin-right:0cm;
+	margin-bottom:6.0pt;
+	margin-left:0cm;
+	line-height:150%;
+	font-size:10.5pt;
+	font-family:"Times New Roman","serif";
+	text-transform:uppercase;
+	font-weight:bold;}
+p.MsoToc2, li.MsoToc2, div.MsoToc2
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:12.0pt;
+	margin-bottom:.0001pt;
+	line-height:150%;
+	font-size:10.5pt;
+	font-family:"Times New Roman","serif";
+	font-variant:small-caps;}
+p.MsoToc3, li.MsoToc3, div.MsoToc3
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:24.0pt;
+	margin-bottom:.0001pt;
+	line-height:150%;
+	font-size:10.5pt;
+	font-family:"Times New Roman","serif";
+	font-style:italic;}
+p.MsoToc4, li.MsoToc4, div.MsoToc4
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:36.0pt;
+	margin-bottom:.0001pt;
+	font-size:9.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoToc5, li.MsoToc5, div.MsoToc5
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:48.0pt;
+	margin-bottom:.0001pt;
+	font-size:9.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoToc6, li.MsoToc6, div.MsoToc6
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:60.0pt;
+	margin-bottom:.0001pt;
+	font-size:9.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoToc7, li.MsoToc7, div.MsoToc7
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:72.0pt;
+	margin-bottom:.0001pt;
+	font-size:9.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoToc8, li.MsoToc8, div.MsoToc8
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:84.0pt;
+	margin-bottom:.0001pt;
+	font-size:9.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoToc9, li.MsoToc9, div.MsoToc9
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:96.0pt;
+	margin-bottom:.0001pt;
+	font-size:9.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoFootnoteText, li.MsoFootnoteText, div.MsoFootnoteText
+	{margin:0cm;
+	margin-bottom:.0001pt;
+	font-size:10.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoHeader, li.MsoHeader, div.MsoHeader
+	{margin:0cm;
+	margin-bottom:.0001pt;
+	font-size:12.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoFooter, li.MsoFooter, div.MsoFooter
+	{margin:0cm;
+	margin-bottom:.0001pt;
+	font-size:12.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoCaption, li.MsoCaption, div.MsoCaption
+	{margin:0cm;
+	margin-bottom:.0001pt;
+	font-size:11.0pt;
+	font-family:"Times New Roman","serif";
+	font-weight:bold;}
+span.MsoFootnoteReference
+	{vertical-align:super;}
+p.MsoTitle, li.MsoTitle, div.MsoTitle
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:120.0pt;
+	margin-left:0cm;
+	text-align:center;
+	font-size:16.0pt;
+	font-family:"Arial","sans-serif";
+	font-weight:bold;}
+p.MsoBodyText, li.MsoBodyText, div.MsoBodyText
+	{mso-style-link:"Body Text Char";
+	margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:6.0pt;
+	margin-left:0cm;
+	font-size:12.0pt;
+	font-family:"Times New Roman","serif";}
+a:link, span.MsoHyperlink
+	{color:blue;
+	text-decoration:underline;}
+a:visited, span.MsoHyperlinkFollowed
+	{color:purple;
+	text-decoration:underline;}
+p.MsoAcetate, li.MsoAcetate, div.MsoAcetate
+	{margin:0cm;
+	margin-bottom:.0001pt;
+	font-size:8.0pt;
+	font-family:"Tahoma","sans-serif";}
+span.BodyTextChar
+	{mso-style-name:"Body Text Char";
+	mso-style-link:"Body Text";}
+ /* Page Definitions */
+ @page WordSection1
+	{size:595.45pt 841.7pt;
+	margin:72.0pt 90.0pt 72.0pt 90.0pt;}
+div.WordSection1
+	{page:WordSection1;}
+@page WordSection2
+	{size:595.45pt 841.7pt;
+	margin:72.0pt 90.0pt 72.0pt 90.0pt;}
+div.WordSection2
+	{page:WordSection2;}
+ /* List Definitions */
+ ol
+	{margin-bottom:0cm;}
+ul
+	{margin-bottom:0cm;}
+-->
+</style>
+
+</head>
+
+<body lang=EN-US link=blue vlink=purple>
+
+<div class=WordSection1>
+
+<p class=MsoTitle>Forward Lock Converter And Decoder</p>
+
+<p class=MsoToc1><span
+class=MsoHyperlink><a href="#_Toc276471422">1<span style='font-size:12.0pt;
+line-height:150%;color:windowtext;text-transform:none;font-weight:normal;
+text-decoration:none'>      </span>Introduction<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471423">2<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'>      </span>Overview<span
+style='color:windowtext;display:none;text-decoration:none'>... </span><span
+style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471424">3<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'>      </span>Use Cases<span
+style='color:windowtext;display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471425">3.1<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'>        </span>Converter<span style='color:windowtext;display:none;
+text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471426">3.1.1<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'>     </span>Convert Data (Push-Mode Conversion)<span
+style='color:windowtext;display:none;text-decoration:none'> </span><span
+style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471427">3.1.2<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'>     </span>Convert File (Pull-Mode Conversion)<span
+style='color:windowtext;display:none;text-decoration:none'> </span><span
+style='color:windowtext;display:none;text-decoration:none'>6</span></a></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471428">3.2<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'>        </span>Decoder<span style='color:windowtext;display:none;
+text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>7</span></a></span></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471429">3.2.1<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'>     </span>Check Integrity<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>8</span></a></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471430">3.2.2<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'>     </span>Get Content Type<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>9</span></a></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471431">3.2.3<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'>     </span>Decode File<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>10</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471432">4<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'>      </span>Definition of the
+Internal Forward Lock File Format<span style='color:windowtext;display:none;
+text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>11</span></a></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471433">4.1<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'>        </span>Key Derivation<span style='color:windowtext;display:none;
+text-decoration:none'>.. </span><span
+style='color:windowtext;display:none;text-decoration:none'>11</span></a></span></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471434">4.2<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'>        </span>Calculation of the Counters<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471435">5<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'>      </span>Unit Test Cases<span
+style='color:windowtext;display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471436">6<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'>      </span>References<span
+style='color:windowtext;display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></p>
+
+<p class=MsoBodyText></p>
+
+</div>
+
+<span style='font-size:12.0pt;font-family:"Times New Roman","serif"'><br
+clear=all style='page-break-before:right'>
+</span>
+
+<div class=WordSection2>
+
+<h1><a name="_Toc276471422"></a><a name="_Ref263085474">1<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Introduction</a></h1>
+
+<p class=MsoBodyText>The internal Forward Lock file format is used for encrypting
+inherently unencrypted OMA DRM version 1 Forward Lock and Combined Delivery
+files so they can be securely stored on externally accessible file system partitions
+such as memory stick.</p>
+
+<p class=MsoBodyText>Our general strategy is to convert such <i>OMA DRM Message</i>
+(‘.dm’) files to internal Forward Lock (‘.fl’) files as soon as they are
+downloaded or otherwise transferred to the phone, and not actually provide any
+decoders for ‘.dm’ files.</p>
+
+<h1><a name="_Toc276471423">2<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span>Overview</a></h1>
+
+<p class=MsoBodyText>The <i>Forward Lock Converter</i> converts OMA DRM Message
+files to the internal file format. The <i>Forward Lock Decoder</i> provides a
+POSIX-level API for transparent reading and seeking through such a converted
+file as if it were unencrypted. The API also includes functions for checking a
+fileÂ’s integrity and getting the MIME type of its embedded content.</p>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'>The converter and decoder are
+built into two separate libraries, which share common code for random number
+generation and key encryption in a third library. For test purposes there is
+also a unit test application. See Figure 1.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=288 height=364
+src="images/image001.gif"></p>
+
+<p class=MsoCaption style='margin-top:12.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref262730885">Figure </a>1. Block diagram illustrating the dependencies between the executable modules.</p>
+
+<b><span style='font-size:16.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h1><a name="_Toc276471424">3<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span>Use Cases</a></h1>
+
+<p class=MsoBodyText>This section describes all the use cases for the converter
+and decoder. It shows the sequence of API calls that should be used to solve
+these use cases.</p>
+
+<h2><a name="_Toc276471425">3.1<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;
+</span>Converter</a></h2>
+
+<p class=MsoBodyText>Through the converter API, conversion can be performed in one
+of two ways:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><i>Push-mode
+conversion</i> is when the client progressively feeds data to the converter as
+it arrives. This is appropriate when data arrives gradually in chunks, with
+idle time in between. Consequently, push mode is used for converting files
+being downloaded through HTTP. See section 3.1.1.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><i>Pull-mode
+conversion</i> is when the converter drives the process and consumes data from
+the client as it needs it. This is appropriate when the entire file to be
+converted is readily available. Hence, pull mode is used by the unit test application.
+See section 3.1.2.</p>
+
+<p class=MsoBodyText>Internally, pull-mode conversion is implemented in terms
+of the API for push-mode conversion.</p>
+
+<h3><a name="_Toc276471426"></a><a name="_Ref263085478">3.1.1<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp; </span>Convert Data
+(Push-Mode Conversion)</a></h3>
+
+<p class=MsoBodyText>Push-mode conversion is performed as follows (see also Figure 2):</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_OpenSession</span>
+initializes the output parameter and returns a <i>session ID</i> to be used in
+subsequent calls to the API. The output parameter is a union of return values
+whose correct use at any given moment is determined by the API function last
+called.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_ConvertData</span>
+is called repeatedly until no more input data remains. Each call converts the
+maximum amount of data possible and writes it to the output buffer. The client then
+writes this data to file.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_CloseSession</span>
+cleans up the session and deallocates the output buffer. If all has gone well, a
+two-part cryptographic signature of the output file is calculated. The client
+must go back and rewrite part of the file header with this updated signature
+information.</p>
+
+<p class=MsoBodyText>Every time a file is being converted, the converter calls <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetRandomNumber</span>
+to generate a new, unique session key. No two converted files look alike, even
+if the original files are the same.</p>
+
+<p class=MsoBodyText><b>Note:</b> The random bytes cannot come from any bare-minimum
+implementation of the C-library <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>rand</span>
+function—they must be cryptographically secure. Otherwise, security will be
+compromised.</p>
+
+<p class=MsoBodyText>The session key is encrypted and stored within the
+converted file. Key encryption is performed using <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetEncryptedKeyLength</span> and <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_EncryptKey</span>.
+These two functions, together with the corresponding decryption function (<span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_DecryptKey</span>),
+are the integration points where an OEM manufacturer may implement their own
+key-encryption scheme.</p>
+
+<p class=MsoBodyText><b>Note:</b> The key-encryption key must be unique to each
+device; this is what makes the files forward lock–protected. Ideally, it should
+be derived from secret hardware parameters, but at the very least it should be
+persistent from one master reset to the next.</p>
+
+<div style='margin-bottom:24.0pt;border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
+background:#F2F2F2'>
+
+<p class=MsoBodyText style='background:#F2F2F2;border:
+none;padding:0cm'><b>Note:</b> In the open-source implementation of the <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>libfwdlock-common</span>
+library, a random key-encryption key is generated and stored in plaintext in
+the file system, without being obfuscated in any way (doing so would be futile
+since the source code is openly available). This key must be kept secret from
+the user, and shouldnÂ’t be possible to extract through backup-and-restore
+functionality or the like. OEM manufacturers will probably want to implement a
+truly hardware-based device-unique key.</p>
+
+</div>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=531 height=563
+src="images/image002.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263085187">Figure </a>2. Converter UC: Convert Data.</p>
+
+<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h3><a name="_Toc276471427"></a><a name="_Ref263163082">3.1.2<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp; </span>Convert File
+(Pull-Mode Conversion)</a></h3>
+
+<p class=MsoBodyText>Pull-mode conversion is performed by calling <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertFile</span>
+with the filename, unless there is need for a specialized <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>read</span> function, in
+which case <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertOpenFile</span>
+should be used directly instead. See Figure 3.</p>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'>Internally, <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertFile</span>
+calls <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertOpenFile</span>.
+The latter then proceeds with the conversion using the push-mode API, acting as
+the client in the previous use case; see section 3.1.1.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=531 height=731
+src="images/image003.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263085208">Figure </a>3. Converter UC: Convert File.</p>
+
+<b><i><span style='font-size:14.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></i></b>
+
+<h2><a name="_Toc276471428">3.2<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;
+</span>Decoder</a></h2>
+
+<p class=MsoBodyText>The decoder API allows the client to do the following:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Check
+the integrity of an internal Forward Lock file, i.e., detect whether it has
+been manipulated in any way; see section 3.2.1.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Get
+the MIME type of the embedded content (the “original” MIME type before DRM protection
+was applied); see section 3.2.2.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Decode
+the file by random access, i.e., read and seek through it in an arbitrary
+manner; see section 3.2.3.</p>
+
+<p class=MsoBodyText>All subsequent operations on a file first require it to be
+opened. Opening a file returns a <i>file descriptor</i>—a handle to be used in
+these subsequent operations.</p>
+
+<p class=MsoBodyText>If the filename is known, an internal Forward Lock file
+can be opened using <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span>.
+If only the file descriptor of an already open file is available, a decoding
+session can instead be initialized using <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>.</p>
+
+<p class=MsoBodyText>Internally, <span style='font-size:10.0pt;font-family:
+"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> calls <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>. For efficiency
+reasons, <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>
+therefore assumes that the file position is at the beginning of the file when
+the function gets called. A client who calls it directly must make sure that
+this assumption holds.</p>
+
+<p class=MsoBodyText>When a file is being attached, the session key stored in
+the file during conversion is decrypted using <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetEncryptedKeyLength</span> and <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_DecryptKey</span>,
+in order to set up for decoding and integrity checking.</p>
+
+<p class=MsoBodyText>For just getting the content type, however, retrieving the
+session key would strictly speaking not be necessary, so there is an
+opportunity here to optimize for that if it proves necessary later.</p>
+
+<p class=MsoBodyText>Symmetrical to <span style='font-size:10.0pt;font-family:
+"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> and <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>, there are also functions
+for closing a file or detaching from it:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>If
+it was opened with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span>
+it should be closed with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_close</span>.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>If
+it was attached with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>
+it should be detached with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_detach</span>.</p>
+
+<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h3><a name="_Ref263163099"></a><a name="_Toc276471429">3.2.1<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp; </span>Check Integrity</a></h3>
+
+<p class=MsoBodyText>There are three methods for checking the integrity of an
+internal Forward Lock file, in whole or in part (see also Figure 4):</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>,
+which checks the integrity of the encrypted content data.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span>,
+which checks the integrity of the file header, including the content type and
+other fields not currently supported but reserved for future use.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckIntegrity</span>,
+which internally calls first <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span>
+and then <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>.</p>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'><span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span> is
+generally much faster than <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>,
+whose running time is directly proportional to the size of the file.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=575
+src="images/image004.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263163308">Figure </a>4. Decoder UC: Check Integrity.</p>
+
+<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h3><a name="_Toc276471430"></a><a name="_Ref263163117">3.2.2<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp; </span>Get Content Type</a></h3>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'><span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_GetContentType</span> returns a
+read-only reference to an ASCII string containing the MIME type of the
+embedded content. This reference is valid as long as the file is kept open.
+Clients who need access to the content type after closing the file should make
+a copy of the string. See Figure 5 below.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=488
+src="images/image005.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263163392">Figure </a>5. Decoder UC: Get Content Type.</p>
+
+<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h3><a name="_Toc276471431"></a><a name="_Ref263163137">3.2.3<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp; </span>Decode File</a></h3>
+
+<p class=MsoBodyText>After opening an internal Forward Lock file (or attaching
+to an already open one), it can be transparently read from as if it were
+unencrypted. Any number of calls to read data from the current file position or
+set it to a new one (which is what <span style='font-size:10.0pt;font-family:
+"Lucida Console","DejaVu Sans Mono"'>lseek</span> does) can be made in any order; this is what we
+call <i>random access</i>. See Figure 6.</p>
+
+<p class=MsoBodyText>The Forward Lock Decoder versions of the <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>read</span>, <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>lseek</span>, and <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>close</span> functions
+have the exact same signatures as their POSIX counterparts. So, for example,
+the call <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_lseek(fd,
+0, SEEK_END)</span> returns the size of the embedded content data, i.e., the
+size of the original file before DRM protection.</p>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'>Moreover, <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span>
+is like regular POSIX <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>open</span>
+except it takes only the filename as a parameter—access is always read-only.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=522
+src="images/image006.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263166303">Figure </a>6. Decoder UC: Decode File.</p>
+
+<b><span style='font-size:16.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h1><a name="_Toc276471432">4<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span>Definition of the Internal Forward Lock File Format</a></h1>
+
+<p class=MsoBodyText style='margin-bottom:12.0pt'>The inner structure of an internal
+Forward Lock file is defined in Table 1 below.</p>
+
+<table class=MsoNormalTable border=1 cellspacing=0 cellpadding=0
+ style='border-collapse:collapse;border:none'>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><b>Offset [bytes]</b></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border:solid windowtext 1.0pt;
+  border-left:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><b>Size [bytes]</b></p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border:solid windowtext 1.0pt;
+  border-left:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><b>Description</b></p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>0</p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>4</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>The file signature (so-called
+  <i>magic number</i>): a four-character code consisting of the letters
+  F-W-L-K.</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>4</p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>1</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>Version number (0 for the
+  first version).</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>5</p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>1</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>Indicates the subformat:</p>
+  <p class=MsoNormal style='page-break-after:avoid'><i>0x00 Forward Lock</i></p>
+  <p class=MsoNormal style='page-break-after:avoid'><i>0x01 Combined Delivery</i></p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>6</p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>1</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>Usage restriction flags (prohibitions
+  against usage as ringtone or as wallpaper and screen saver). Also indicates
+  if the file is bound to a specific SIM card.</p>
+  <p class=MsoNormal style='page-break-after:avoid'><i>0x00 No usage
+  restrictions</i></p>
+  <p class=MsoNormal style='page-break-after:avoid'><i>0x01 Ringtone usage
+  prohibited</i></p>
+  <p class=MsoNormal style='page-break-after:avoid'><i>0x02 Screen usage
+  prohibited</i></p>
+  <p class=MsoNormal style='page-break-after:avoid'><i>0x80 Bound to SIM</i></p>
+  <p class=MsoNormal style='page-break-after:avoid'>(Any number of these may be
+  OR-ed together.)</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>7</p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>1</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>Length of the MIME content
+  type (<i>k</i>).</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>8</p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><i>k</i></p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>The MIME content type
+  (ASCII-encoded without null-character termination).</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><i>l </i>= 0 or 16</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>If the subformat is
+  Combined Delivery, this field contains the auto-generated content ID (16&nbsp;bytes).
+  If not, this field is zero-size.</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><i>m </i>= 0 or 9</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>If the file is bound to a
+  specific SIM card, this field contains the 9-byte packed IMSI number. If not,
+  this field is zero-size.</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i>+<i>m</i></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><i>n</i> &#8805; 16</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>The encrypted session key, the
+  first sixteen bytes of which are also used as the CTR-mode <i>nonce</i> (similar
+  to the CBC-mode <i>initialization vector</i>).</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>20</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>Data signature—the SHA-1
+  HMAC of the encrypted content data.</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>28+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>20</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>Header signature—the SHA-1
+  HMAC of all the fields above, including the encrypted session key and data
+  signature.</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>48+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><i>&lt;to the end of the
+  file&gt;</i></p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>The content data encrypted
+  using 128-bit AES in CTR mode.</p>
+  </td>
+ </tr>
+</table>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm;page-break-after:avoid'><a name="_Ref151269206">Table </a>1. Definition of the fields of an internal Forward Lock file.</p>
+
+<p class=MsoBodyText>As of now, neither Combined Delivery nor usage
+restrictions (including SIM binding) are supported. These fields are reserved
+for future use.</p>
+
+<h2><a name="_Toc276471433">4.1<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;
+</span>Key Derivation</a></h2>
+
+<p class=MsoBodyText>The session key consists of sixteen bytes fetched from a
+cryptographically secure random number generator. From the session key, two
+separate keys are derived: one used for encryption, the other for signing.</p>
+
+<p class=MsoBodyText>The encryption key is the output from encrypting the
+16-byte all-zero input block {0, 0, Â…, 0} using 128-bit AES with the random session
+key as the key. The signing key is the output from encrypting the 16-byte input
+block {1, 0, Â…, 0} the same way. The keys so derived will be cryptographically
+independent from each other.</p>
+
+<p class=MsoBodyText>The session key is encrypted using a hardware-dependent
+key-encryption key unique to each device. The encrypted session key is stored
+inside the file, and its first sixteen bytes are also used as the <i>nonce</i>
+for the CTR-mode encryption of the content data.</p>
+
+<h2><a name="_Toc276471434">4.2<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;
+</span>Calculation of the Counters</a></h2>
+
+<p class=MsoBodyText>Using CTR (“counter”) mode, a block cipher such as AES can
+be turned into a stream cipher. The process of encryption and decryption is
+well defined in [1], except for the specifics of the calculation of the
+counters. For the internal Forward Lock file format, the counters are
+calculated as follows:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>The
+nonce is interpreted as a 128-bit unsigned integer in little-endian format.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>The
+zero-based block sequence number (also a little-endian unsigned integer) is
+added modulo 2<sup>128</sup> to the nonce to produce the counter for a given
+block.</p>
+
+<h1><a name="_Toc276471435">5<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span>Unit Test Cases</a></h1>
+
+<p class=MsoBodyText>Unit test cases for the converter and decoder come in two
+varieties:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><i>Black-box</i>
+test cases aim to verify that you get sensible results from malformed or
+“tricky” input data.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><i>White-box</i>
+test cases aim to maximize code coverage using knowledge of code internals.</p>
+
+<p class=MsoBodyText>The black-box test cases are dependent on a specifically
+designed set of input files found in the <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>forward-lock/internal-format/test/res</span>
+directory in the repository. For ‘tests’ variants of the software, these input
+files will be automatically installed in the file system image during build.</p>
+
+<p class=MsoBodyText>Run the test cases from the ADB shell command line as
+follows:</p>
+
+<p class=MsoNormal style='margin-top:0cm;margin-right:0cm;margin-bottom:6.0pt;
+margin-left:21.55pt'><span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>#
+gtest_fwdlock</span></p>
+
+<p class=MsoBodyText>If all black-box but no white-box test cases fail, the
+input files probably canÂ’t be found in the working directory.</p>
+
+<h1><a name="_Toc276471436">6<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span>References</a></h1>
+
+<p class=MsoBodyText style='margin-left:28.9pt;text-indent:-28.9pt'>[1]<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span><a
+href="http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf">Dworkin,
+Morris: “Recommendation for Block Cipher Modes of Operation—Methods and
+Techniques,” NIST Special Publication 800-38A, December 2001.</a><a
+name="_Ref151269073"></a></p>
+
+</div>
+
+</body>
+
+</html>
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif
new file mode 100644
index 0000000..ee94513
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif
new file mode 100644
index 0000000..8c12f46
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif
new file mode 100644
index 0000000..9e019ca
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif
new file mode 100644
index 0000000..cae1d01
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif
new file mode 100644
index 0000000..0d87be9
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif
new file mode 100644
index 0000000..9445b6b
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif
Binary files differ
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index 58206d4..49f497c 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -204,7 +204,7 @@
     @Override
     public ConstantState getConstantState() {
         if (mState.canConstantState()) {
-            mState.mChangingConfigurations = super.getChangingConfigurations();
+            mState.mChangingConfigurations = getChangingConfigurations();
             return mState;
         }
         return null;
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 32111e8..0b8465e 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -427,7 +427,7 @@
 
     @Override
     public final ConstantState getConstantState() {
-        mBitmapState.mChangingConfigurations = super.getChangingConfigurations();
+        mBitmapState.mChangingConfigurations = getChangingConfigurations();
         return mBitmapState;
     }
 
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index a772871..2b3bd80 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -229,7 +229,7 @@
     @Override
     public ConstantState getConstantState() {
         if (mClipState.canConstantState()) {
-            mClipState.mChangingConfigurations = super.getChangingConfigurations();
+            mClipState.mChangingConfigurations = getChangingConfigurations();
             return mClipState;
         }
         return null;
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index 604c602..0985c1b 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -124,7 +124,7 @@
 
     @Override
     public ConstantState getConstantState() {
-        mState.mChangingConfigurations = super.getChangingConfigurations();
+        mState.mChangingConfigurations = getChangingConfigurations();
         return mState;
     }
 
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index c6f57d4..b13f26f 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -236,7 +236,7 @@
     @Override
     public ConstantState getConstantState() {
         if (mDrawableContainerState.canConstantState()) {
-            mDrawableContainerState.mChangingConfigurations = super.getChangingConfigurations();
+            mDrawableContainerState.mChangingConfigurations = getChangingConfigurations();
             return mDrawableContainerState;
         }
         return null;
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 33ecbea..308fd08 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -832,7 +832,7 @@
     
     @Override
     public ConstantState getConstantState() {
-        mGradientState.mChangingConfigurations = super.getChangingConfigurations();
+        mGradientState.mChangingConfigurations = getChangingConfigurations();
         return mGradientState;
     }
 
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index a9c983e..67c928c 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -238,7 +238,7 @@
     @Override
     public ConstantState getConstantState() {
         if (mInsetState.canConstantState()) {
-            mInsetState.mChangingConfigurations = super.getChangingConfigurations();
+            mInsetState.mChangingConfigurations = getChangingConfigurations();
             return mInsetState;
         }
         return null;
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 8047dd4..234b80d 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -523,7 +523,7 @@
     @Override
     public ConstantState getConstantState() {
         if (mLayerState.canConstantState()) {
-            mLayerState.mChangingConfigurations = super.getChangingConfigurations();
+            mLayerState.mChangingConfigurations = getChangingConfigurations();
             return mLayerState;
         }
         return null;
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 00416d8..6768186 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -327,7 +327,7 @@
     
     @Override
     public ConstantState getConstantState() {
-        mNinePatchState.mChangingConfigurations = super.getChangingConfigurations();
+        mNinePatchState.mChangingConfigurations = getChangingConfigurations();
         return mNinePatchState;
     }
 
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 9c47dab..1428efa 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -189,7 +189,7 @@
     @Override
     public ConstantState getConstantState() {
         if (mState.canConstantState()) {
-            mState.mChangingConfigurations = super.getChangingConfigurations();
+            mState.mChangingConfigurations = getChangingConfigurations();
             return mState;
         }
         return null;
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index b623d80..a95eb06 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -237,7 +237,7 @@
     @Override
     public ConstantState getConstantState() {
         if (mScaleState.canConstantState()) {
-            mScaleState.mChangingConfigurations = super.getChangingConfigurations();
+            mScaleState.mChangingConfigurations = getChangingConfigurations();
             return mScaleState;
         }
         return null;
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index be1892e..0201fb0 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -348,7 +348,7 @@
     
     @Override
     public ConstantState getConstantState() {
-        mShapeState.mChangingConfigurations = super.getChangingConfigurations();
+        mShapeState.mChangingConfigurations = getChangingConfigurations();
         return mShapeState;
     }
 
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
new file mode 100644
index 0000000..54ce789
--- /dev/null
+++ b/include/gui/SurfaceTexture.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_GUI_SURFACETEXTURE_H
+#define ANDROID_GUI_SURFACETEXTURE_H
+
+namespace android {
+
+struct SurfaceTexture {
+    struct FrameAvailableListener : public virtual RefBase {};
+
+    SurfaceTexture(GLuint) {}
+    void updateTexImage() {}
+    void decStrong(android::sp<android::SurfaceTexture>* const) {}
+    void incStrong(android::sp<android::SurfaceTexture>* const) {}
+    void getTransformMatrix(float mtx[16]) {}
+    void setFrameAvailableListener(const sp<FrameAvailableListener>&) {}
+};
+
+}
+
+#endif
diff --git a/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java b/include/gui/SurfaceTextureClient.h
similarity index 62%
copy from tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java
copy to include/gui/SurfaceTextureClient.h
index 7bb4c35..a83756e 100644
--- a/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java
+++ b/include/gui/SurfaceTextureClient.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -14,18 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.testplugin;
+#ifndef ANDROID_GUI_SURFACETEXTURECLIENT_H
+#define ANDROID_GUI_SURFACETEXTURECLIENT_H
 
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
+#include <ui/egl/android_natives.h>
 
-public class TestPlugin extends Service {
+namespace android {
 
-    @Override
-    public IBinder onBind(Intent intent) {
-        // TODO Auto-generated method stub
-        return null;
-    }
+struct SurfaceTextureClient : public ANativeWindow {
+    SurfaceTextureClient(const sp<SurfaceTexture>&){}
+};
 
 }
+
+#endif
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 8c6018b..4e809d6 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -38,6 +38,15 @@
     AKEY_EVENT_FLAG_START_TRACKING = 0x40000000
 };
 
+enum {
+    /*
+     * Indicates that an input device has switches.
+     * This input source flag is hidden from the API because switches are only used by the system
+     * and applications have no way to interact with them.
+     */
+    AINPUT_SOURCE_SWITCH = 0x80000000,
+};
+
 /*
  * Maximum number of pointers supported per motion event.
  * Smallest number of pointers is 1.
diff --git a/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java b/include/utils/Functor.h
similarity index 62%
rename from tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java
rename to include/utils/Functor.h
index 7bb4c35..56a7557 100644
--- a/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java
+++ b/include/utils/Functor.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2011 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.
@@ -14,18 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.testplugin;
+#ifndef ANDROID_FUNCTOR_H
+#define ANDROID_FUNCTOR_H
 
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
+namespace  android {
 
-public class TestPlugin extends Service {
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        // TODO Auto-generated method stub
-        return null;
-    }
+struct Functor{};
 
 }
+
+#endif
+
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index da86da4..ab7b973 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -1983,6 +1983,7 @@
 
 #ifndef HAVE_ANDROID_OS
     void print(bool inclValues) const;
+    static String8 normalizeForOutput(const char* input);
 #endif
 
 private:
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 3197ab2..8000b2d 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -806,7 +806,7 @@
 }
 
 uint32_t SwitchInputMapper::getSources() {
-    return 0;
+    return AINPUT_SOURCE_SWITCH;
 }
 
 void SwitchInputMapper::process(const RawEvent* rawEvent) {
diff --git a/libs/ui/tests/InputReader_test.cpp b/libs/ui/tests/InputReader_test.cpp
index de4b05a..c53d9c0 100644
--- a/libs/ui/tests/InputReader_test.cpp
+++ b/libs/ui/tests/InputReader_test.cpp
@@ -1356,7 +1356,7 @@
     SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
     addMapperAndConfigure(mapper);
 
-    ASSERT_EQ(uint32_t(0), mapper->getSources());
+    ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper->getSources());
 }
 
 TEST_F(SwitchInputMapperTest, GetSwitchState) {
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 8345cc3..7fb7ae3 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -4038,6 +4038,38 @@
     }
 }
 
+// Normalize a string for output
+String8 ResTable::normalizeForOutput( const char *input )
+{
+    String8 ret;
+    char buff[2];
+    buff[1] = '\0';
+
+    while (*input != '\0') {
+        switch (*input) {
+            // All interesting characters are in the ASCII zone, so we are making our own lives
+            // easier by scanning the string one byte at a time.
+        case '\\':
+            ret += "\\\\";
+            break;
+        case '\n':
+            ret += "\\n";
+            break;
+        case '"':
+            ret += "\\\"";
+            break;
+        default:
+            buff[0] = *input;
+            ret += buff;
+            break;
+        }
+
+        input++;
+    }
+
+    return ret;
+}
+
 void ResTable::print_value(const Package* pkg, const Res_value& value) const
 {
     if (value.dataType == Res_value::TYPE_NULL) {
@@ -4051,13 +4083,13 @@
         const char* str8 = pkg->header->values.string8At(
                 value.data, &len);
         if (str8 != NULL) {
-            printf("(string8) \"%s\"\n", str8);
+            printf("(string8) \"%s\"\n", normalizeForOutput(str8).string());
         } else {
             const char16_t* str16 = pkg->header->values.stringAt(
                     value.data, &len);
             if (str16 != NULL) {
                 printf("(string16) \"%s\"\n",
-                    String8(str16, len).string());
+                    normalizeForOutput(String8(str16, len).string()).string());
             } else {
                 printf("(string) null\n");
             }
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index f6c55e4..0b360f4 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -775,6 +775,7 @@
             self->mLock.lock();
             self->mRunning = false;
             self->mThreadExitedCondition.broadcast();
+            self->mThread = thread_id_t(-1); // thread id could be reused
             self->mLock.unlock();
             break;
         }
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/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index ecabae8..9e97e92 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -306,7 +306,7 @@
             degrees != 270) {
             throw new IllegalArgumentException("Unsupported angle: " + degrees);
         }
-        setParameter(String.format("video-param-rotation-angle-degrees=%d", degrees));
+        setParameter("video-param-rotation-angle-degrees=" + degrees);
     }
 
     /**
@@ -425,7 +425,7 @@
         if (samplingRate <= 0) {
             throw new IllegalArgumentException("Audio sampling rate is not positive");
         }
-        setParameter(String.format("audio-param-sampling-rate=%d", samplingRate));
+        setParameter("audio-param-sampling-rate=" + samplingRate);
     }
 
     /**
@@ -440,7 +440,7 @@
         if (numChannels <= 0) {
             throw new IllegalArgumentException("Number of channels is not positive");
         }
-        setParameter(String.format("audio-param-number-of-channels=%d", numChannels));
+        setParameter("audio-param-number-of-channels=" + numChannels);
     }
 
     /**
@@ -456,7 +456,7 @@
         if (bitRate <= 0) {
             throw new IllegalArgumentException("Audio encoding bit rate is not positive");
         }
-        setParameter(String.format("audio-param-encoding-bitrate=%d", bitRate));
+        setParameter("audio-param-encoding-bitrate=" + bitRate);
     }
 
     /**
@@ -472,7 +472,7 @@
         if (bitRate <= 0) {
             throw new IllegalArgumentException("Video encoding bit rate is not positive");
         }
-        setParameter(String.format("video-param-encoding-bitrate=%d", bitRate));
+        setParameter("video-param-encoding-bitrate=" + bitRate);
     }
 
     /**
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index ab260d5..f029bfe 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -1815,3 +1815,8 @@
     }
     return setError(EGL_BAD_DISPLAY, NULL);
 }
+
+EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) { return 0; }
+EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list) { return 0; }
+EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) { return 0; }
+
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/tests/BrowserTestPlugin/MODULE_LICENSE_APACHE2 b/packages/WAPPushManager/MODULE_LICENSE_APACHE2
similarity index 100%
rename from tests/BrowserTestPlugin/MODULE_LICENSE_APACHE2
rename to packages/WAPPushManager/MODULE_LICENSE_APACHE2
diff --git a/tests/BrowserTestPlugin/NOTICE b/packages/WAPPushManager/NOTICE
similarity index 98%
rename from tests/BrowserTestPlugin/NOTICE
rename to packages/WAPPushManager/NOTICE
index 9df2554..c5b1efa 100644
--- a/tests/BrowserTestPlugin/NOTICE
+++ b/packages/WAPPushManager/NOTICE
@@ -1,5 +1,5 @@
 
-   Copyright (c) 2005-2009, The Android Open Source Project
+   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.
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/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl
similarity index 61%
copy from tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java
copy to packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl
index 7bb4c35..f0670fa 100644
--- a/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -14,18 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.testplugin;
+package com.android.smspush.unitTests;
 
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
+/**
+ * Interface to receiver application data verifyer class
+ */
+interface IDataVerify {
+    /**
+     * Verify data
+     */
+    boolean verifyData(in byte[] pdu);
 
-public class TestPlugin extends Service {
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
+    /**
+     * 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/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 1f06dcc..5e33f05 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -221,8 +221,7 @@
         final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
 
         ab.setAdapter(mAdapter, this)
-                .setInverseBackgroundForced(true)
-                .setTitle(R.string.global_actions);
+                .setInverseBackgroundForced(true);
 
         final AlertDialog dialog = ab.create();
         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
@@ -249,6 +248,7 @@
         } else {
             mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
         }
+        mDialog.setTitle(R.string.global_actions);
     }
 
 
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 8aaa325..433f1f7 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -975,7 +975,6 @@
 {
     int32_t* const outTemp = state->outputTemp;
     const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount;
-    memset(outTemp, 0, size);
 
     size_t numFrames = state->frameCount;
 
@@ -997,6 +996,7 @@
         }
         e0 &= ~(e1);
         int32_t *out = t1.mainBuffer;
+        memset(outTemp, 0, size);
         while (e1) {
             const int i = 31 - __builtin_clz(e1);
             e1 &= ~(1<<i);
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 7b68d68..c0c6c36 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -97,11 +97,12 @@
                     LocalSocketAddress.Namespace.RESERVED);
 
             socket.connect(address);
-            mCallbacks.onDaemonConnected();
 
             InputStream inputStream = socket.getInputStream();
             mOutputStream = socket.getOutputStream();
 
+            mCallbacks.onDaemonConnected();
+
             byte[] buffer = new byte[BUFFER_SIZE];
             int start = 0;
 
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index f605c11..5b236a7 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -522,9 +522,8 @@
                 } catch (InterruptedException e) {
                 }
             }
+            return thr.mService;
         }
-
-        return thr.mService;
     }
 
     static class WMThread extends Thread {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2322ee1..a881bc4 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -4385,12 +4385,15 @@
         perm.modeFlags |= modeFlags;
         if (owner == null) {
             perm.globalModeFlags |= modeFlags;
-        } else if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
-            perm.readOwners.add(owner);
-            owner.addReadPermission(perm);
-        } else if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
-            perm.writeOwners.add(owner);
-            owner.addWritePermission(perm);
+        } else {
+            if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+                 perm.readOwners.add(owner);
+                 owner.addReadPermission(perm);
+            }
+            if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+                 perm.writeOwners.add(owner);
+                 owner.addWritePermission(perm);
+            }
         }
     }
 
diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
index a75e41d..6d4ad9a 100755
--- a/services/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -217,82 +217,10 @@
     create_thread_callback,
 };
 
-static const GpsInterface* get_gps_interface() {
+static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
     int err;
     hw_module_t* module;
-    const GpsInterface* interface = NULL;
 
-    err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
-    if (err == 0) {
-        hw_device_t* device;
-        err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
-        if (err == 0) {
-            gps_device_t* gps_device = (gps_device_t *)device;
-            interface = gps_device->get_gps_interface(gps_device);
-        }
-    }
-
-    return interface;
-}
-
-static const GpsInterface* GetGpsInterface(JNIEnv* env, jobject obj) {
-    // this must be set before calling into the HAL library
-    if (!mCallbacksObj)
-        mCallbacksObj = env->NewGlobalRef(obj);
-
-    if (!sGpsInterface) {
-        sGpsInterface = get_gps_interface();
-        if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0) {
-            sGpsInterface = NULL;
-            return NULL;
-        }
-    }
-    return sGpsInterface;
-}
-
-static const AGpsInterface* GetAGpsInterface(JNIEnv* env, jobject obj)
-{
-    const GpsInterface* interface = GetGpsInterface(env, obj);
-    if (!interface)
-        return NULL;
-
-    if (!sAGpsInterface) {
-        sAGpsInterface = (const AGpsInterface*)interface->get_extension(AGPS_INTERFACE);
-        if (sAGpsInterface)
-            sAGpsInterface->init(&sAGpsCallbacks);
-    }
-    return sAGpsInterface;
-}
-
-static const GpsNiInterface* GetNiInterface(JNIEnv* env, jobject obj)
-{
-    const GpsInterface* interface = GetGpsInterface(env, obj);
-    if (!interface)
-        return NULL;
-
-    if (!sGpsNiInterface) {
-       sGpsNiInterface = (const GpsNiInterface*)interface->get_extension(GPS_NI_INTERFACE);
-        if (sGpsNiInterface)
-           sGpsNiInterface->init(&sGpsNiCallbacks);
-    }
-    return sGpsNiInterface;
-}
-
-static const AGpsRilInterface* GetAGpsRilInterface(JNIEnv* env, jobject obj)
-{
-    const GpsInterface* interface = GetGpsInterface(env, obj);
-    if (!interface)
-        return NULL;
-
-    if (!sAGpsRilInterface) {
-       sAGpsRilInterface = (const AGpsRilInterface*)interface->get_extension(AGPS_RIL_INTERFACE);
-        if (sAGpsRilInterface)
-            sAGpsRilInterface->init(&sAGpsRilCallbacks);
-    }
-    return sAGpsRilInterface;
-}
-
-static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
     method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");
     method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
     method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");
@@ -300,40 +228,73 @@
     method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
     method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V");
     method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
-    method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification", "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
+    method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification",
+            "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
     method_requestRefLocation = env->GetMethodID(clazz,"requestRefLocation","(I)V");
     method_requestSetID = env->GetMethodID(clazz,"requestSetID","(I)V");
+
+    err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
+    if (err == 0) {
+        hw_device_t* device;
+        err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
+        if (err == 0) {
+            gps_device_t* gps_device = (gps_device_t *)device;
+            sGpsInterface = gps_device->get_gps_interface(gps_device);
+        }
+    }
+    if (sGpsInterface) {
+        sGpsXtraInterface =
+            (const GpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE);
+        sAGpsInterface =
+            (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
+        sGpsNiInterface =
+            (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
+        sGpsDebugInterface =
+            (const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);
+        sAGpsRilInterface =
+            (const AGpsRilInterface*)sGpsInterface->get_extension(AGPS_RIL_INTERFACE);
+    }
 }
 
 static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) {
-    return (sGpsInterface != NULL || get_gps_interface() != NULL);
+    return (sGpsInterface != NULL);
 }
 
 static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
 {
-    const GpsInterface* interface = GetGpsInterface(env, obj);
-    if (!interface)
+    // this must be set before calling into the HAL library
+    if (!mCallbacksObj)
+        mCallbacksObj = env->NewGlobalRef(obj);
+
+    // fail if the main interface fails to initialize
+    if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)
         return false;
 
-    if (!sGpsDebugInterface)
-       sGpsDebugInterface = (const GpsDebugInterface*)interface->get_extension(GPS_DEBUG_INTERFACE);
+    // if XTRA initialization fails we will disable it by sGpsXtraInterface to null,
+    // but continue to allow the rest of the GPS interface to work.
+    if (sGpsXtraInterface && sGpsXtraInterface->init(&sGpsXtraCallbacks) != 0)
+        sGpsXtraInterface = NULL;
+    if (sAGpsInterface)
+        sAGpsInterface->init(&sAGpsCallbacks);
+    if (sGpsNiInterface)
+        sGpsNiInterface->init(&sGpsNiCallbacks);
+    if (sAGpsRilInterface)
+        sAGpsRilInterface->init(&sAGpsRilCallbacks);
 
     return true;
 }
 
 static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject obj)
 {
-    const GpsInterface* interface = GetGpsInterface(env, obj);
-    if (interface)
-        interface->cleanup();
+    if (sGpsInterface)
+        sGpsInterface->cleanup();
 }
 
 static jboolean android_location_GpsLocationProvider_set_position_mode(JNIEnv* env, jobject obj,
         jint mode, jint recurrence, jint min_interval, jint preferred_accuracy, jint preferred_time)
 {
-    const GpsInterface* interface = GetGpsInterface(env, obj);
-    if (interface)
-        return (interface->set_position_mode(mode, recurrence, min_interval, preferred_accuracy,
+    if (sGpsInterface)
+        return (sGpsInterface->set_position_mode(mode, recurrence, min_interval, preferred_accuracy,
                 preferred_time) == 0);
     else
         return false;
@@ -341,27 +302,24 @@
 
 static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj)
 {
-    const GpsInterface* interface = GetGpsInterface(env, obj);
-    if (interface)
-        return (interface->start() == 0);
+    if (sGpsInterface)
+        return (sGpsInterface->start() == 0);
     else
         return false;
 }
 
 static jboolean android_location_GpsLocationProvider_stop(JNIEnv* env, jobject obj)
 {
-    const GpsInterface* interface = GetGpsInterface(env, obj);
-    if (interface)
-        return (interface->stop() == 0);
+    if (sGpsInterface)
+        return (sGpsInterface->stop() == 0);
     else
         return false;
 }
 
 static void android_location_GpsLocationProvider_delete_aiding_data(JNIEnv* env, jobject obj, jint flags)
 {
-    const GpsInterface* interface = GetGpsInterface(env, obj);
-    if (interface)
-        interface->delete_aiding_data(flags);
+    if (sGpsInterface)
+        sGpsInterface->delete_aiding_data(flags);
 }
 
 static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj,
@@ -399,8 +357,8 @@
         jobject obj, jint type, jint mcc, jint mnc, jint lac, jint cid)
 {
     AGpsRefLocation location;
-    const AGpsRilInterface* interface = GetAGpsRilInterface(env, obj);
-    if (!interface) {
+
+    if (!sAGpsRilInterface) {
         LOGE("no AGPS RIL interface in agps_set_reference_location_cellid");
         return;
     }
@@ -419,15 +377,15 @@
             return;
             break;
     }
-    interface->set_ref_location(&location, sizeof(location));
+    sAGpsRilInterface->set_ref_location(&location, sizeof(location));
 }
 
 static void android_location_GpsLocationProvider_agps_send_ni_message(JNIEnv* env,
         jobject obj, jbyteArray ni_msg, jint size)
 {
     size_t sz;
-    const AGpsRilInterface* interface = GetAGpsRilInterface(env, obj);
-    if (!interface) {
+
+    if (!sAGpsRilInterface) {
         LOGE("no AGPS RIL interface in send_ni_message");
         return;
     }
@@ -435,21 +393,20 @@
         return;
     sz = (size_t)size;
     jbyte* b = env->GetByteArrayElements(ni_msg, 0);
-    interface->ni_message((uint8_t *)b,sz);
+    sAGpsRilInterface->ni_message((uint8_t *)b,sz);
     env->ReleaseByteArrayElements(ni_msg,b,0);
 }
 
 static void android_location_GpsLocationProvider_agps_set_id(JNIEnv *env,
         jobject obj, jint type, jstring  setid_string)
 {
-    const AGpsRilInterface* interface = GetAGpsRilInterface(env, obj);
-    if (!interface) {
+    if (!sAGpsRilInterface) {
         LOGE("no AGPS RIL interface in agps_set_id");
         return;
     }
 
     const char *setid = env->GetStringUTFChars(setid_string, NULL);
-    interface->set_set_id(type, setid);
+    sAGpsRilInterface->set_set_id(type, setid);
     env->ReleaseStringUTFChars(setid_string, setid);
 }
 
@@ -469,40 +426,30 @@
 static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobject obj,
         jlong time, jlong timeReference, jint uncertainty)
 {
-    const GpsInterface* interface = GetGpsInterface(env, obj);
-    if (interface)
-        interface->inject_time(time, timeReference, uncertainty);
+    if (sGpsInterface)
+        sGpsInterface->inject_time(time, timeReference, uncertainty);
 }
 
 static void android_location_GpsLocationProvider_inject_location(JNIEnv* env, jobject obj,
         jdouble latitude, jdouble longitude, jfloat accuracy)
 {
-    const GpsInterface* interface = GetGpsInterface(env, obj);
-    if (interface)
-        interface->inject_location(latitude, longitude, accuracy);
+    if (sGpsInterface)
+        sGpsInterface->inject_location(latitude, longitude, accuracy);
 }
 
 static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, jobject obj)
 {
-    if (!sGpsXtraInterface) {
-        const GpsInterface* interface = GetGpsInterface(env, obj);
-        if (!interface)
-            return false;
-        sGpsXtraInterface = (const GpsXtraInterface*)interface->get_extension(GPS_XTRA_INTERFACE);
-        if (sGpsXtraInterface) {
-            int result = sGpsXtraInterface->init(&sGpsXtraCallbacks);
-            if (result) {
-                sGpsXtraInterface = NULL;
-            }
-        }
-    }
-
     return (sGpsXtraInterface != NULL);
 }
 
 static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj,
         jbyteArray data, jint length)
 {
+    if (!sGpsXtraInterface) {
+        LOGE("no XTRA interface in inject_xtra_data");
+        return;
+    }
+
     jbyte* bytes = (jbyte *)env->GetPrimitiveArrayCritical(data, 0);
     sGpsXtraInterface->inject_xtra_data((char *)bytes, length);
     env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
@@ -510,8 +457,7 @@
 
 static void android_location_GpsLocationProvider_agps_data_conn_open(JNIEnv* env, jobject obj, jstring apn)
 {
-    const AGpsInterface* interface = GetAGpsInterface(env, obj);
-    if (!interface) {
+    if (!sAGpsInterface) {
         LOGE("no AGPS interface in agps_data_conn_open");
         return;
     }
@@ -520,53 +466,49 @@
         return;
     }
     const char *apnStr = env->GetStringUTFChars(apn, NULL);
-    interface->data_conn_open(apnStr);
+    sAGpsInterface->data_conn_open(apnStr);
     env->ReleaseStringUTFChars(apn, apnStr);
 }
 
 static void android_location_GpsLocationProvider_agps_data_conn_closed(JNIEnv* env, jobject obj)
 {
-    const AGpsInterface* interface = GetAGpsInterface(env, obj);
-    if (!interface) {
+    if (!sAGpsInterface) {
         LOGE("no AGPS interface in agps_data_conn_open");
         return;
     }
-    interface->data_conn_closed();
+    sAGpsInterface->data_conn_closed();
 }
 
 static void android_location_GpsLocationProvider_agps_data_conn_failed(JNIEnv* env, jobject obj)
 {
-    const AGpsInterface* interface = GetAGpsInterface(env, obj);
-    if (!interface) {
+    if (!sAGpsInterface) {
         LOGE("no AGPS interface in agps_data_conn_open");
         return;
     }
-    interface->data_conn_failed();
+    sAGpsInterface->data_conn_failed();
 }
 
 static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jobject obj,
         jint type, jstring hostname, jint port)
 {
-    const AGpsInterface* interface = GetAGpsInterface(env, obj);
-    if (!interface) {
+    if (!sAGpsInterface) {
         LOGE("no AGPS interface in agps_data_conn_open");
         return;
     }
     const char *c_hostname = env->GetStringUTFChars(hostname, NULL);
-    interface->set_server(type, c_hostname, port);
+    sAGpsInterface->set_server(type, c_hostname, port);
     env->ReleaseStringUTFChars(hostname, c_hostname);
 }
 
 static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* env, jobject obj,
       jint notifId, jint response)
 {
-    const GpsNiInterface* interface = GetNiInterface(env, obj);
-    if (!interface) {
+    if (!sGpsNiInterface) {
         LOGE("no NI interface in send_ni_response");
         return;
     }
 
-    interface->respond(notifId, response);
+    sGpsNiInterface->respond(notifId, response);
 }
 
 static jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* env, jobject obj)
@@ -586,14 +528,14 @@
 static void android_location_GpsLocationProvider_update_network_state(JNIEnv* env, jobject obj,
         jboolean connected, int type, jboolean roaming, jstring extraInfo)
 {
-    const AGpsRilInterface* interface = GetAGpsRilInterface(env, obj);
-    if (interface && interface->update_network_state) {
+
+    if (sAGpsRilInterface && sAGpsRilInterface->update_network_state) {
         if (extraInfo) {
             const char *extraInfoStr = env->GetStringUTFChars(extraInfo, NULL);
-            interface->update_network_state(connected, type, roaming, extraInfoStr);
+            sAGpsRilInterface->update_network_state(connected, type, roaming, extraInfoStr);
             env->ReleaseStringUTFChars(extraInfo, extraInfoStr);
         } else {
-            interface->update_network_state(connected, type, roaming, NULL);
+            sAGpsRilInterface->update_network_state(connected, type, roaming, NULL);
         }
     }
 }
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/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
old mode 100644
new mode 100755
index f93e494..ad34550
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -65,6 +65,7 @@
 
 public abstract class SMSDispatcher extends Handler {
     private static final String TAG = "SMS";
+    private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg";
 
     /** Default checking period for SMS sent without user permit */
     private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000;
@@ -156,6 +157,8 @@
     protected boolean mStorageAvailable = true;
     protected boolean mReportMemoryStatusPending = false;
 
+    protected static int mRemainingMessages = -1;
+
     protected static int getNextConcatenatedRef() {
         sConcatenatedRef += 1;
         return sConcatenatedRef;
@@ -392,6 +395,7 @@
                 mCm.reportSmsMemoryStatus(mStorageAvailable,
                         obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
             }
+            break;
 
         case EVENT_NEW_BROADCAST_SMS:
             handleBroadcastSms((AsyncResult)msg.obj);
@@ -469,7 +473,17 @@
 
             if (sentIntent != null) {
                 try {
-                    sentIntent.send(Activity.RESULT_OK);
+                    if (mRemainingMessages > -1) {
+                        mRemainingMessages--;
+                    }
+
+                    if (mRemainingMessages == 0) {
+                        Intent sendNext = new Intent();
+                        sendNext.putExtra(SEND_NEXT_MSG_EXTRA, true);
+                        sentIntent.send(mContext, Activity.RESULT_OK, sendNext);
+                    } else {
+                        sentIntent.send(Activity.RESULT_OK);
+                    }
                 } catch (CanceledException ex) {}
             }
         } else {
@@ -508,8 +522,15 @@
                     if (ar.result != null) {
                         fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode);
                     }
-                    tracker.mSentIntent.send(mContext, error, fillIn);
+                    if (mRemainingMessages > -1) {
+                        mRemainingMessages--;
+                    }
 
+                    if (mRemainingMessages == 0) {
+                        fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
+                    }
+
+                    tracker.mSentIntent.send(mContext, error, fillIn);
                 } catch (CanceledException ex) {}
             }
         }
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/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
old mode 100644
new mode 100755
index 40ee0b0..8355046
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -23,6 +23,7 @@
 import android.os.AsyncResult;
 import android.os.Message;
 import android.os.SystemProperties;
+import android.provider.Telephony.Sms;
 import android.provider.Telephony.Sms.Intents;
 import android.telephony.ServiceState;
 import android.telephony.SmsCbMessage;
@@ -68,13 +69,17 @@
         String pduString = (String) ar.result;
         SmsMessage sms = SmsMessage.newFromCDS(pduString);
 
+        int tpStatus = sms.getStatus();
+
         if (sms != null) {
             int messageRef = sms.messageRef;
             for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
                 SmsTracker tracker = deliveryPendingList.get(i);
                 if (tracker.mMessageRef == messageRef) {
                     // Found it.  Remove from list and broadcast.
-                    deliveryPendingList.remove(i);
+                    if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) {
+                       deliveryPendingList.remove(i);
+                    }
                     PendingIntent intent = tracker.mDeliveryIntent;
                     Intent fillIn = new Intent();
                     fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString));
@@ -183,6 +188,8 @@
         int msgCount = parts.size();
         int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN;
 
+        mRemainingMessages = msgCount;
+
         for (int i = 0; i < msgCount; i++) {
             TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false);
             if (encoding != details.codeUnitSize
@@ -272,6 +279,8 @@
         int msgCount = parts.size();
         int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN;
 
+        mRemainingMessages = msgCount;
+
         for (int i = 0; i < msgCount; i++) {
             TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false);
             if (encoding != details.codeUnitSize
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
old mode 100644
new mode 100755
index 438996f..3b133da
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -144,6 +144,25 @@
     private static final int EVENT_GET_CFIS_DONE = 32;
     private static final int EVENT_GET_CSP_CPHS_DONE = 33;
 
+    // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
+
+    private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = {
+        "405025", "405026", "405027", "405028", "405029", "405030", "405031", "405032",
+        "405033", "405034", "405035", "405036", "405037", "405038", "405039", "405040",
+        "405041", "405042", "405043", "405044", "405045", "405046", "405047", "405750",
+        "405751", "405752", "405753", "405754", "405755", "405756", "405799", "405800",
+        "405801", "405802", "405803", "405804", "405805", "405806", "405807", "405808",
+        "405809", "405810", "405811", "405812", "405813", "405814", "405815", "405816",
+        "405817", "405818", "405819", "405820", "405821", "405822", "405823", "405824",
+        "405825", "405826", "405827", "405828", "405829", "405830", "405831", "405832",
+        "405833", "405834", "405835", "405836", "405837", "405838", "405839", "405840",
+        "405841", "405842", "405843", "405844", "405845", "405846", "405847", "405848",
+        "405849", "405850", "405851", "405852", "405853", "405875", "405876", "405877",
+        "405878", "405879", "405880", "405881", "405882", "405883", "405884", "405885",
+        "405886", "405908", "405909", "405910", "405911", "405925", "405926", "405927",
+        "405928", "405929", "405932"
+    };
+
     // ***** Constructor
 
     SIMRecords(GSMPhone p) {
@@ -501,6 +520,17 @@
 
                 Log.d(LOG_TAG, "IMSI: " + imsi.substring(0, 6) + "xxxxxxx");
 
+                if (((mncLength == UNKNOWN) || (mncLength == 2)) &&
+                        ((imsi != null) && (imsi.length() >= 6))) {
+                    String mccmncCode = imsi.substring(0, 6);
+                    for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
+                        if (mccmnc.equals(mccmncCode)) {
+                            mncLength = 3;
+                            break;
+                        }
+                    }
+                }
+
                 if (mncLength == UNKNOWN) {
                     // the SIM has told us all it knows, but it didn't know the mnc length.
                     // guess using the mcc
@@ -752,6 +782,17 @@
                         mncLength = UNKNOWN;
                     }
                 } finally {
+                    if (((mncLength == UNINITIALIZED) || (mncLength == UNKNOWN) ||
+                            (mncLength == 2)) && ((imsi != null) && (imsi.length() >= 6))) {
+                        String mccmncCode = imsi.substring(0, 6);
+                        for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
+                            if (mccmnc.equals(mccmncCode)) {
+                                mncLength = 3;
+                                break;
+                            }
+                        }
+                    }
+
                     if (mncLength == UNKNOWN || mncLength == UNINITIALIZED) {
                         if (imsi != null) {
                             try {
diff --git a/test-runner/src/android/test/SingleLaunchActivityTestCase.java b/test-runner/src/android/test/SingleLaunchActivityTestCase.java
index b63b3ce..79c554a 100644
--- a/test-runner/src/android/test/SingleLaunchActivityTestCase.java
+++ b/test-runner/src/android/test/SingleLaunchActivityTestCase.java
@@ -75,7 +75,7 @@
     protected void tearDown() throws Exception {
         // If it is the last test case, call finish on the activity.
         sTestCaseCounter --;
-        if (sTestCaseCounter == 1) {
+        if (sTestCaseCounter == 0) {
             sActivity.finish();
         }        
         super.tearDown();
diff --git a/tests/BrowserTestPlugin/Android.mk b/tests/BrowserTestPlugin/Android.mk
deleted file mode 100644
index 968d9e6..0000000
--- a/tests/BrowserTestPlugin/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (C) 2009 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.
-#
-
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build application
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := BrowserTestPlugin
-
-LOCAL_JNI_SHARED_LIBRARIES := libtestplugin
-
-include $(BUILD_PACKAGE)
-
-# ============================================================
-
-# Also build all of the sub-targets under this one: the shared library.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/BrowserTestPlugin/AndroidManifest.xml b/tests/BrowserTestPlugin/AndroidManifest.xml
deleted file mode 100644
index f071ab6..0000000
--- a/tests/BrowserTestPlugin/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.testplugin"
-      android:versionCode="1"
-      android:versionName="1.0">
-
-    <uses-permission android:name="android.webkit.permission.PLUGIN"/>
-
-    <uses-sdk android:minSdkVersion="3" />
-
-    <application android:icon="@drawable/browser_test_plugin"
-                android:label="Browser Test Plugin">
-        <service android:name="TestPlugin">
-            <intent-filter>
-                <action android:name="android.webkit.PLUGIN" />
-            </intent-filter>
-        </service>
-    </application>
-
-</manifest>
diff --git a/tests/BrowserTestPlugin/jni/Android.mk b/tests/BrowserTestPlugin/jni/Android.mk
deleted file mode 100644
index 95a21e9..0000000
--- a/tests/BrowserTestPlugin/jni/Android.mk
+++ /dev/null
@@ -1,49 +0,0 @@
-##
-##
-## Copyright 2009, The Android Open Source Project
-##
-## Redistribution and use in source and binary forms, with or without
-## modification, are permitted provided that the following conditions
-## are met:
-##  * Redistributions of source code must retain the above copyright
-##    notice, this list of conditions and the following disclaimer.
-##  * Redistributions in binary form must reproduce the above copyright
-##    notice, this list of conditions and the following disclaimer in the
-##    documentation and/or other materials provided with the distribution.
-##
-## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
-## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-## PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
-## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
-## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-##
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-	main.cpp \
-	PluginObject.cpp \
-	event/EventPlugin.cpp \
-
-LOCAL_C_INCLUDES += \
-	$(LOCAL_PATH) \
-	$(LOCAL_PATH)/event \
-	external/webkit/WebCore/bridge \
-	external/webkit/WebCore/plugins \
-	external/webkit/WebCore/platform/android/JavaVM \
-	external/webkit/WebKit/android/plugins
-
-LOCAL_CFLAGS += -fvisibility=hidden 
-LOCAL_PRELINK_MODULE := false
-
-LOCAL_MODULE := libtestplugin
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/BrowserTestPlugin/jni/PluginObject.cpp b/tests/BrowserTestPlugin/jni/PluginObject.cpp
deleted file mode 100644
index 68fca60..0000000
--- a/tests/BrowserTestPlugin/jni/PluginObject.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
- consideration of your agreement to the following terms, and your use, installation,
- modification or redistribution of this Apple software constitutes acceptance of these
- terms.  If you do not agree with these terms, please do not use, install, modify or
- redistribute this Apple software.
-
- In consideration of your agreement to abide by the following terms, and subject to these
- terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
- this original Apple software (the "Apple Software"), to use, reproduce, modify and
- redistribute the Apple Software, with or without modifications, in source and/or binary
- forms; provided that if you redistribute the Apple Software in its entirety and without
- modifications, you must retain this notice and the following text and disclaimers in all
- such redistributions of the Apple Software.  Neither the name, trademarks, service marks
- or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
- the Apple Software without specific prior written permission from Apple. Except as expressly
- stated in this notice, no other rights or licenses, express or implied, are granted by Apple
- herein, including but not limited to any patent rights that may be infringed by your
- derivative works or by other works in which the Apple Software may be incorporated.
-
- The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO WARRANTIES,
- EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
- USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
-
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-          OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
- REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
- WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
- OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdlib.h>
-#include "main.h"
-#include "PluginObject.h"
-
-static void pluginInvalidate(NPObject *obj);
-static bool pluginHasProperty(NPObject *obj, NPIdentifier name);
-static bool pluginHasMethod(NPObject *obj, NPIdentifier name);
-static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant);
-static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant);
-static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
-static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
-static NPObject *pluginAllocate(NPP npp, NPClass *theClass);
-static void pluginDeallocate(NPObject *obj);
-static bool pluginRemoveProperty(NPObject *npobj, NPIdentifier name);
-static bool pluginEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count);
-
-
-
-static NPClass pluginClass = {
-    NP_CLASS_STRUCT_VERSION,
-    pluginAllocate,
-    pluginDeallocate,
-    pluginInvalidate,
-    pluginHasMethod,
-    pluginInvoke,
-    pluginInvokeDefault,
-    pluginHasProperty,
-    pluginGetProperty,
-    pluginSetProperty,
-    pluginRemoveProperty,
-    pluginEnumerate
-};
-
-NPClass *getPluginClass(void)
-{
-    return &pluginClass;
-}
-
-static bool identifiersInitialized = false;
-
-#define ID_TESTFILE_PROPERTY            0
-#define NUM_PROPERTY_IDENTIFIERS        1
-
-static NPIdentifier pluginPropertyIdentifiers[NUM_PROPERTY_IDENTIFIERS];
-static const NPUTF8 *pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = {
-    "testfile"
-};
-
-#define ID_GETTESTFILE_METHOD                   0
-#define NUM_METHOD_IDENTIFIERS                  1
-
-static NPIdentifier pluginMethodIdentifiers[NUM_METHOD_IDENTIFIERS];
-static const NPUTF8 *pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = {
-    "getTestFile"
-};
-
-static void initializeIdentifiers(void)
-{
-    browser->getstringidentifiers(pluginPropertyIdentifierNames, NUM_PROPERTY_IDENTIFIERS, pluginPropertyIdentifiers);
-    browser->getstringidentifiers(pluginMethodIdentifierNames, NUM_METHOD_IDENTIFIERS, pluginMethodIdentifiers);
-}
-
-static bool pluginHasProperty(NPObject *obj, NPIdentifier name)
-{
-    int i;
-    for (i = 0; i < NUM_PROPERTY_IDENTIFIERS; i++)
-        if (name == pluginPropertyIdentifiers[i])
-            return true;
-    return false;
-}
-
-static bool pluginHasMethod(NPObject *obj, NPIdentifier name)
-{
-    int i;
-    for (i = 0; i < NUM_METHOD_IDENTIFIERS; i++)
-        if (name == pluginMethodIdentifiers[i])
-            return true;
-    return false;
-}
-
-static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant)
-{
-    PluginObject *plugin = (PluginObject *)obj;
-    if (name == pluginPropertyIdentifiers[ID_TESTFILE_PROPERTY]) {
-        BOOLEAN_TO_NPVARIANT(true, *variant);
-        return true;
-    }
-    return false;
-}
-
-static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant)
-{
-    return false;
-}
-
-static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
-{
-    PluginObject *plugin = (PluginObject *)obj;
-    if (name == pluginMethodIdentifiers[ID_GETTESTFILE_METHOD]) {
-        return true;
-    }
-    return false;
-}
-
-static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result)
-{
-    return false;
-}
-
-static void pluginInvalidate(NPObject *obj)
-{
-    // Release any remaining references to JavaScript objects.
-}
-
-static NPObject *pluginAllocate(NPP npp, NPClass *theClass)
-{
-    PluginObject *newInstance = (PluginObject*) malloc(sizeof(PluginObject));
-    newInstance->header._class = theClass;
-    newInstance->header.referenceCount = 1;
-
-    if (!identifiersInitialized) {
-        identifiersInitialized = true;
-        initializeIdentifiers();
-    }
-
-    newInstance->npp = npp;
-
-    return &newInstance->header;
-}
-
-static void pluginDeallocate(NPObject *obj)
-{
-    free(obj);
-}
-
-static bool pluginRemoveProperty(NPObject *npobj, NPIdentifier name)
-{
-    return false;
-}
-
-static bool pluginEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count)
-{
-    return false;
-}
diff --git a/tests/BrowserTestPlugin/jni/PluginObject.h b/tests/BrowserTestPlugin/jni/PluginObject.h
deleted file mode 100644
index a058d4a..0000000
--- a/tests/BrowserTestPlugin/jni/PluginObject.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
- consideration of your agreement to the following terms, and your use, installation,
- modification or redistribution of this Apple software constitutes acceptance of these
- terms.  If you do not agree with these terms, please do not use, install, modify or
- redistribute this Apple software.
-
- In consideration of your agreement to abide by the following terms, and subject to these
- terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
- this original Apple software (the "Apple Software"), to use, reproduce, modify and
- redistribute the Apple Software, with or without modifications, in source and/or binary
- forms; provided that if you redistribute the Apple Software in its entirety and without
- modifications, you must retain this notice and the following text and disclaimers in all
- such redistributions of the Apple Software.  Neither the name, trademarks, service marks
- or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
- the Apple Software without specific prior written permission from Apple. Except as expressly
- stated in this notice, no other rights or licenses, express or implied, are granted by Apple
- herein, including but not limited to any patent rights that may be infringed by your
- derivative works or by other works in which the Apple Software may be incorporated.
-
- The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO WARRANTIES,
- EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
- USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
-
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-          OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
- REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
- WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
- OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef PluginObject__DEFINED
-#define PluginObject__DEFINED
-
-#include "main.h"
-
-class SubPlugin {
-public:
-    SubPlugin(NPP inst) : m_inst(inst) {}
-    virtual ~SubPlugin() {}
-    virtual int16 handleEvent(const ANPEvent* evt) = 0;
-
-    NPP inst() const { return m_inst; }
-
-private:
-    NPP m_inst;
-};
-
-typedef struct PluginObject {
-    NPObject header;
-    NPP npp;
-    NPWindow* window;
-
-    SubPlugin* subPlugin;
-
-} PluginObject;
-
-NPClass *getPluginClass(void);
-
-#endif // PluginObject__DEFINED
diff --git a/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp b/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
deleted file mode 100644
index 2eff394..0000000
--- a/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "EventPlugin.h"
-#include "android_npapi.h"
-
-#include <stdio.h>
-#include <sys/time.h>
-#include <time.h>
-#include <math.h>
-#include <string.h>
-
-extern NPNetscapeFuncs*        browser;
-extern ANPCanvasInterfaceV0    gCanvasI;
-extern ANPLogInterfaceV0       gLogI;
-extern ANPPaintInterfaceV0     gPaintI;
-extern ANPTypefaceInterfaceV0  gTypefaceI;
-
-///////////////////////////////////////////////////////////////////////////////
-
-EventPlugin::EventPlugin(NPP inst) : SubPlugin(inst) { }
-
-EventPlugin::~EventPlugin() { }
-
-void EventPlugin::drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip) {
-
-    gLogI.log(kDebug_ANPLogType, " ------ %p drawing the plugin (%d,%d)",
-              inst(), bitmap.width, bitmap.height);
-
-    // get the plugin's dimensions according to the DOM
-    PluginObject *obj = (PluginObject*) inst()->pdata;
-    const int W = obj->window->width;
-    const int H = obj->window->height;
-
-    // compute the current zoom level
-    const float zoomFactorW = static_cast<float>(bitmap.width) / W;
-    const float zoomFactorH = static_cast<float>(bitmap.height) / H;
-
-    // check to make sure the zoom level is uniform
-    if (zoomFactorW + .01 < zoomFactorH && zoomFactorW - .01 > zoomFactorH)
-        gLogI.log(kError_ANPLogType, " ------ %p zoom is out of sync (%f,%f)",
-                  inst(), zoomFactorW, zoomFactorH);
-
-    // scale the variables based on the zoom level
-    const int fontSize = (int)(zoomFactorW * 16);
-    const int leftMargin = (int)(zoomFactorW * 10);
-
-    // create and clip a canvas
-    ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
-
-    ANPRectF clipR;
-    clipR.left = clip.left;
-    clipR.top = clip.top;
-    clipR.right = clip.right;
-    clipR.bottom = clip.bottom;
-    gCanvasI.clipRect(canvas, &clipR);
-
-    gCanvasI.drawColor(canvas, 0xFFFFFFFF);
-
-    // configure the paint
-    ANPPaint* paint = gPaintI.newPaint();
-    gPaintI.setFlags(paint, gPaintI.getFlags(paint) | kAntiAlias_ANPPaintFlag);
-    gPaintI.setColor(paint, 0xFF0000FF);
-    gPaintI.setTextSize(paint, fontSize);
-
-    // configure the font
-    ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
-    gPaintI.setTypeface(paint, tf);
-    gTypefaceI.unref(tf);
-
-    // retrieve the font metrics
-    ANPFontMetrics fm;
-    gPaintI.getFontMetrics(paint, &fm);
-
-    // write text on the canvas
-    const char c[] = "Browser Test Plugin";
-    gCanvasI.drawText(canvas, c, sizeof(c)-1, leftMargin, -fm.fTop, paint);
-
-    // clean up variables
-    gPaintI.deletePaint(paint);
-    gCanvasI.deleteCanvas(canvas);
-}
-
-void EventPlugin::printToDiv(const char* text, int length) {
-    // Get the plugin's DOM object
-    NPObject* windowObject = NULL;
-    browser->getvalue(inst(), NPNVWindowNPObject, &windowObject);
-
-    if (!windowObject)
-        gLogI.log(kError_ANPLogType, " ------ %p Unable to retrieve DOM Window", inst());
-
-    // create a string (JS code) that is stored in memory allocated by the browser
-    const char* jsBegin = "var outputDiv = document.getElementById('eventOutput'); outputDiv.innerHTML += ' ";
-    const char* jsEnd = "';";
-
-    // allocate memory and configure pointers
-    int totalLength = strlen(jsBegin) + length + strlen(jsEnd);
-    char* beginMem = (char*)browser->memalloc(totalLength);
-    char* middleMem = beginMem + strlen(jsBegin);
-    char* endMem = middleMem + length;
-
-    // copy into the allocated memory
-    memcpy(beginMem, jsBegin, strlen(jsBegin));
-    memcpy(middleMem, text, length);
-    memcpy(endMem, jsEnd, strlen(jsEnd));
-
-    gLogI.log(kDebug_ANPLogType, "text: %.*s\n", totalLength, (char*)beginMem);
-
-    // execute the javascript in the plugin's DOM object
-    NPString script = { (char*)beginMem, totalLength };
-    NPVariant scriptVariant;
-    if (!browser->evaluate(inst(), windowObject, &script, &scriptVariant))
-        gLogI.log(kError_ANPLogType, " ------ %p Unable to eval the JS.", inst());
-
-    // free the memory allocated within the browser
-    browser->memfree(beginMem);
-}
-
-int16 EventPlugin::handleEvent(const ANPEvent* evt) {
-    switch (evt->eventType) {
-
-        case kDraw_ANPEventType: {
-            switch (evt->data.draw.model) {
-                case kBitmap_ANPDrawingModel:
-                    drawPlugin(evt->data.draw.data.bitmap, evt->data.draw.clip);
-                    return 1;
-                default:
-                    break;   // unknown drawing model
-            }
-        }
-        case kLifecycle_ANPEventType:
-            switch (evt->data.lifecycle.action) {
-                case kOnLoad_ANPLifecycleAction: {
-                    char msg[] = "lifecycle-onLoad";
-                    printToDiv(msg, strlen(msg));
-                    break;
-                }
-                case kGainFocus_ANPLifecycleAction: {
-                    char msg[] = "lifecycle-gainFocus";
-                    printToDiv(msg, strlen(msg));
-                    break;
-                }
-                case kLoseFocus_ANPLifecycleAction: {
-                    char msg[] = "lifecycle-loseFocus";
-                    printToDiv(msg, strlen(msg));
-                    break;
-                }
-            }
-            return 1;
-        case kTouch_ANPEventType:
-            gLogI.log(kError_ANPLogType, " ------ %p the plugin did not request touch events", inst());
-            break;
-        case kKey_ANPEventType:
-            gLogI.log(kError_ANPLogType, " ------ %p the plugin did not request key events", inst());
-            break;
-        default:
-            break;
-    }
-    return 0;   // unknown or unhandled event
-}
diff --git a/tests/BrowserTestPlugin/jni/event/EventPlugin.h b/tests/BrowserTestPlugin/jni/event/EventPlugin.h
deleted file mode 100644
index 88b7c9d..0000000
--- a/tests/BrowserTestPlugin/jni/event/EventPlugin.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "PluginObject.h"
-
-#ifndef eventPlugin__DEFINED
-#define eventPlugin__DEFINED
-
-class EventPlugin : public SubPlugin {
-public:
-    EventPlugin(NPP inst);
-    virtual ~EventPlugin();
-    virtual int16 handleEvent(const ANPEvent* evt);
-
-private:
-    void drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip);
-    void printToDiv(const char* text, int length);
-};
-
-#endif // eventPlugin__DEFINED
diff --git a/tests/BrowserTestPlugin/jni/main.cpp b/tests/BrowserTestPlugin/jni/main.cpp
deleted file mode 100644
index 402a7e2..0000000
--- a/tests/BrowserTestPlugin/jni/main.cpp
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include "android_npapi.h"
-#include "main.h"
-#include "PluginObject.h"
-#include "EventPlugin.h"
-
-NPNetscapeFuncs* browser;
-#define EXPORT __attribute__((visibility("default")))
-
-NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
-        char* argn[], char* argv[], NPSavedData* saved);
-NPError NPP_Destroy(NPP instance, NPSavedData** save);
-NPError NPP_SetWindow(NPP instance, NPWindow* window);
-NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
-        NPBool seekable, uint16* stype);
-NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason);
-int32   NPP_WriteReady(NPP instance, NPStream* stream);
-int32   NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len,
-        void* buffer);
-void    NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname);
-void    NPP_Print(NPP instance, NPPrint* platformPrint);
-int16   NPP_HandleEvent(NPP instance, void* event);
-void    NPP_URLNotify(NPP instance, const char* URL, NPReason reason,
-        void* notifyData);
-NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value);
-NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value);
-
-extern "C" {
-EXPORT NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env);
-EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value);
-EXPORT const char* NP_GetMIMEDescription(void);
-EXPORT void NP_Shutdown(void);
-};
-
-ANPAudioTrackInterfaceV0    gSoundI;
-ANPBitmapInterfaceV0        gBitmapI;
-ANPCanvasInterfaceV0        gCanvasI;
-ANPLogInterfaceV0           gLogI;
-ANPPaintInterfaceV0         gPaintI;
-ANPPathInterfaceV0          gPathI;
-ANPTypefaceInterfaceV0      gTypefaceI;
-ANPWindowInterfaceV0        gWindowI;
-
-#define ARRAY_COUNT(array)      (sizeof(array) / sizeof(array[0]))
-
-NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env)
-{
-    // Make sure we have a function table equal or larger than we are built against.
-    if (browserFuncs->size < sizeof(NPNetscapeFuncs)) {
-        return NPERR_GENERIC_ERROR;
-    }
-
-    // Copy the function table (structure)
-    browser = (NPNetscapeFuncs*) malloc(sizeof(NPNetscapeFuncs));
-    memcpy(browser, browserFuncs, sizeof(NPNetscapeFuncs));
-
-    // Build the plugin function table
-    pluginFuncs->version = 11;
-    pluginFuncs->size = sizeof(pluginFuncs);
-    pluginFuncs->newp = NPP_New;
-    pluginFuncs->destroy = NPP_Destroy;
-    pluginFuncs->setwindow = NPP_SetWindow;
-    pluginFuncs->newstream = NPP_NewStream;
-    pluginFuncs->destroystream = NPP_DestroyStream;
-    pluginFuncs->asfile = NPP_StreamAsFile;
-    pluginFuncs->writeready = NPP_WriteReady;
-    pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write;
-    pluginFuncs->print = NPP_Print;
-    pluginFuncs->event = NPP_HandleEvent;
-    pluginFuncs->urlnotify = NPP_URLNotify;
-    pluginFuncs->getvalue = NPP_GetValue;
-    pluginFuncs->setvalue = NPP_SetValue;
-
-    static const struct {
-        NPNVariable     v;
-        uint32_t        size;
-        ANPInterface*   i;
-    } gPairs[] = {
-        { kCanvasInterfaceV0_ANPGetValue,       sizeof(gCanvasI),   &gCanvasI },
-        { kLogInterfaceV0_ANPGetValue,          sizeof(gLogI),      &gLogI },
-        { kPaintInterfaceV0_ANPGetValue,        sizeof(gPaintI),    &gPaintI },
-        { kTypefaceInterfaceV0_ANPGetValue,     sizeof(gTypefaceI), &gTypefaceI },
-    };
-    for (size_t i = 0; i < ARRAY_COUNT(gPairs); i++) {
-        gPairs[i].i->inSize = gPairs[i].size;
-        NPError err = browser->getvalue(NULL, gPairs[i].v, gPairs[i].i);
-        if (err) {
-            return err;
-        }
-    }
-
-    return NPERR_NO_ERROR;
-}
-
-void NP_Shutdown(void)
-{
-
-}
-
-const char *NP_GetMIMEDescription(void)
-{
-    return "application/x-browsertestplugin:btp:Android Browser Test Plugin";
-}
-
-NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
-                char* argn[], char* argv[], NPSavedData* saved)
-{
-
-
-    gLogI.log(kDebug_ANPLogType, "creating plugin");
-
-    PluginObject *obj = NULL;
-
-    // Scripting functions appeared in NPAPI version 14
-    if (browser->version >= 14) {
-    instance->pdata = browser->createobject (instance, getPluginClass());
-    obj = static_cast<PluginObject*>(instance->pdata);
-    memset(obj, 0, sizeof(*obj));
-    } else {
-        return NPERR_GENERIC_ERROR;
-    }
-
-    // select the drawing model
-    ANPDrawingModel model = kBitmap_ANPDrawingModel;
-
-    // notify the plugin API of the drawing model we wish to use. This must be
-    // done prior to creating certain subPlugin objects (e.g. surfaceViews)
-    NPError err = browser->setvalue(instance, kRequestDrawingModel_ANPSetValue,
-                            reinterpret_cast<void*>(model));
-    if (err) {
-        gLogI.log(kError_ANPLogType, "request model %d err %d", model, err);
-        return err;
-    }
-
-    // create the sub-plugin
-    obj->subPlugin = new EventPlugin(instance);
-
-    return NPERR_NO_ERROR;
-}
-
-NPError NPP_Destroy(NPP instance, NPSavedData** save)
-{
-    PluginObject *obj = (PluginObject*) instance->pdata;
-    if (obj) {
-        delete obj->subPlugin;
-        browser->releaseobject(&obj->header);
-    }
-
-    return NPERR_NO_ERROR;
-}
-
-NPError NPP_SetWindow(NPP instance, NPWindow* window)
-{
-    PluginObject *obj = (PluginObject*) instance->pdata;
-
-    // Do nothing if browser didn't support NPN_CreateObject which would have created the PluginObject.
-    if (obj != NULL) {
-        obj->window = window;
-    }
-
-    return NPERR_NO_ERROR;
-}
-
-NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
-{
-    *stype = NP_ASFILEONLY;
-    return NPERR_NO_ERROR;
-}
-
-NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason)
-{
-    return NPERR_NO_ERROR;
-}
-
-int32 NPP_WriteReady(NPP instance, NPStream* stream)
-{
-    return 0;
-}
-
-int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer)
-{
-    return 0;
-}
-
-void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
-{
-}
-
-void NPP_Print(NPP instance, NPPrint* platformPrint)
-{
-}
-
-int16 NPP_HandleEvent(NPP instance, void* event)
-{
-    PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
-    const ANPEvent* evt = reinterpret_cast<const ANPEvent*>(event);
-
-    if(!obj->subPlugin) {
-        gLogI.log(kError_ANPLogType, "the sub-plugin is null.");
-        return 0; // unknown or unhandled event
-    }
-    else {
-        return obj->subPlugin->handleEvent(evt);
-    }
-}
-
-void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
-{
-}
-
-EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value) {
-
-    if (variable == NPPVpluginNameString) {
-        const char **str = (const char **)value;
-        *str = "Browser Test Plugin";
-        return NPERR_NO_ERROR;
-    }
-
-    if (variable == NPPVpluginDescriptionString) {
-        const char **str = (const char **)value;
-        *str = "Description of Browser Test Plugin";
-        return NPERR_NO_ERROR;
-    }
-
-    return NPERR_GENERIC_ERROR;
-}
-
-NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value)
-{
-    if (variable == NPPVpluginScriptableNPObject) {
-        void **v = (void **)value;
-        PluginObject *obj = (PluginObject*) instance->pdata;
-
-        if (obj)
-            browser->retainobject((NPObject*)obj);
-
-        *v = obj;
-        return NPERR_NO_ERROR;
-    }
-
-    return NPERR_GENERIC_ERROR;
-}
-
-NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
-{
-    return NPERR_GENERIC_ERROR;
-}
-
diff --git a/tests/BrowserTestPlugin/jni/main.h b/tests/BrowserTestPlugin/jni/main.h
deleted file mode 100644
index e6e8c73..0000000
--- a/tests/BrowserTestPlugin/jni/main.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <npapi.h>
-#include <npfunctions.h>
-#include <npruntime.h>
-#include "android_npapi.h"
-
-extern NPNetscapeFuncs* browser;
diff --git a/tests/BrowserTestPlugin/res/drawable/browser_test_plugin.png b/tests/BrowserTestPlugin/res/drawable/browser_test_plugin.png
deleted file mode 100755
index 47c79d1..0000000
--- a/tests/BrowserTestPlugin/res/drawable/browser_test_plugin.png
+++ /dev/null
Binary files differ
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index e4f447e..2b2ec7b 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -685,13 +685,11 @@
     if (*c != 0) return false;
     if (c-val == 0 || c-val > 3) return false;
 
-    int d = atoi(val);
-    if (d != 0) {
-        if (out) out->mnc = d;
-        return true;
+    if (out) {
+        out->mnc = atoi(val);
     }
 
-    return false;
+    return true;
 }
 
 /*
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 c8ba904..0a4f24f 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1655,7 +1655,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),
@@ -1665,6 +1666,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);
@@ -1717,7 +1722,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);
     }
@@ -1768,7 +1773,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;
@@ -1839,7 +1844,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;
         }
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index fc576a6..818c3c6 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1322,6 +1322,22 @@
                     }
                 }
             } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) {
+                // Check whether these strings need valid formats.
+                // (simplified form of what string16 does above)
+                size_t n = block.getAttributeCount();
+                for (size_t i = 0; i < n; i++) {
+                    size_t length;
+                    const uint16_t* attr = block.getAttributeName(i, &length);
+                    if (strcmp16(attr, translatable16.string()) == 0
+                            || strcmp16(attr, formatted16.string()) == 0) {
+                        const uint16_t* value = block.getAttributeStringValue(i, &length);
+                        if (strcmp16(value, false16.string()) == 0) {
+                            curIsFormatted = false;
+                            break;
+                        }
+                    }
+                }
+
                 curTag = &string_array16;
                 curType = array16;
                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 8551b0f..c0d7427 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -451,13 +451,15 @@
                     printf("=?0x%x", (int)value.data);
                 } else if (value.dataType == Res_value::TYPE_STRING) {
                     printf("=\"%s\"",
-                           String8(block->getAttributeStringValue(i, &len)).string());
+                            ResTable::normalizeForOutput(String8(block->getAttributeStringValue(i,
+                                        &len)).string()).string());
                 } else {
                     printf("=(type 0x%x)0x%x", (int)value.dataType, (int)value.data);
                 }
                 const char16_t* val = block->getAttributeStringValue(i, &len);
                 if (val != NULL) {
-                    printf(" (Raw: \"%s\")", String8(val).string());
+                    printf(" (Raw: \"%s\")", ResTable::normalizeForOutput(String8(val).string()).
+                            string());
                 }
                 printf("\n");
             }