am 04a536af: Merge "Import revised translations.  DO NOT MERGE" into honeycomb

* commit '04a536af3c91fc78d07a7d5b242a40b2731ec0ff':
  Import revised translations.  DO NOT MERGE
diff --git a/Android.mk b/Android.mk
index fcb66ae..cfa0d5e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -187,6 +187,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/NOTICE b/NOTICE
index 462d5ae..152be20 100644
--- a/NOTICE
+++ b/NOTICE
@@ -56,16 +56,6 @@
    =========================================================================
    ==  NOTICE file corresponding to the section 4 d of                    ==
    ==  the Apache License, Version 2.0,                                   ==
-   ==  in this case for Additional Codecs code.                           ==
-   =========================================================================
-
-Additional Codecs
-These files are Copyright 2003-2010 VisualOn, but released under
-the Apache2 License.
-
-   =========================================================================
-   ==  NOTICE file corresponding to the section 4 d of                    ==
-   ==  the Apache License, Version 2.0,                                   ==
    ==  in this case for the TagSoup code.                                 ==
    =========================================================================
 
@@ -82,6 +72,16 @@
 OF ANY KIND, either express or implied; not even the implied warranty
 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for Additional Codecs code.                           ==
+   =========================================================================
+
+Additional Codecs
+These files are Copyright 2003-2010 VisualOn, but released under
+the Apache2 License.
+
   =========================================================================
   ==  NOTICE file corresponding to the section 4 d of                    ==
   ==  the Apache License, Version 2.0,                                   ==
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index da518c2..2d03e7c 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1579,9 +1579,11 @@
 
         @Override
         protected void finalize() throws Throwable {
+            // TODO: integrate CloseGuard support.
             try {
                 if(!mCloseFlag) {
-                    ContentResolver.this.releaseProvider(mContentProvider);
+                    Log.w(TAG, "Cursor finalized without prior close()");
+                    close();
                 }
             } finally {
                 super.finalize();
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 622bcdb..8c56fda 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -702,4 +702,28 @@
             return null;
         }
     }
+
+    /**
+     * To change the Secure Element Card Emulation state (ON/OFF)
+     * @hide
+     */
+    public void changeNfcSecureElementCardEmulationState(boolean state)
+    {
+        int seId = 11259375;
+        if(state){
+            /* Enable card emulation */
+            try {
+                sService.selectSecureElement(seId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Enable card emulation failed", e);
+            }
+        }else{
+            /* Disable card emulation */
+            try {
+                sService.deselectSecureElement();
+            } catch (RemoteException e) {
+                Log.e(TAG, " card emulation failed", e);
+            }
+        }
+    }
 }
diff --git a/core/java/android/nfc/tech/MifareUltralight.java b/core/java/android/nfc/tech/MifareUltralight.java
index f7fee72..87c8d99 100644
--- a/core/java/android/nfc/tech/MifareUltralight.java
+++ b/core/java/android/nfc/tech/MifareUltralight.java
@@ -147,7 +147,7 @@
      * @throws IOException if there is an I/O failure, or the operation is canceled
      */
     public byte[] readPages(int pageOffset) throws IOException {
-        validatePageOffset(pageOffset);
+        validatePageIndex(pageOffset);
         checkConnected();
 
         byte[] cmd = { 0x30, (byte) pageOffset};
@@ -172,7 +172,7 @@
      * @throws IOException if there is an I/O failure, or the operation is canceled
      */
     public void writePage(int pageOffset, byte[] data) throws IOException {
-        validatePageOffset(pageOffset);
+        validatePageIndex(pageOffset);
         checkConnected();
 
         byte[] cmd = new byte[data.length + 2];
@@ -202,14 +202,14 @@
         return transceive(data, true);
     }
 
-    private static void validatePageOffset(int pageOffset) {
+    private static void validatePageIndex(int pageIndex) {
         // Do not be too strict on upper bounds checking, since some cards
         // may have more addressable memory than they report.
         // Note that issuing a command to an out-of-bounds block is safe - the
         // tag will wrap the read to an addressable area. This validation is a
         // helper to guard against obvious programming mistakes.
-        if (pageOffset < 0 || pageOffset >= MAX_PAGE_COUNT) {
-            throw new IndexOutOfBoundsException("page out of bounds: " + pageOffset);
+        if (pageIndex < 0 || pageIndex >= MAX_PAGE_COUNT) {
+            throw new IndexOutOfBoundsException("page out of bounds: " + pageIndex);
         }
     }
 }
diff --git a/core/java/android/nfc/tech/Ndef.java b/core/java/android/nfc/tech/Ndef.java
index 6727d6a..e4daa57 100644
--- a/core/java/android/nfc/tech/Ndef.java
+++ b/core/java/android/nfc/tech/Ndef.java
@@ -103,6 +103,8 @@
     public static final int TYPE_4 = 4;
     /** @hide */
     public static final int TYPE_MIFARE_CLASSIC = 101;
+    /** @hide */
+    public static final int TYPE_ICODE_SLI = 102;
 
     /** @hide */
     public static final String UNKNOWN = "android.ndef.unknown";
@@ -117,6 +119,11 @@
     public static final String NFC_FORUM_TYPE_4 = "org.nfcforum.ndef.type4";
     /** NDEF on MIFARE Classic */
     public static final String MIFARE_CLASSIC = "com.nxp.ndef.mifareclassic";
+    /**
+     * NDEF on iCODE SLI
+     * @hide
+     */
+    public static final String ICODE_SLI = "com.nxp.ndef.icodesli";
 
     private final int mMaxNdefSize;
     private final int mCardState;
@@ -200,6 +207,8 @@
                 return NFC_FORUM_TYPE_4;
             case TYPE_MIFARE_CLASSIC:
                 return MIFARE_CLASSIC;
+            case TYPE_ICODE_SLI:
+                return ICODE_SLI;
             default:
                 return UNKNOWN;
         }
diff --git a/core/java/android/nfc/tech/package.html b/core/java/android/nfc/tech/package.html
new file mode 100644
index 0000000..a99828f
--- /dev/null
+++ b/core/java/android/nfc/tech/package.html
@@ -0,0 +1,13 @@
+<HTML>
+<BODY>
+<p>
+These classes provide access to a tag technology's features, which vary by the type
+of tag that is scanned. A scanned tag can support multiple technologies, and you can find
+out what they are by calling {@link android.nfc.Tag#getTechList getTechList()}.</p>
+
+<p>For more information on dealing with tag technologies and handling the ones that you care about, see
+<a href="{@docRoot}guide/topics/nfc/index.html#dispatch">The Tag Dispatch System</a>.
+The {@link android.nfc.tech.TagTechnology} interface provides an overview of the
+supported technologies.</p>
+</BODY>
+</HTML>
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index eb941e4..557e53f 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -96,9 +96,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;
     
@@ -107,11 +107,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;
             }
         }
@@ -248,12 +249,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++;
             }
         }
     }
diff --git a/core/java/android/pim/ICalendar.java b/core/java/android/pim/ICalendar.java
index cc0f45e..9c4eaf4 100644
--- a/core/java/android/pim/ICalendar.java
+++ b/core/java/android/pim/ICalendar.java
@@ -578,6 +578,23 @@
                             + text);
                 }
                 parameter.name = text.substring(startIndex + 1, equalIndex);
+            } else if (c == '"') {
+                if (parameter == null) {
+                    throw new FormatException("Expected parameter before '\"' in " + text);
+                }
+                if (equalIndex == -1) {
+                    throw new FormatException("Expected '=' within parameter in " + text);
+                }
+                if (state.index > equalIndex + 1) {
+                    throw new FormatException("Parameter value cannot contain a '\"' in " + text);
+                }
+                final int endQuote = text.indexOf('"', state.index + 1);
+                if (endQuote < 0) {
+                    throw new FormatException("Expected closing '\"' in " + text);
+                }
+                parameter.value = text.substring(state.index + 1, endQuote);
+                state.index = endQuote + 1;
+                return parameter;
             }
             ++state.index;
         }
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 912a88d..dd39714 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -524,8 +524,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 85ce5e1..5c281cf 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -190,7 +190,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/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index a17ed9d..81346b4 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -38,7 +38,7 @@
  * for the device you are running on.  For example:
  *
  * <pre>LayoutInflater inflater = (LayoutInflater)context.getSystemService
- *      Context.LAYOUT_INFLATER_SERVICE);</pre>
+ *      (Context.LAYOUT_INFLATER_SERVICE);</pre>
  * 
  * <p>
  * To create a new LayoutInflater with an additional {@link Factory} for your
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c26fa93..02e5b63 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1171,14 +1171,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/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 5bdf408..b410699 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -56,7 +56,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/DatePicker.java b/core/java/android/widget/DatePicker.java
index 4a34b45..80131e3 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -711,3 +711,4 @@
         };
     }
 }
+
diff --git a/core/java/android/widget/EdgeGlow.java b/core/java/android/widget/EdgeGlow.java
index 2a0e849..c2cb0a0 100644
--- a/core/java/android/widget/EdgeGlow.java
+++ b/core/java/android/widget/EdgeGlow.java
@@ -339,6 +339,7 @@
                     mEdgeScaleY = mEdgeScaleYStart +
                         (mEdgeScaleYFinish - mEdgeScaleYStart) *
                             interp * factor;
+                    mState = STATE_RECEDE;
                     break;
                 case STATE_RECEDE:
                     mState = STATE_IDLE;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index ef4e4e0..cf72ec4 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -701,8 +701,8 @@
 
             if (mProgress > max) {
                 mProgress = max;
-                refreshProgress(R.id.progress, mProgress, false);
             }
+            refreshProgress(R.id.progress, mProgress, false);
         }
     }
     
diff --git a/core/res/res/values-mcc219/config.xml b/core/res/res/values-mcc219/config.xml
new file mode 100644
index 0000000..7ae82fa
--- /dev/null
+++ b/core/res/res/values-mcc219/config.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+    <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
+    <string-array name="config_twoDigitNumberPattern">
+        <item>"92"</item>
+        <item>"93"</item>
+        <item>"94"</item>
+        <item>"95"</item>
+        <item>"96"</item>
+    </string-array>
+
+</resources>
diff --git a/core/res/res/values-mcc220/config.xml b/core/res/res/values-mcc220/config.xml
new file mode 100644
index 0000000..7ae82fa
--- /dev/null
+++ b/core/res/res/values-mcc220/config.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+    <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
+    <string-array name="config_twoDigitNumberPattern">
+        <item>"92"</item>
+        <item>"93"</item>
+        <item>"94"</item>
+        <item>"95"</item>
+        <item>"96"</item>
+    </string-array>
+
+</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index aa3f722..2e2faf5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -551,4 +551,11 @@
     <!-- Sets whether menu shortcuts should be displayed on panel menus when
          a keyboard is present. -->
     <bool name="config_showMenuShortcutsWhenKeyboardPresent">false</bool>
+
+    <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
+    <string-array name="config_twoDigitNumberPattern">
+    </string-array>
+
+    <!-- The VoiceMail default value is displayed to my own number if it is true -->
+    <bool name="config_telephony_use_own_number_for_voicemail">false</bool>
 </resources>
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
new file mode 100644
index 0000000..2b6dee8b
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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 android.content;
+
+import android.content.ContentResolver;
+import android.provider.ContactsContract;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+public class ContentResolverTest extends AndroidTestCase {
+    private ContentResolver mContentResolver;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContentResolver = mContext.getContentResolver();
+    }
+
+    @LargeTest
+    public void testCursorFinalizer() throws Exception {
+        // TODO: Want a test case that more predictably reproduce this issue. Selected
+        // 600 as this causes the problem 100% of the runs on current hw, it might not
+        // do so on some other configuration though.
+        for (int i = 0; i < 600; i++) {
+            mContentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
+        }
+    }
+}
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 5d00db1..60030f0 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/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index 9a6f787..b6e0c30 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -117,7 +117,11 @@
 status_t DrmManager::setDrmServiceListener(
             int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) {
     Mutex::Autolock _l(mLock);
-    mServiceListeners.add(uniqueId, drmServiceListener);
+    if (NULL != drmServiceListener.get()) {
+        mServiceListeners.add(uniqueId, drmServiceListener);
+    } else {
+        mServiceListeners.removeItem(uniqueId);
+    }
     return DRM_NO_ERROR;
 }
 
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 2f54b33..6caf678 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -738,26 +738,34 @@
      */
     private String convertUriToPath(Uri uri) {
         String path = null;
-        String scheme = uri.getScheme();
-        if (null == scheme || scheme.equals("") || scheme.equals(ContentResolver.SCHEME_FILE)) {
-            path = uri.getPath();
-        } else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
-            String[] projection = new String[] {MediaStore.MediaColumns.DATA};
-            Cursor cursor = null;
-            try {
-                cursor = mContext.getContentResolver().query(uri, projection, null, null, null);
-            } catch (SQLiteException e) {
-                throw new IllegalArgumentException("Given Uri is not formatted in a way " +
-                        "so that it can be found in media store.");
+        if (null != uri) {
+            String scheme = uri.getScheme();
+            if (null == scheme || scheme.equals("") ||
+                    scheme.equals(ContentResolver.SCHEME_FILE)) {
+                path = uri.getPath();
+            } else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
+                String[] projection = new String[] {MediaStore.MediaColumns.DATA};
+                Cursor cursor = null;
+                try {
+                    cursor = mContext.getContentResolver().query(uri, projection, null,
+                            null, null);
+                    if (null == cursor || 0 == cursor.getCount() || !cursor.moveToFirst()) {
+                        throw new IllegalArgumentException("Given Uri could not be found" +
+                                " in media store");
+                    }
+                    int pathIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
+                    path = cursor.getString(pathIndex);
+                } catch (SQLiteException e) {
+                    throw new IllegalArgumentException("Given Uri is not formatted in a way " +
+                            "so that it can be found in media store.");
+                } finally {
+                    if (null != cursor) {
+                        cursor.close();
+                    }
+                }
+            } else {
+                throw new IllegalArgumentException("Given Uri scheme is not supported");
             }
-            if (null == cursor || 0 == cursor.getCount() || !cursor.moveToFirst()) {
-                throw new IllegalArgumentException("Given Uri could not be found in media store");
-            }
-            int pathIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
-            path = cursor.getString(pathIndex);
-            cursor.close();
-        } else {
-            throw new IllegalArgumentException("Given Uri scheme is not supported");
         }
         return path;
     }
diff --git a/drm/libdrmframework/DrmManagerClient.cpp b/drm/libdrmframework/DrmManagerClient.cpp
index 578e135..7b51822 100644
--- a/drm/libdrmframework/DrmManagerClient.cpp
+++ b/drm/libdrmframework/DrmManagerClient.cpp
@@ -31,6 +31,7 @@
 DrmManagerClient::~DrmManagerClient() {
     DrmManagerClientImpl::remove(mUniqueId);
     mDrmManagerClientImpl->removeClient(mUniqueId);
+    mDrmManagerClientImpl->setOnInfoListener(mUniqueId, NULL);
     delete mDrmManagerClientImpl; mDrmManagerClientImpl = NULL;
 }
 
diff --git a/drm/libdrmframework/DrmManagerClientImpl.cpp b/drm/libdrmframework/DrmManagerClientImpl.cpp
index f39131d..d20de92 100644
--- a/drm/libdrmframework/DrmManagerClientImpl.cpp
+++ b/drm/libdrmframework/DrmManagerClientImpl.cpp
@@ -81,7 +81,8 @@
             int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener) {
     Mutex::Autolock _l(mLock);
     mOnInfoListener = infoListener;
-    return getDrmManagerService()->setDrmServiceListener(uniqueId, this);
+    return getDrmManagerService()->setDrmServiceListener(uniqueId,
+            (NULL != infoListener.get()) ? this : NULL);
 }
 
 status_t DrmManagerClientImpl::installDrmEngine(int uniqueId, const String8& drmEngineFile) {
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/include/drm/DrmManagerClient.h b/include/drm/DrmManagerClient.h
index e6ba3c4..085ebf1 100644
--- a/include/drm/DrmManagerClient.h
+++ b/include/drm/DrmManagerClient.h
@@ -49,6 +49,9 @@
     class OnInfoListener: virtual public RefBase {
 
     public:
+        virtual ~OnInfoListener() {}
+
+    public:
         virtual void onInfo(const DrmInfoEvent& event) = 0;
     };
 
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index ed7f53d1..6227f3e 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -1985,6 +1985,7 @@
 
 #ifndef HAVE_ANDROID_OS
     void print(bool inclValues) const;
+    static String8 normalizeForOutput(const char* input);
 #endif
 
 private:
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 73ff230..7197ad7 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -4141,6 +4141,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) {
@@ -4154,13 +4186,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 ad9a94f..b1bd828 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -778,6 +778,7 @@
             // called by a new thread using the same thread ID as this one.
             self->mThread = thread_id_t(-1);
             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 11900d4..edefb22 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -343,7 +343,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);
     }
 
     /**
@@ -463,7 +463,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);
     }
 
     /**
@@ -478,7 +478,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);
     }
 
     /**
@@ -494,7 +494,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);
     }
 
     /**
@@ -510,7 +510,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/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_4g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_4g.png
new file mode 100644
index 0000000..1aea612
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_4g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_4g.png
new file mode 100644
index 0000000..425535e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_4g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_4g.png
new file mode 100644
index 0000000..fcad363
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_4g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_4g.png
new file mode 100644
index 0000000..4ff7db3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_4g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_4g.png
new file mode 100644
index 0000000..2c4a07f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_4g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_4g.png
new file mode 100644
index 0000000..879c703
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inadnout_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inadnout_e.png
new file mode 100644
index 0000000..61a7503
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inadnout_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_4g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_4g.png
new file mode 100644
index 0000000..c5edf2c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_4g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_4g.png
new file mode 100644
index 0000000..ddf88be
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_1x.png
index 78ece9e..c77e61e 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_3g.png
index 31fc1b02..b9f721a 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_4g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_4g.png
new file mode 100644
index 0000000..cff969e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_e.png
index 19adb4b0..d2d7ab3 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_g.png
index fd419ea..83ce6d0 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_h.png
index 94e77ae..abe511f 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_1x.png
index 91328c0..d685af8 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_3g.png
index 2fee6924..8c697a1 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_4g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_4g.png
new file mode 100644
index 0000000..9a4b807
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_e.png
index d0968aa..eb11d04 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_g.png
index 991228b..6e54de0 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_h.png
index ae03e38..5bfb33b 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_1x.png
index 97b011e..119067b 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_3g.png
index a826866..a70cc2e 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_4g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_4g.png
new file mode 100644
index 0000000..ea3dba7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_e.png
index f6a6891..53221b9 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_g.png
index 19b9816..11d44d0 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_h.png
index f8c0961..9defd79 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_1x.png
index 22deb70..136576d 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_3g.png
index c7c1b49..26ca31f 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_4g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_4g.png
new file mode 100644
index 0000000..de8c5ee
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_e.png
index d9a0702..64dbf3c 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_g.png
index 6beed8a..34923fb 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_h.png
index e4179c1..506b5c6 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_1x.png
index 4b2f86d..163976f 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_3g.png
index 6779604..a6af649 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_4g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_4g.png
new file mode 100644
index 0000000..0c08e52
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_e.png
index 1309a97..1d02edb 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_g.png
index 2fc1e8e..edc9536 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_h.png
index 0eef2c1..8376817 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_1x.png
index f8904e2..ecef547 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_3g.png
index 3ef306e..a7c48b6 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_4g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_4g.png
new file mode 100644
index 0000000..f4bcd9a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_e.png
index 2ff6d90..b46bb3a 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_g.png
index 8ff49b0..e8b70f2 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_h.png
index f416203..4e23c4e 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inadnout_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inadnout_e.png
new file mode 100644
index 0000000..ced9175
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inadnout_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_1x.png
index 24b7daa..92d4a19 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_3g.png
index 5ea9142..a208736 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_4g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_4g.png
new file mode 100644
index 0000000..f407bc9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_g.png
index 002bf46..b8a65c2 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_h.png
index 924b84f..a978b68 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_1x.png
index bd0d1ca..710dd52 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_3g.png
index f583eec..a7b35e4 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_4g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_4g.png
new file mode 100644
index 0000000..bb05449
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_e.png
index 66940eaf..a144222 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_g.png
index 0381f52..b0eafb6 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_h.png
index 0b84fe8..f6b83d0 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_h.png
Binary files differ
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
index a834266..1368baa 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
@@ -71,6 +71,7 @@
     private static final int DLG_CONFIRM_KILL_STORAGE_USERS = 1;
     private static final int DLG_ERROR_SHARING = 2;
     static final boolean localLOGV = false;
+    private boolean mDestroyed;
 
     // UI thread
     private Handler mUIHandler;
@@ -136,6 +137,12 @@
         mProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress);
     }
 
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mDestroyed = true;
+    }
+
     private void switchDisplay(final boolean usbStorageInUse) {
         mUIHandler.post(new Runnable() {
             @Override
@@ -232,9 +239,16 @@
         return null;
     }
 
-    private void showDialogInner(int id) {
-        removeDialog(id);
-        showDialog(id);
+    private void scheduleShowDialog(final int id) {
+        mUIHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (!mDestroyed) {
+                    removeDialog(id);
+                    showDialog(id);
+                }
+            }
+        });
     }
 
     private void switchUsbMassStorage(final boolean on) {
@@ -276,7 +290,7 @@
         IMountService ims = getMountService();
         if (ims == null) {
             // Display error dialog
-            showDialogInner(DLG_ERROR_SHARING);
+            scheduleShowDialog(DLG_ERROR_SHARING);
         }
         String extStoragePath = Environment.getExternalStorageDirectory().toString();
         boolean showDialog = false;
@@ -294,11 +308,11 @@
             }
         } catch (RemoteException e) {
             // Display error dialog
-            showDialogInner(DLG_ERROR_SHARING);
+            scheduleShowDialog(DLG_ERROR_SHARING);
         }
         if (showDialog) {
             // Display dialog to user
-            showDialogInner(DLG_CONFIRM_KILL_STORAGE_USERS);
+            scheduleShowDialog(DLG_CONFIRM_KILL_STORAGE_USERS);
         } else {
             if (localLOGV) Log.i(TAG, "Enabling UMS");
             switchUsbMassStorage(true);
diff --git a/packages/WAPPushManager/Android.mk b/packages/WAPPushManager/Android.mk
new file mode 100644
index 0000000..c1d8f4b
--- /dev/null
+++ b/packages/WAPPushManager/Android.mk
@@ -0,0 +1,20 @@
+# Copyright 2007-2008 The Android Open Source Project
+
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := WAPPushManager
+
+LOCAL_STATIC_JAVA_LIBRARIES += android-common
+
+LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags
+
+include $(BUILD_PACKAGE)
+
+# This finds and builds the test apk as well, so a single make does both.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/WAPPushManager/AndroidManifest.xml b/packages/WAPPushManager/AndroidManifest.xml
new file mode 100644
index 0000000..89e9d6a
--- /dev/null
+++ b/packages/WAPPushManager/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.smspush">
+
+    <permission android:name="com.android.smspush.WAPPUSH_MANAGER_BIND"
+        android:protectionLevel="signatureOrSystem" />
+
+    <original-package android:name="com.android.smspush" />
+    <application>
+        <service android:name=".WapPushManager"
+            android:permission="com.android.smspush.WAPPUSH_MANAGER_BIND"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.internal.telephony.IWapPushManager"></action>
+            </intent-filter>
+        </service>
+    </application>
+
+
+</manifest>
diff --git a/packages/WAPPushManager/CleanSpec.mk b/packages/WAPPushManager/CleanSpec.mk
new file mode 100644
index 0000000..b84e1b6
--- /dev/null
+++ b/packages/WAPPushManager/CleanSpec.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/packages/WAPPushManager/MODULE_LICENSE_APACHE2 b/packages/WAPPushManager/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/WAPPushManager/MODULE_LICENSE_APACHE2
diff --git a/packages/WAPPushManager/NOTICE b/packages/WAPPushManager/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/packages/WAPPushManager/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/packages/WAPPushManager/proguard.flags b/packages/WAPPushManager/proguard.flags
new file mode 100644
index 0000000..d09887b
--- /dev/null
+++ b/packages/WAPPushManager/proguard.flags
@@ -0,0 +1,18 @@
+
+#apply method is dynamically referenced by the reflection class.
+-keep class android.app.ContextImpl$SharedPreferencesImpl$EditorImpl {
+    void apply();
+}
+-keep class android.content.SharedPreferences$Editor {
+    void apply();
+}
+
+#WapPushManager is referenced only by AndroidManifest.xml
+-keep class com.android.smspush.WapPushManager {
+#CTS module method
+    public boolean isDataExist(java.lang.String, java.lang.String,
+            java.lang.String, java.lang.String);
+    public boolean verifyData(java.lang.String, java.lang.String,
+            java.lang.String, java.lang.String, int, boolean, boolean);
+}
+
diff --git a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
new file mode 100644
index 0000000..96e0377
--- /dev/null
+++ b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush;
+
+import android.app.Service;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.telephony.WapPushManagerParams;
+
+/**
+ * The WapPushManager service is implemented to process incoming
+ * WAP Push messages and to maintain the Receiver Application/Application
+ * ID mapping. The WapPushManager runs as a system service, and only the
+ * WapPushManager can update the WAP Push message and Receiver Application
+ * mapping (Application ID Table). When the device receives an SMS WAP Push
+ * message, the WapPushManager looks up the Receiver Application name in
+ * Application ID Table. If an application is found, the application is
+ * launched using its full component name instead of broadcasting an implicit
+ * Intent. If a Receiver Application is not found in the Application ID
+ * Table or the WapPushManager returns a process-further value, the
+ * telephony stack will process the message using existing message processing
+ * flow, and broadcast an implicit Intent.
+ */
+public class WapPushManager extends Service {
+
+    private static final String LOG_TAG = "WAP PUSH";
+    private static final String DATABASE_NAME = "wappush.db";
+    private static final String APPID_TABLE_NAME = "appid_tbl";
+
+    /**
+     * Version number must be incremented when table structure is changed.
+     */
+    private static final int WAP_PUSH_MANAGER_VERSION = 1;
+    private static final boolean DEBUG_SQL = false;
+    private static final boolean LOCAL_LOGV = false;
+
+    /**
+     * Inner class that deals with application ID table
+     */
+    private class WapPushManDBHelper extends SQLiteOpenHelper {
+        WapPushManDBHelper(Context context) {
+            super(context, DATABASE_NAME, null, WAP_PUSH_MANAGER_VERSION);
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "helper instance created.");
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "db onCreate.");
+            String sql = "CREATE TABLE " + APPID_TABLE_NAME + " ("
+                    + "id INTEGER PRIMARY KEY, "
+                    + "x_wap_application TEXT, "
+                    + "content_type TEXT, "
+                    + "package_name TEXT, "
+                    + "class_name TEXT, "
+                    + "app_type INTEGER, "
+                    + "need_signature INTEGER, "
+                    + "further_processing INTEGER, "
+                    + "install_order INTEGER "
+                    + ")";
+
+            if (DEBUG_SQL) Log.v(LOG_TAG, "sql: " + sql);
+            db.execSQL(sql);
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db,
+                    int oldVersion, int newVersion) {
+            // TODO: when table structure is changed, need to dump and restore data.
+            /*
+              db.execSQL(
+              "drop table if exists "+APPID_TABLE_NAME);
+              onCreate(db);
+            */
+            Log.w(LOG_TAG, "onUpgrade is not implemented yet. do nothing.");
+        }
+
+        protected class queryData {
+            public String packageName;
+            public String className;
+            int appType;
+            int needSignature;
+            int furtherProcessing;
+            int installOrder;
+        }
+
+        /**
+         * Query the latest receiver application info with supplied application ID and
+         * content type.
+         * @param app_id    application ID to look up
+         * @param content_type    content type to look up
+         */
+        protected queryData queryLastApp(SQLiteDatabase db,
+                String app_id, String content_type) {
+            String sql = "select install_order, package_name, class_name, "
+                    + " app_type, need_signature, further_processing"
+                    + " from " + APPID_TABLE_NAME
+                    + " where x_wap_application=\'" + app_id + "\'"
+                    + " and content_type=\'" + content_type + "\'"
+                    + " order by install_order desc";
+            if (DEBUG_SQL) Log.v(LOG_TAG, "sql: " + sql);
+            Cursor cur = db.rawQuery(sql, null);
+            queryData ret = null;
+
+            if (cur.moveToNext()) {
+                ret = new queryData();
+                ret.installOrder = cur.getInt(cur.getColumnIndex("install_order"));
+                ret.packageName = cur.getString(cur.getColumnIndex("package_name"));
+                ret.className = cur.getString(cur.getColumnIndex("class_name"));
+                ret.appType = cur.getInt(cur.getColumnIndex("app_type"));
+                ret.needSignature = cur.getInt(cur.getColumnIndex("need_signature"));
+                ret.furtherProcessing = cur.getInt(cur.getColumnIndex("further_processing"));
+            }
+            cur.close();
+            return ret;
+        }
+
+    }
+
+    /**
+     * The exported API implementations class
+     */
+    private class IWapPushManagerStub extends IWapPushManager.Stub {
+        public Context mContext;
+
+        public IWapPushManagerStub() {
+
+        }
+
+        /**
+         * Compare the package signature with WapPushManager package
+         */
+        protected boolean signatureCheck(String package_name) {
+            PackageManager pm = mContext.getPackageManager();
+            int match = pm.checkSignatures(mContext.getPackageName(), package_name);
+
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "compare signature " + mContext.getPackageName()
+                    + " and " +  package_name + ", match=" + match);
+
+            return match == PackageManager.SIGNATURE_MATCH;
+        }
+
+        /**
+         * Returns the status value of the message processing.
+         * The message will be processed as follows:
+         * 1.Look up Application ID Table with x-wap-application-id + content type
+         * 2.Check the signature of package name that is found in the
+         *   Application ID Table by using PackageManager.checkSignature
+         * 3.Trigger the Application
+         * 4.Returns the process status value.
+         */
+        public int processMessage(String app_id, String content_type, Intent intent)
+            throws RemoteException {
+            Log.d(LOG_TAG, "wpman processMsg " + app_id + ":" + content_type);
+
+            WapPushManDBHelper dbh = getDatabase(mContext);
+            SQLiteDatabase db = dbh.getReadableDatabase();
+            WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, app_id, content_type);
+            db.close();
+
+            if (lastapp == null) {
+                Log.w(LOG_TAG, "no receiver app found for " + app_id + ":" + content_type);
+                return WapPushManagerParams.APP_QUERY_FAILED;
+            }
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "starting " + lastapp.packageName
+                    + "/" + lastapp.className);
+
+            if (lastapp.needSignature != 0) {
+                if (!signatureCheck(lastapp.packageName)) {
+                    return WapPushManagerParams.SIGNATURE_NO_MATCH;
+                }
+            }
+
+            if (lastapp.appType == WapPushManagerParams.APP_TYPE_ACTIVITY) {
+                //Intent intent = new Intent(Intent.ACTION_MAIN);
+                intent.setClassName(lastapp.packageName, lastapp.className);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+                try {
+                    mContext.startActivity(intent);
+                } catch (ActivityNotFoundException e) {
+                    Log.w(LOG_TAG, "invalid name " +
+                            lastapp.packageName + "/" + lastapp.className);
+                    return WapPushManagerParams.INVALID_RECEIVER_NAME;
+                }
+            } else {
+                intent.setClassName(mContext, lastapp.className);
+                intent.setComponent(new ComponentName(lastapp.packageName,
+                        lastapp.className));
+                if (mContext.startService(intent) == null) {
+                    Log.w(LOG_TAG, "invalid name " +
+                            lastapp.packageName + "/" + lastapp.className);
+                    return WapPushManagerParams.INVALID_RECEIVER_NAME;
+                }
+            }
+
+            return WapPushManagerParams.MESSAGE_HANDLED
+                    | (lastapp.furtherProcessing == 1 ?
+                            WapPushManagerParams.FURTHER_PROCESSING : 0);
+        }
+
+        protected boolean appTypeCheck(int app_type) {
+            if (app_type == WapPushManagerParams.APP_TYPE_ACTIVITY ||
+                    app_type == WapPushManagerParams.APP_TYPE_SERVICE) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        /**
+         * Returns true if adding the package succeeded.
+         */
+        public boolean addPackage(String x_app_id, String content_type,
+                String package_name, String class_name,
+                int app_type, boolean need_signature, boolean further_processing) {
+            WapPushManDBHelper dbh = getDatabase(mContext);
+            SQLiteDatabase db = dbh.getWritableDatabase();
+            WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
+            boolean ret = false;
+            boolean insert = false;
+            int sq = 0;
+
+            if (!appTypeCheck(app_type)) {
+                Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be "
+                        + WapPushManagerParams.APP_TYPE_ACTIVITY + " or "
+                        + WapPushManagerParams.APP_TYPE_SERVICE);
+                return false;
+            }
+
+            if (lastapp == null) {
+                insert = true;
+                sq = 0;
+            } else if (!lastapp.packageName.equals(package_name) ||
+                    !lastapp.className.equals(class_name)) {
+                insert = true;
+                sq = lastapp.installOrder + 1;
+            }
+
+            if (insert) {
+                ContentValues values = new ContentValues();
+
+                values.put("x_wap_application", x_app_id);
+                values.put("content_type", content_type);
+                values.put("package_name", package_name);
+                values.put("class_name", class_name);
+                values.put("app_type", app_type);
+                values.put("need_signature", need_signature ? 1 : 0);
+                values.put("further_processing", further_processing ? 1 : 0);
+                values.put("install_order", sq);
+                db.insert(APPID_TABLE_NAME, null, values);
+                if (LOCAL_LOGV) Log.v(LOG_TAG, "add:" + x_app_id + ":" + content_type
+                        + " " + package_name + "." + class_name
+                        + ", newsq:" + sq);
+                ret = true;
+            }
+
+            db.close();
+
+            return ret;
+        }
+
+        /**
+         * Returns true if updating the package succeeded.
+         */
+        public boolean updatePackage(String x_app_id, String content_type,
+                String package_name, String class_name,
+                int app_type, boolean need_signature, boolean further_processing) {
+
+            if (!appTypeCheck(app_type)) {
+                Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be "
+                        + WapPushManagerParams.APP_TYPE_ACTIVITY + " or "
+                        + WapPushManagerParams.APP_TYPE_SERVICE);
+                return false;
+            }
+
+            WapPushManDBHelper dbh = getDatabase(mContext);
+            SQLiteDatabase db = dbh.getWritableDatabase();
+            WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
+
+            if (lastapp == null) {
+                db.close();
+                return false;
+            }
+
+            ContentValues values = new ContentValues();
+            String where = "x_wap_application=\'" + x_app_id + "\'"
+                    + " and content_type=\'" + content_type + "\'"
+                    + " and install_order=" + lastapp.installOrder;
+
+            values.put("package_name", package_name);
+            values.put("class_name", class_name);
+            values.put("app_type", app_type);
+            values.put("need_signature", need_signature ? 1 : 0);
+            values.put("further_processing", further_processing ? 1 : 0);
+
+            int num = db.update(APPID_TABLE_NAME, values, where, null);
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "update:" + x_app_id + ":" + content_type + " "
+                    + package_name + "." + class_name
+                    + ", sq:" + lastapp.installOrder);
+
+            db.close();
+
+            return num > 0;
+        }
+
+        /**
+         * Returns true if deleting the package succeeded.
+         */
+        public boolean deletePackage(String x_app_id, String content_type,
+                String package_name, String class_name) {
+            WapPushManDBHelper dbh = getDatabase(mContext);
+            SQLiteDatabase db = dbh.getWritableDatabase();
+            String where = "x_wap_application=\'" + x_app_id + "\'"
+                    + " and content_type=\'" + content_type + "\'"
+                    + " and package_name=\'" + package_name + "\'"
+                    + " and class_name=\'" + class_name + "\'";
+            int num_removed = db.delete(APPID_TABLE_NAME, where, null);
+
+            db.close();
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "deleted " + num_removed + " rows:"
+                    + x_app_id + ":" + content_type + " "
+                    + package_name + "." + class_name);
+            return num_removed > 0;
+        }
+    };
+
+
+    /**
+     * Linux IPC Binder
+     */
+    private final IWapPushManagerStub mBinder = new IWapPushManagerStub();
+
+    /**
+     * Default constructor
+     */
+    public WapPushManager() {
+        super();
+        mBinder.mContext = this;
+    }
+
+    @Override
+    public IBinder onBind(Intent arg0) {
+        return mBinder;
+    }
+
+    /**
+     * Application ID database instance
+     */
+    private WapPushManDBHelper mDbHelper = null;
+    protected WapPushManDBHelper getDatabase(Context context) {
+        if (mDbHelper == null) {
+            if (LOCAL_LOGV) Log.v(LOG_TAG, "create new db inst.");
+            mDbHelper = new WapPushManDBHelper(context);
+        }
+        return mDbHelper;
+    }
+
+
+    /**
+     * This method is used for testing
+     */
+    public boolean verifyData(String x_app_id, String content_type,
+            String package_name, String class_name,
+            int app_type, boolean need_signature, boolean further_processing) {
+        WapPushManDBHelper dbh = getDatabase(this);
+        SQLiteDatabase db = dbh.getReadableDatabase();
+        WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
+
+        db.close();
+
+        if (lastapp == null) return false;
+
+        if (lastapp.packageName.equals(package_name)
+                && lastapp.className.equals(class_name)
+                && lastapp.appType == app_type
+                &&  lastapp.needSignature == (need_signature ? 1 : 0)
+                &&  lastapp.furtherProcessing == (further_processing ? 1 : 0)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * This method is used for testing
+     */
+    public boolean isDataExist(String x_app_id, String content_type,
+            String package_name, String class_name) {
+        WapPushManDBHelper dbh = getDatabase(this);
+        SQLiteDatabase db = dbh.getReadableDatabase();
+        boolean ret = dbh.queryLastApp(db, x_app_id, content_type) != null;
+
+        db.close();
+        return ret;
+    }
+
+}
+
diff --git a/packages/WAPPushManager/tests/Android.mk b/packages/WAPPushManager/tests/Android.mk
new file mode 100644
index 0000000..0a95b52
--- /dev/null
+++ b/packages/WAPPushManager/tests/Android.mk
@@ -0,0 +1,38 @@
+# Copyright 2008, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += \
+        src/com/android/smspush/unitTests/IDataVerify.aidl
+
+
+# Notice that we don't have to include the src files of Email because, by
+# running the tests using an instrumentation targeting Eamil, we
+# automatically get all of its classes loaded into our environment.
+
+LOCAL_PACKAGE_NAME := WAPPushManagerTests
+
+LOCAL_INSTRUMENTATION_FOR := WAPPushManager
+
+include $(BUILD_PACKAGE)
+
diff --git a/packages/WAPPushManager/tests/AndroidManifest.xml b/packages/WAPPushManager/tests/AndroidManifest.xml
new file mode 100644
index 0000000..da7634f
--- /dev/null
+++ b/packages/WAPPushManager/tests/AndroidManifest.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+
+-->
+
+<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.smspush.unitTests">
+
+
+     <uses-permission android:name="com.android.smspush.WAPPUSH_MANAGER_BIND" />
+     <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
+     <!--testing.../-->
+     <application android:icon="@drawable/icon" android:label="wappush test">
+         <uses-library android:name="android.test.runner" />
+         <activity android:name=".ClientTest"
+             android:label="wappush test">
+             <intent-filter>
+                 <action android:name="android.intent.action.MAIN" />
+                 <category android:name="android.intent.category.LAUNCHER" />
+             </intent-filter>
+         </activity>
+
+         <receiver android:name=".DrmReceiver" android:enabled="true">
+             <intent-filter>
+                 <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
+                 <data android:mimeType="application/vnd.oma.drm.rights+xml" />
+                 <data android:value="application/vnd.oma.drm.rights+wbxml" />
+             </intent-filter>
+         </receiver>
+
+         <service android:enabled="true" android:name=".ReceiverService"
+            android:exported="true"/>
+
+         <activity android:name=".ReceiverActivity"
+             android:exported="true" android:label="test receiver" />
+
+        <service android:name=".DataVerify"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.smspush.unitTests.IDataVerify" />
+            </intent-filter>
+        </service>
+
+     </application>
+
+     <!--
+     This declares that this application uses the instrumentation test runner targeting
+     the package of com.android.smspush.  To run the tests use the command:
+     "adb shell am instrument -w
+        com.android.smspush.unitTests/android.test.InstrumentationTestRunner"
+     -->
+     <instrumentation android:name="android.test.InstrumentationTestRunner"
+         android:targetPackage="com.android.smspush"
+         android:label="Tests for WAPPushManager"/>
+
+</manifest>
+
diff --git a/packages/WAPPushManager/tests/res/drawable-hdpi/icon.png b/packages/WAPPushManager/tests/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..8074c4c
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png b/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..1095584e
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png b/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/packages/WAPPushManager/tests/res/layout/main.xml b/packages/WAPPushManager/tests/res/layout/main.xml
new file mode 100644
index 0000000..c7bdbb2
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/layout/main.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<AbsoluteLayout
+android:id="@+id/widget133"
+android:layout_width="fill_parent"
+android:layout_height="fill_parent"
+xmlns:android="http://schemas.android.com/apk/res/android"
+>
+<EditText
+android:id="@+id/app_id"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="10"
+android:textSize="18sp"
+android:layout_x="0px"
+android:layout_y="26px"
+>
+</EditText>
+<EditText
+android:id="@+id/cont"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="20"
+android:textSize="18sp"
+android:layout_x="47px"
+android:layout_y="26px"
+>
+</EditText>
+<EditText
+android:id="@+id/pkg"
+android:layout_width="125px"
+android:layout_height="wrap_content"
+android:text="pkg"
+android:textSize="18sp"
+android:layout_x="0px"
+android:layout_y="81px"
+>
+</EditText>
+<EditText
+android:id="@+id/cls"
+android:layout_width="173px"
+android:layout_height="wrap_content"
+android:text="cls"
+android:textSize="18sp"
+android:layout_x="147px"
+android:layout_y="81px"
+>
+</EditText>
+<Button
+android:id="@+id/addpkg"
+android:layout_width="182px"
+android:layout_height="wrap_content"
+android:text="add/update package"
+android:layout_x="15px"
+android:layout_y="225px"
+>
+</Button>
+<TextView
+android:id="@+id/widget52"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="input app_id, cont_type, pkg, cls"
+android:textSize="18sp"
+android:layout_x="0px"
+android:layout_y="0px"
+>
+</TextView>
+<Button
+android:id="@+id/procmsg"
+android:layout_width="109px"
+android:layout_height="wrap_content"
+android:text="process msg"
+android:layout_x="197px"
+android:layout_y="361px"
+>
+</Button>
+<RadioGroup
+android:id="@+id/widget137"
+android:layout_width="83px"
+android:layout_height="80px"
+android:orientation="vertical"
+android:layout_x="19px"
+android:layout_y="137px"
+>
+<RadioButton
+android:id="@+id/act"
+android:layout_width="182px"
+android:layout_height="wrap_content"
+android:text="act"
+android:checked="true"
+>
+</RadioButton>
+<RadioButton
+android:id="@+id/svc"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="svc"
+>
+</RadioButton>
+</RadioGroup>
+<Button
+android:id="@+id/delpkg"
+android:layout_width="174px"
+android:layout_height="wrap_content"
+android:text="delete pkg"
+android:layout_x="14px"
+android:layout_y="283px"
+>
+</Button>
+<EditText
+android:id="@+id/pdu"
+android:layout_width="186px"
+android:layout_height="83px"
+android:text="0006080302030aaf02905c030d6a0085070373616d706c6540646f636f6d6f2e6e652e6a700005c3072009102012345601"
+android:textSize="18sp"
+android:layout_x="10px"
+android:layout_y="341px"
+>
+</EditText>
+<CheckBox
+android:id="@+id/ftr"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="ftr_proc"
+android:layout_x="143px"
+android:layout_y="181px"
+>
+</CheckBox>
+<CheckBox
+android:id="@+id/sig"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="nd_sig"
+android:checked="true"
+android:layout_x="142px"
+android:layout_y="140px"
+>
+</CheckBox>
+</AbsoluteLayout>
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java
new file mode 100644
index 0000000..78fd174
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush.unitTests;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.RadioButton;
+
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.telephony.WapPushManagerParams;
+import com.android.internal.telephony.WapPushOverSms;
+import com.android.internal.util.HexDump;
+import com.android.smspush.WapPushManager;
+
+/**
+ * WapPushManager test application
+ */
+public class ClientTest extends Activity {
+    private static final String LOG_TAG = "WAP PUSH";
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        Button addpbtn = (Button) findViewById(R.id.addpkg);
+        Button procbtn = (Button) findViewById(R.id.procmsg);
+        Button delbtn = (Button) findViewById(R.id.delpkg);
+
+        Log.v(LOG_TAG, "activity created!!");
+
+        addpbtn.setOnClickListener(new View.OnClickListener() {
+                public void onClick(View v) {
+                    EditText app_id = (EditText) findViewById(R.id.app_id);
+                    EditText cont = (EditText) findViewById(R.id.cont);
+                    EditText pkg = (EditText) findViewById(R.id.pkg);
+                    EditText cls = (EditText) findViewById(R.id.cls);
+                    RadioButton act = (RadioButton) findViewById(R.id.act);
+                    CheckBox sig = (CheckBox) findViewById(R.id.sig);
+                    CheckBox ftr = (CheckBox) findViewById(R.id.ftr);
+
+                    try {
+                        if (!mWapPushMan.addPackage(
+                                app_id.getText().toString(),
+                                cont.getText().toString(),
+                                pkg.getText().toString(),
+                                cls.getText().toString(),
+                                act.isChecked() ? WapPushManagerParams.APP_TYPE_ACTIVITY :
+                                WapPushManagerParams.APP_TYPE_SERVICE,
+                                sig.isChecked(), ftr.isChecked())) {
+
+                            Log.w(LOG_TAG, "remote add pkg failed...");
+                            mWapPushMan.updatePackage(
+                                    app_id.getText().toString(),
+                                    cont.getText().toString(),
+                                    pkg.getText().toString(),
+                                    cls.getText().toString(),
+                                    act.isChecked() ? WapPushManagerParams.APP_TYPE_ACTIVITY :
+                                    WapPushManagerParams.APP_TYPE_SERVICE,
+                                    sig.isChecked(), ftr.isChecked());
+                        }
+                    } catch (RemoteException e) {
+                            Log.w(LOG_TAG, "remote func failed...");
+                    }
+                }
+            });
+
+        delbtn.setOnClickListener(new View.OnClickListener() {
+                public void onClick(View v) {
+                    EditText app_id = (EditText) findViewById(R.id.app_id);
+                    EditText cont = (EditText) findViewById(R.id.cont);
+                    EditText pkg = (EditText) findViewById(R.id.pkg);
+                    EditText cls = (EditText) findViewById(R.id.cls);
+                    // CheckBox delall = (CheckBox) findViewById(R.id.delall);
+                    // Log.d(LOG_TAG, "button clicked");
+
+                    try {
+                        mWapPushMan.deletePackage(
+                                app_id.getText().toString(),
+                                cont.getText().toString(),
+                                pkg.getText().toString(),
+                                cls.getText().toString());
+                        // delall.isChecked());
+                    } catch (RemoteException e) {
+                        Log.w(LOG_TAG, "remote func failed...");
+                    }
+                }
+            });
+
+        procbtn.setOnClickListener(new View.OnClickListener() {
+                public void onClick(View v) {
+                    EditText pdu = (EditText) findViewById(R.id.pdu);
+                    EditText app_id = (EditText) findViewById(R.id.app_id);
+                    EditText cont = (EditText) findViewById(R.id.cont);
+
+                    // WapPushOverSms wap = new WapPushOverSms();
+                    // wap.dispatchWapPdu(strToHex(pdu.getText().toString()));
+                    try {
+                        Intent intent = new Intent();
+                        intent.putExtra("transactionId", 0);
+                        intent.putExtra("pduType", 6);
+                        intent.putExtra("header",
+                                HexDump.hexStringToByteArray(pdu.getText().toString()));
+                        intent.putExtra("data",
+                                HexDump.hexStringToByteArray(pdu.getText().toString()));
+
+                        mWapPushMan.processMessage(
+                                app_id.getText().toString(),
+                                cont.getText().toString(),
+                                intent);
+                        //HexDump.hexStringToByteArray(pdu.getText().toString()), 0, 6, 5, 5);
+                    } catch (RemoteException e) {
+                        Log.w(LOG_TAG, "remote func failed...");
+                    }
+                }
+            });
+    }
+
+    private IWapPushManager mWapPushMan;
+    private ServiceConnection conn = new ServiceConnection() {
+        public void onServiceDisconnected(ComponentName name) {
+            mWapPushMan = null;
+            Log.v(LOG_TAG, "service disconnected.");
+        }
+
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mWapPushMan = IWapPushManager.Stub.asInterface(service);
+            Log.v(LOG_TAG, "service connected.");
+        }
+        };
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        Log.v(LOG_TAG, "onStart bind WAPPushManager service "
+                + IWapPushManager.class.getName());
+        this.bindService(new Intent(IWapPushManager.class.getName()), conn,
+                Context.BIND_AUTO_CREATE);
+        Log.v(LOG_TAG, "bind service done.");
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        this.unbindService(conn);
+    }
+
+}
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java
new file mode 100644
index 0000000..ef491fd
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush.unitTests;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * To verify that receiver application receives correct body data.
+ */
+public class DataVerify extends Service {
+    private static final String LOG_TAG = "WAP PUSH";
+    private static final int TIME_WAIT = 100;
+    private static final int WAIT_COUNT = 100;
+    private static byte[] mLastReceivedPdu = null;
+    private static boolean sDataSet = false;
+
+    private class IDataVerifyStub extends IDataVerify.Stub {
+        public Context mContext;
+
+        public IDataVerifyStub() {
+        }
+
+        boolean arrayCompare(byte[] arr1, byte[] arr2) {
+            int i;
+
+            if (arr1 == null || arr2 == null) {
+                if (arr1 == null) {
+                    Log.w(LOG_TAG, "arr1 is null");
+                } else {
+                    Log.w(LOG_TAG, "arr2 is null");
+                }
+                return false;
+            }
+
+            if (arr1.length != arr2.length) {
+                return false;
+            }
+
+            for (i = 0; i < arr1.length; i++) {
+                if (arr1[i] != arr2[i]) return false;
+            }
+            return true;
+        }
+
+        /**
+         * Compare pdu and received pdu
+         */
+        public synchronized boolean verifyData(byte[] pdu) {
+            int cnt = 0;
+
+            while (!sDataSet) {
+                // wait for the activity receive data.
+                try {
+                    Thread.sleep(TIME_WAIT);
+                    if (cnt++ > WAIT_COUNT) {
+                        // don't wait more than 10 sec.
+                        return false;
+                    }
+                } catch (InterruptedException e) {}
+            }
+
+            Log.v(LOG_TAG, "verify pdu");
+            boolean ret = arrayCompare(pdu, mLastReceivedPdu);
+            return ret;
+        }
+
+        /**
+         * Clear the old data. This method must be called before starting the test
+         */
+        public void resetData() {
+            mLastReceivedPdu = null;
+            sDataSet = false;
+        }
+    }
+
+    private final IDataVerifyStub binder = new IDataVerifyStub();
+
+    /**
+     * Constructor
+     */
+    public DataVerify() {
+    }
+
+    /**
+     * Receiver application must call this method when it receives the wap push message
+     */
+    public static void SetLastReceivedPdu(byte[] pdu) {
+        mLastReceivedPdu = pdu;
+        sDataSet = true;
+    }
+
+    @Override
+    public IBinder onBind(Intent arg0) {
+        return binder;
+    }
+
+}
+
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java
new file mode 100644
index 0000000..5f5f121
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush.unitTests;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * A sample wap push receiver application for existing framework
+ * This class is listening for "application/vnd.oma.drm.rights+xml" message
+ */
+public class DrmReceiver extends BroadcastReceiver {
+    private static final String LOG_TAG = "WAP PUSH";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.d(LOG_TAG, "DrmReceiver received.");
+
+        byte[] body;
+        byte[] header;
+
+        body = intent.getByteArrayExtra("data");
+        header = intent.getByteArrayExtra("header");
+
+        Log.d(LOG_TAG, "header:");
+        Log.d(LOG_TAG, HexDump.dumpHexString(header));
+        Log.d(LOG_TAG, "body:");
+        Log.d(LOG_TAG, HexDump.dumpHexString(body));
+
+        DataVerify.SetLastReceivedPdu(body);
+    }
+
+}
+
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl
new file mode 100644
index 0000000..f0670fa
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush.unitTests;
+
+/**
+ * Interface to receiver application data verifyer class
+ */
+interface IDataVerify {
+    /**
+     * Verify data
+     */
+    boolean verifyData(in byte[] pdu);
+
+    /**
+     * Initialize data
+     */
+    void resetData();
+}
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java
new file mode 100644
index 0000000..07f55ea
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush.unitTests;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * Activity type receiver application
+ */
+public class ReceiverActivity extends Activity {
+    private static final String LOG_TAG = "WAP PUSH";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.d(LOG_TAG, "activity created!!");
+
+        Intent in = getIntent();
+        byte[] body;
+        byte[] header;
+
+        body = in.getByteArrayExtra("data");
+        header = in.getByteArrayExtra("header");
+
+        Log.d(LOG_TAG, "header:");
+        Log.d(LOG_TAG, HexDump.dumpHexString(header));
+        Log.d(LOG_TAG, "body:");
+        Log.d(LOG_TAG, HexDump.dumpHexString(body));
+
+        DataVerify.SetLastReceivedPdu(body);
+
+        finish();
+
+    }
+}
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java
new file mode 100644
index 0000000..b024bf5
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush.unitTests;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * Service type receiver application
+ */
+public class ReceiverService extends Service {
+    private static final String LOG_TAG = "WAP PUSH";
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.d(LOG_TAG, "Receiver service created");
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.d(LOG_TAG, "Receiver service started");
+
+        byte[] body;
+        byte[] header;
+        body = intent.getByteArrayExtra("data");
+        header = intent.getByteArrayExtra("header");
+
+        Log.d(LOG_TAG, "header:");
+        Log.d(LOG_TAG, HexDump.dumpHexString(header));
+        Log.d(LOG_TAG, "body:");
+        Log.d(LOG_TAG, HexDump.dumpHexString(body));
+
+        DataVerify.SetLastReceivedPdu(body);
+        return START_STICKY;
+    }
+}
+
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
new file mode 100644
index 0000000..9b0a36b
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
@@ -0,0 +1,2513 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush.unitTests;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.provider.Telephony.Sms.Intents;
+import android.test.ServiceTestCase;
+import android.util.Log;
+import android.util.Config;
+
+import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.telephony.WapPushManagerParams;
+import com.android.internal.telephony.WspTypeDecoder;
+import com.android.internal.util.HexDump;
+import com.android.smspush.WapPushManager;
+
+import java.util.Random;
+
+/**
+ * This is a simple framework for a test of a Service.  See {@link android.test.ServiceTestCase
+ * ServiceTestCase} for more information on how to write and extend service tests.
+ *
+ * To run this test, you can type:
+ * adb shell am instrument -w \
+ * -e class com.android.smspush.unitTests.WapPushTest \
+ * com.android.smspush.unitTests/android.test.InstrumentationTestRunner
+ */
+public class WapPushTest extends ServiceTestCase<WapPushManager> {
+    private static final String LOG_TAG = "WAP PUSH";
+    private static final boolean LOCAL_LOGV = false;
+    private static final int TIME_WAIT = 100;
+
+    protected int mAppIdValue = 0x8002;
+    protected String mAppIdName = "x-wap-application:*";
+    protected int mContentTypeValue = 0x030a;
+    protected String mContentTypeName = "application/vnd.wap.sic";
+
+    protected String mPackageName;
+    protected String mClassName;
+
+    protected byte[] mGsmHeader = {
+            (byte) 0x00, // sc address
+            (byte) 0x40, // TP-MTI
+            (byte) 0x04, // sender address length?
+            (byte) 0x81, (byte) 0x55, (byte) 0x45, // sender address?
+            (byte) 0x00, // data schema
+            (byte) 0x00, // proto ID
+            (byte) 0x01, (byte) 0x60, (byte) 0x12, (byte) 0x31,
+            (byte) 0x74, (byte) 0x34, (byte) 0x63 // time stamp
+    };
+
+    protected byte[] mUserDataHeader = {
+            (byte) 0x07, // UDH len
+            (byte) 0x06, // header len
+            (byte) 0x05, // port addressing type?
+            (byte) 0x00, // dummy
+            (byte) 0x0B, (byte) 0x84, // dest port
+            (byte) 0x23, (byte) 0xF0 // src port
+    };
+
+    protected byte[] mWspHeader;
+
+    protected byte[] mMessageBody = {
+            (byte) 0x00,
+            (byte) 0x01,
+            (byte) 0x02,
+            (byte) 0x03,
+            (byte) 0x04,
+            (byte) 0x05,
+            (byte) 0x06,
+            (byte) 0x07,
+            (byte) 0x08,
+            (byte) 0x09,
+            (byte) 0x0a,
+            (byte) 0x0b,
+            (byte) 0x0c,
+            (byte) 0x0d,
+            (byte) 0x0e,
+            (byte) 0x0f
+    };
+
+    protected int mWspHeaderStart;
+    protected int mWspHeaderLen;
+    protected int mWspContentTypeStart;
+
+    /**
+     * OMA application ID in binary form
+     * http://www.openmobilealliance.org/tech/omna/omna-push-app-id.aspx
+     */
+    final int[] OMA_APPLICATION_ID_VALUES = new int[] {
+            0x00,
+            0x01,
+            0x02,
+            0x03,
+            0x04,
+            0x05,
+            0x06,
+            0x07,
+            0x08,
+            0x09,
+            0x0A,
+            0x8000,
+            0x8001,
+            0x8002,
+            0x8003,
+            0x8004,
+            0x8005,
+            0x8006,
+            0x8007,
+            0x8008,
+            0x8009,
+            0x800B,
+            0x8010
+    };
+
+    /**
+     * OMA application ID in string form
+     * http://www.openmobilealliance.org/tech/omna/omna-push-app-id.aspx
+     */
+    final String[] OMA_APPLICATION_ID_NAMES = new String[] {
+            "x-wap-application:*",
+            "x-wap-application:push.sia",
+            "x-wap-application:wml.ua",
+            "x-wap-application:wta.ua",
+            "x-wap-application:mms.ua",
+            "x-wap-application:push.syncml",
+            "x-wap-application:loc.ua",
+            "x-wap-application:syncml.dm",
+            "x-wap-application:drm.ua",
+            "x-wap-application:emn.ua",
+            "x-wap-application:wv.ua",
+            "x-wap-microsoft:localcontent.ua",
+            "x-wap-microsoft:IMclient.ua",
+            "x-wap-docomo:imode.mail.ua",
+            "x-wap-docomo:imode.mr.ua",
+            "x-wap-docomo:imode.mf.ua",
+            "x-motorola:location.ua",
+            "x-motorola:now.ua",
+            "x-motorola:otaprov.ua",
+            "x-motorola:browser.ua",
+            "x-motorola:splash.ua",
+            "x-wap-nai:mvsw.command",
+            "x-wap-openwave:iota.ua"
+    };
+
+    /**
+     * OMA content type in binary form
+     * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
+     */
+    final int[] OMA_CONTENT_TYPE_VALUES = new int[] {
+            0x00,
+            0x01,
+            0x02,
+            0x03,
+            0x04,
+            0x05,
+            0x06,
+            0x07,
+            0x08,
+            0x09,
+            0x0A,
+            0x0B,
+            0x0C,
+            0x0D,
+            0x0E,
+            0x0F,
+            0x10,
+            0x11,
+            0x12,
+            0x13,
+            0x14,
+            0x15,
+            0x16,
+            0x17,
+            0x18,
+            0x19,
+            0x1A,
+            0x1B,
+            0x1C,
+            0x1D,
+            0x1E,
+            0x1F,
+            0x20,
+            0x21,
+            0x22,
+            0x23,
+            0x24,
+            0x25,
+            0x26,
+            0x27,
+            0x28,
+            0x29,
+            0x2A,
+            0x2B,
+            0x2C,
+            0x2D,
+            0x2E,
+            0x2F,
+            0x30,
+            0x31,
+            0x32,
+            0x33,
+            0x34,
+            0x35,
+            0x36,
+            0x37,
+            0x38,
+            0x39,
+            0x3A,
+            0x3B,
+            0x3C,
+            0x3D,
+            0x3E,
+            0x3F,
+            0x40,
+            0x41,
+            0x42,
+            0x43,
+            0x44,
+            0x45,
+            0x46,
+            0x47,
+            0x48,
+            0x49,
+            0x4A,
+            0x4B,
+            0x4C,
+            0x4D,
+            0x4E,
+            0x4F,
+            0x50,
+            0x51,
+            0x52,
+            0x53,
+            0x54,
+//            0x55,
+//            0x56,
+//            0x57,
+//            0x58,
+            0x0201,
+            0x0202,
+            0x0203,
+            0x0204,
+            0x0205,
+            0x0206,
+            0x0207,
+            0x0208,
+            0x0209,
+            0x020A,
+            0x020B,
+            0x020C,
+            0x0300,
+            0x0301,
+            0x0302,
+            0x0303,
+            0x0304,
+            0x0305,
+            0x0306,
+            0x0307,
+            0x0308,
+            0x0309,
+            0x030A,
+            0x030B,
+            0x030C,
+            0x030D,
+            0x030E,
+            0x030F,
+            0x0310,
+            0x0311,
+            0x0312,
+            0x0313,
+            0x0314,
+            0x0315,
+            0x0316,
+            0x0317,
+            0x0318,
+            0x0319,
+            0x031A,
+            0x031B
+            /*0x031C,
+              0x031D*/
+    };
+
+    /**
+     * OMA content type in string form
+     * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
+     */
+    final String[] OMA_CONTENT_TYPE_NAMES = new String[] {
+            "*/*",
+            "text/*",
+            "text/html",
+            "text/plain",
+            "text/x-hdml",
+            "text/x-ttml",
+            "text/x-vCalendar",
+            "text/x-vCard",
+            "text/vnd.wap.wml",
+            "text/vnd.wap.wmlscript",
+            "text/vnd.wap.wta-event",
+            "multipart/*",
+            "multipart/mixed",
+            "multipart/form-data",
+            "multipart/byterantes",
+            "multipart/alternative",
+            "application/*",
+            "application/java-vm",
+            "application/x-www-form-urlencoded",
+            "application/x-hdmlc",
+            "application/vnd.wap.wmlc",
+            "application/vnd.wap.wmlscriptc",
+            "application/vnd.wap.wta-eventc",
+            "application/vnd.wap.uaprof",
+            "application/vnd.wap.wtls-ca-certificate",
+            "application/vnd.wap.wtls-user-certificate",
+            "application/x-x509-ca-cert",
+            "application/x-x509-user-cert",
+            "image/*",
+            "image/gif",
+            "image/jpeg",
+            "image/tiff",
+            "image/png",
+            "image/vnd.wap.wbmp",
+            "application/vnd.wap.multipart.*",
+            "application/vnd.wap.multipart.mixed",
+            "application/vnd.wap.multipart.form-data",
+            "application/vnd.wap.multipart.byteranges",
+            "application/vnd.wap.multipart.alternative",
+            "application/xml",
+            "text/xml",
+            "application/vnd.wap.wbxml",
+            "application/x-x968-cross-cert",
+            "application/x-x968-ca-cert",
+            "application/x-x968-user-cert",
+            "text/vnd.wap.si",
+            "application/vnd.wap.sic",
+            "text/vnd.wap.sl",
+            "application/vnd.wap.slc",
+            "text/vnd.wap.co",
+            "application/vnd.wap.coc",
+            "application/vnd.wap.multipart.related",
+            "application/vnd.wap.sia",
+            "text/vnd.wap.connectivity-xml",
+            "application/vnd.wap.connectivity-wbxml",
+            "application/pkcs7-mime",
+            "application/vnd.wap.hashed-certificate",
+            "application/vnd.wap.signed-certificate",
+            "application/vnd.wap.cert-response",
+            "application/xhtml+xml",
+            "application/wml+xml",
+            "text/css",
+            "application/vnd.wap.mms-message",
+            "application/vnd.wap.rollover-certificate",
+            "application/vnd.wap.locc+wbxml",
+            "application/vnd.wap.loc+xml",
+            "application/vnd.syncml.dm+wbxml",
+            "application/vnd.syncml.dm+xml",
+            "application/vnd.syncml.notification",
+            "application/vnd.wap.xhtml+xml",
+            "application/vnd.wv.csp.cir",
+            "application/vnd.oma.dd+xml",
+            "application/vnd.oma.drm.message",
+            "application/vnd.oma.drm.content",
+            "application/vnd.oma.drm.rights+xml",
+            "application/vnd.oma.drm.rights+wbxml",
+            "application/vnd.wv.csp+xml",
+            "application/vnd.wv.csp+wbxml",
+            "application/vnd.syncml.ds.notification",
+            "audio/*",
+            "video/*",
+            "application/vnd.oma.dd2+xml",
+            "application/mikey",
+            "application/vnd.oma.dcd",
+            "application/vnd.oma.dcdc",
+//            "text/x-vMessage",
+//            "application/vnd.omads-email+wbxml",
+//            "text/x-vBookmark",
+//            "application/vnd.syncml.dm.notification",
+            "application/vnd.uplanet.cacheop-wbxml",
+            "application/vnd.uplanet.signal",
+            "application/vnd.uplanet.alert-wbxml",
+            "application/vnd.uplanet.list-wbxml",
+            "application/vnd.uplanet.listcmd-wbxml",
+            "application/vnd.uplanet.channel-wbxml",
+            "application/vnd.uplanet.provisioning-status-uri",
+            "x-wap.multipart/vnd.uplanet.header-set",
+            "application/vnd.uplanet.bearer-choice-wbxml",
+            "application/vnd.phonecom.mmc-wbxml",
+            "application/vnd.nokia.syncset+wbxml",
+            "image/x-up-wpng",
+            "application/iota.mmc-wbxml",
+            "application/iota.mmc-xml",
+            "application/vnd.syncml+xml",
+            "application/vnd.syncml+wbxml",
+            "text/vnd.wap.emn+xml",
+            "text/calendar",
+            "application/vnd.omads-email+xml",
+            "application/vnd.omads-file+xml",
+            "application/vnd.omads-folder+xml",
+            "text/directory;profile=vCard",
+            "application/vnd.wap.emn+wbxml",
+            "application/vnd.nokia.ipdc-purchase-response",
+            "application/vnd.motorola.screen3+xml",
+            "application/vnd.motorola.screen3+gzip",
+            "application/vnd.cmcc.setting+wbxml",
+            "application/vnd.cmcc.bombing+wbxml",
+            "application/vnd.docomo.pf",
+            "application/vnd.docomo.ub",
+            "application/vnd.omaloc-supl-init",
+            "application/vnd.oma.group-usage-list+xml",
+            "application/oma-directory+xml",
+            "application/vnd.docomo.pf2",
+            "application/vnd.oma.drm.roap-trigger+wbxml",
+            "application/vnd.sbm.mid2",
+            "application/vnd.wmf.bootstrap",
+            "application/vnc.cmcc.dcd+xml",
+            "application/vnd.sbm.cid",
+            "application/vnd.oma.bcast.provisioningtrigger",
+            /*"application/vnd.docomo.dm",
+              "application/vnd.oma.scidm.messages+xml"*/
+    };
+
+    private IDataVerify mIVerify = null;
+
+    ServiceConnection mConn = new ServiceConnection() {
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                Log.v(LOG_TAG, "data verify interface connected.");
+                mIVerify = IDataVerify.Stub.asInterface(service);
+            }
+            public void onServiceDisconnected(ComponentName name) {
+            }
+        };
+
+    /**
+     * Main WapPushManager test module constructor
+     */
+    public WapPushTest() {
+        super(WapPushManager.class);
+        mClassName = this.getClass().getName();
+        mPackageName = this.getClass().getPackage().getName();
+    }
+
+    /**
+     * Initialize the verifier
+     */
+    @Override
+    public void setUp() {
+        try {
+            super.setUp();
+            // get verifier
+            getContext().bindService(new Intent(IDataVerify.class.getName()),
+                    mConn, Context.BIND_AUTO_CREATE);
+        } catch (Exception e) {
+            Log.w(LOG_TAG, "super exception");
+        }
+        // Log.d(LOG_TAG, "test setup");
+    }
+
+    private IWapPushManager mWapPush = null;
+    IWapPushManager getInterface() {
+        if (mWapPush != null) return mWapPush;
+        Intent startIntent = new Intent();
+        startIntent.setClass(getContext(), WapPushManager.class);
+        IBinder service = bindService(startIntent);
+
+        mWapPush = IWapPushManager.Stub.asInterface(service);
+        return mWapPush;
+    }
+
+    /*
+     * All methods need to start with 'test'.
+     * Use various assert methods to pass/fail the test case.
+     */
+    protected void utAddPackage(boolean need_sig, boolean more_proc) {
+        IWapPushManager iwapman = getInterface();
+
+        // insert new data
+        try {
+            assertTrue(iwapman.addPackage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        // verify the data
+        WapPushManager wpman = getService();
+        assertTrue(wpman.verifyData(Integer.toString(mAppIdValue),
+                Integer.toString(mContentTypeValue),
+                mPackageName, mClassName,
+                WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+    }
+
+    /**
+     * Add package test
+     */
+    public void testAddPackage1() {
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+
+        utAddPackage(true, true);
+        mAppIdValue += 10;
+        utAddPackage(true, false);
+        mContentTypeValue += 20;
+        utAddPackage(false, true);
+        mContentTypeValue += 20;
+        utAddPackage(false, false);
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+
+        // clean up data
+        try {
+            IWapPushManager iwapman = getInterface();
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            mAppIdValue += 10;
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            mContentTypeValue += 20;
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            mContentTypeValue += 20;
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /**
+     * Add duprecated package test.
+     */
+    public void testAddPackage2() {
+        try {
+            IWapPushManager iwapman = getInterface();
+
+            // set up data
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+                    false, false);
+            iwapman.addPackage(Integer.toString(mAppIdValue + 10),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+                    false, false);
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue + 10), mPackageName, mClassName, 0,
+                    false, false);
+
+            assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+                    false, false));
+            assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue + 10),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+                    false, false));
+            assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue + 10), mPackageName, mClassName, 0,
+                    false, false));
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue + 10), mPackageName, mClassName);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+    }
+
+    protected void utUpdatePackage(boolean need_sig, boolean more_proc) {
+        IWapPushManager iwapman = getInterface();
+
+        // insert new data
+        try {
+            assertTrue(iwapman.updatePackage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        // verify the data
+        WapPushManager wpman = getService();
+        assertTrue(wpman.verifyData(
+                Integer.toString(mAppIdValue),
+                Integer.toString(mContentTypeValue),
+                mPackageName, mClassName,
+                WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+    }
+
+    /**
+     * Updating package test
+     */
+    public void testUpdatePackage1() {
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+
+        // set up data
+        try {
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            mAppIdValue += 10;
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            mContentTypeValue += 20;
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            mContentTypeValue += 20;
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        utUpdatePackage(false, false);
+        mAppIdValue += 10;
+        utUpdatePackage(false, true);
+        mContentTypeValue += 20;
+        utUpdatePackage(true, false);
+        mContentTypeValue += 20;
+        utUpdatePackage(true, true);
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+
+        // clean up data
+        try {
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            mAppIdValue += 10;
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            mContentTypeValue += 20;
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            mContentTypeValue += 20;
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /**
+     * Updating invalid package test
+     */
+    public void testUpdatePackage2() {
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+
+        try {
+            // set up data
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            assertFalse(iwapman.updatePackage(
+                    Integer.toString(mAppIdValue + 10),
+                    Integer.toString(mContentTypeValue),
+                    mPackageName, mClassName, 0, false, false));
+            assertFalse(iwapman.updatePackage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue + 10),
+                    mPackageName, mClassName, 0, false, false));
+            assertTrue(iwapman.updatePackage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    mPackageName + "dummy_data", mClassName, 0, false, false));
+            assertTrue(iwapman.updatePackage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    mPackageName, mClassName + "dummy_data", 0, false, false));
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName,
+                    mClassName + "dummy_data");
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+    }
+
+    protected void utDeletePackage() {
+        IWapPushManager iwapman = getInterface();
+
+        try {
+            assertTrue(iwapman.deletePackage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    mPackageName, mClassName));
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        // verify the data
+        WapPushManager wpman = getService();
+        assertTrue(!wpman.isDataExist(
+                Integer.toString(mAppIdValue),
+                Integer.toString(mContentTypeValue),
+                mPackageName, mClassName));
+    }
+
+    /**
+     * Deleting package test
+     */
+    public void testDeletePackage1() {
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+
+        // set up data
+        try {
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            mAppIdValue += 10;
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            mContentTypeValue += 20;
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+            mContentTypeValue += 20;
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        utDeletePackage();
+        mAppIdValue += 10;
+        utDeletePackage();
+        mContentTypeValue += 20;
+        utDeletePackage();
+        mContentTypeValue += 20;
+        utDeletePackage();
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /**
+     * Deleting invalid package test
+     */
+    public void testDeletePackage2() {
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+
+        try {
+            // set up data
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    0, false, false);
+
+            assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName));
+            assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue + 20), mPackageName, mClassName));
+            assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+                    Integer.toString(mContentTypeValue + 20), mPackageName, mClassName));
+
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+    }
+
+
+    protected int encodeUint32(int uint32Val, byte[] arr, int start) {
+        int bit = 1;
+        int topbit = 0;
+        int encodeLen;
+        int tmpVal;
+
+        assertTrue(uint32Val >= 0);
+        for (int i = 0; i < 31; i++) {
+            if ((bit & uint32Val) > 0) topbit = i;
+            bit = (bit << 1);
+        }
+        encodeLen = topbit/7 + 1;
+        if (arr == null) return encodeLen;
+
+        //Log.d(LOG_TAG, "uint32Val = " + Integer.toHexString(uint32Val) + ", topbit = "
+        //      + topbit + ", encodeLen = " + encodeLen);
+
+        tmpVal = uint32Val;
+        for (int i = encodeLen - 1; i >= 0; i--) {
+            long val = 0;
+            if (i < encodeLen - 1) val = 0x80;
+            val |= tmpVal & 0x7f;
+            arr[start + i] = (byte) (val & 0xFF);
+            tmpVal = (tmpVal >> 7);
+        }
+        return encodeLen;
+    }
+
+    protected int encodeShortInt(int sintVal, byte[] arr, int start) {
+        int encodeLen = 0;
+
+        if (sintVal >= 0x80) return encodeLen;
+        encodeLen = 1;
+        arr[start] = (byte) (sintVal | 0x80);
+        return encodeLen;
+    }
+
+
+    /**
+     * Generate Random WSP header with integer application ID
+     */
+    protected void createRandomWspHeader(byte[] arr, Random rd, int headerStart,
+            boolean noAppId) {
+
+        boolean appIdAdded = false;
+
+        Log.d(LOG_TAG, "headerStart = " + headerStart + ", appId = " + mAppIdValue
+                + "(" + Integer.toHexString(mAppIdValue) + ")");
+        Log.d(LOG_TAG, "random arr length:" + arr.length);
+        String typename[] = new String[] { "short int", "long int", "string", "uint32"};
+
+        while (!appIdAdded) {
+            int type;
+            int index = headerStart;
+            int len = arr.length;
+            int i;
+            boolean addAppid = false;
+            int tmpVal = 0;
+            int tmpVal2 = 0;
+
+            while (true) {
+                int add;
+
+                /*
+                 * field name
+                 * 0: short int
+                 * 1: long int
+                 * 2: text
+                 * (no uint for param value)
+                 */
+                type = rd.nextInt(3);
+                switch (type) {
+                case 0: // header short integer
+                    if (index > 100 && !appIdAdded) addAppid = true;
+                    add = 1;
+                    break;
+                case 1: // header long int
+                    add = 1 + rd.nextInt(29);
+                    break;
+                default: // header string
+                    add = 2 + rd.nextInt(10);
+                    break;
+                }
+                if (index + add >= len) break;
+
+                // fill header name
+                switch (type) {
+                case 0: // header short integer
+                    if (!addAppid) {
+                        do {
+                            arr[index] = (byte) (0x80 | rd.nextInt(128));
+                        } while (arr[index] == (byte) 0xaf);
+                    } else {
+                        Log.d(LOG_TAG, "appId added.");
+                        arr[index] = (byte) 0xaf;
+                        // if noAppId case, appId fld must be decieved.
+                        if (noAppId) arr[index]++;
+                    }
+                    break;
+                case 1: // header long int
+                    arr[index] = (byte) (add - 1);
+                    tmpVal2 = 0;
+                    for (i = 1; i < add; i++) {
+                        tmpVal = rd.nextInt(255);
+                        tmpVal2 = (tmpVal2 << 8) | tmpVal;
+                        arr[index + i] = (byte) tmpVal;
+                    }
+                    // don't set application id
+                    if (tmpVal2 == 0x2f) arr[index + 1]++;
+                    break;
+                default: // header string
+                    for (i = 0; i < add - 1; i++) {
+                        tmpVal = rd.nextInt(127);
+                        if (tmpVal < 32) tmpVal= (32 + tmpVal);
+                        arr[index + i] = (byte) tmpVal;
+                    }
+                    arr[index + i] = (byte) 0x0;
+                    break;
+                }
+
+                if (LOCAL_LOGV) {
+                    Log.d(LOG_TAG, "field name index:" + index);
+                    Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+                    if (type != 2) {
+                        for (i = index; i< index + add; i++) {
+                            System.out.print(Integer.toHexString(0xff & arr[i]));
+                            System.out.print(' ');
+                        }
+                    } else {
+                        System.out.print(Integer.toHexString(0xff & arr[index]));
+                        System.out.print(' ');
+                        String str = new String(arr, index + 1, add - 2);
+                        for (i = 0; i < str.length(); i++) {
+                            System.out.print(str.charAt(i));
+                            System.out.print(' ');
+                        }
+                    }
+                    System.out.print('\n');
+                }
+                index += add;
+
+
+                /*
+                 * field value
+                 * 0: short int
+                 * 1: long int
+                 * 2: text
+                 * 3: uint
+                 */
+                if (addAppid) {
+                    type = 1;
+                } else {
+                    type = rd.nextInt(4);
+                }
+                switch (type) {
+                case 0: // header short integer
+                    add = 1;
+                    break;
+                case 1: // header long int
+                    if (addAppid) {
+                        int bit = 1;
+                        int topBit = 0;
+
+                        for (i = 0; i < 31; i++) {
+                            if ((mAppIdValue & bit) > 0) topBit = i;
+                            bit = (bit << 1);
+                        }
+                        add = 2 + topBit/8;
+                    } else {
+                        add = 1 + rd.nextInt(29);
+                    }
+                    break;
+                case 2: // header string
+                    add = 2 + rd.nextInt(10);
+                    break;
+                default: // uint32
+                    add = 6;
+                }
+                if (index + add >= len) break;
+
+                // fill field value
+                switch (type) {
+                case 0: // header short int
+                    arr[index] = (byte) (0x80 | rd.nextInt(128));
+                    break;
+                case 1: // header long int
+                    if (addAppid) {
+                        addAppid = false;
+                        appIdAdded = true;
+
+                        arr[index] = (byte) (add - 1);
+                        tmpVal = mAppIdValue;
+                        for (i = add; i > 1; i--) {
+                            arr[index + i - 1] = (byte) (tmpVal & 0xff);
+                            tmpVal = (tmpVal >> 8);
+                        }
+                    } else {
+                        arr[index] = (byte) (add - 1);
+                        for (i = 1; i < add; i++) {
+                            arr[index + i] = (byte) rd.nextInt(255);
+                        }
+                    }
+                    break;
+                case 2:// header string
+                    for (i = 0; i < add - 1; i++) {
+                        tmpVal = rd.nextInt(127);
+                        if (tmpVal < 32) tmpVal= (32 + tmpVal);
+                        arr[index + i] = (byte) tmpVal;
+                    }
+                    arr[index + i] = (byte) 0x0;
+                    break;
+                default: // header uvarint
+                    arr[index] = (byte) 31;
+                    tmpVal = rd.nextInt(0x0FFFFFFF);
+                    add = 1 + encodeUint32(tmpVal, null, index + 1);
+                    encodeUint32(tmpVal, arr, index + 1);
+                    break;
+
+                }
+
+                if (LOCAL_LOGV) {
+                    Log.d(LOG_TAG, "field value index:" + index);
+                    Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+                    if (type != 2) {
+                        for (i = index; i< index + add; i++) {
+                            System.out.print(Integer.toHexString(0xff & arr[i]));
+                            System.out.print(' ');
+                        }
+                    } else {
+                        System.out.print(Integer.toHexString(0xff & arr[index]));
+                        System.out.print(' ');
+                        String str = new String(arr, index + 1, add - 2);
+                        for (i = 0; i < str.length(); i++) {
+                            System.out.print(str.charAt(i));
+                            System.out.print(' ');
+                        }
+                    }
+                    System.out.print('\n');
+                }
+                index += add;
+            }
+            if (noAppId) break;
+        }
+
+        Log.d(LOG_TAG, HexDump.dumpHexString(arr));
+    }
+
+    /**
+     * Generate Random WSP header with string application ID
+     */
+    protected void createRandomWspHeaderStrAppId(byte[] arr, Random rd, int headerStart,
+            boolean randomStr) {
+
+        boolean appIdAdded = false;
+
+        Log.d(LOG_TAG, "random arr length:" + arr.length);
+        String typename[] = new String[] { "short int", "long int", "string", "uint32"};
+
+        while (!appIdAdded) {
+            int type;
+            int index = headerStart;
+            int len = arr.length;
+            int i;
+            boolean addAppid = false;
+            int tmpVal = 0;
+            int tmpVal2 = 0;
+
+            while (true) {
+                int add;
+
+                /*
+                 * field name
+                 * 0: short int
+                 * 1: long int
+                 * 2: text
+                 * (no uint for param value)
+                 */
+                type = rd.nextInt(3);
+                switch (type) {
+                case 0: // header short integer
+                    if (index > 100 && !appIdAdded) addAppid = true;
+                    add = 1;
+                    break;
+                case 1: // header long int
+                    add = 1 + rd.nextInt(29);
+                    break;
+                default: // header string
+                    add = 2 + rd.nextInt(10);
+                    break;
+                }
+                if (index + add >= len) break;
+
+                // fill header name
+                switch (type) {
+                case 0: // header short integer
+                    if (!addAppid) {
+                        do {
+                            arr[index] = (byte) (0x80 | rd.nextInt(128));
+                        } while (arr[index] == (byte) 0xaf);
+                    } else {
+                        Log.d(LOG_TAG, "appId added.");
+                        arr[index] = (byte) 0xaf;
+                    }
+                    break;
+                case 1: // header long int
+                    arr[index] = (byte) (add - 1);
+                    tmpVal2 = 0;
+                    for (i = 1; i < add; i++) {
+                        tmpVal = rd.nextInt(255);
+                        tmpVal2 = (tmpVal2 << 8) | tmpVal;
+                        arr[index + i] = (byte) tmpVal;
+                    }
+                    // don't set application id
+                    if (tmpVal2 == 0x2f) arr[index + 1]++;
+                    break;
+                default: // header string
+                    for (i = 0; i < add - 1; i++) {
+                        tmpVal = rd.nextInt(127);
+                        if (tmpVal < 32) tmpVal= (32 + tmpVal);
+                        arr[index + i] = (byte) tmpVal;
+                    }
+                    arr[index + i] = (byte) 0x0;
+                    break;
+                }
+
+                if (LOCAL_LOGV) {
+                    Log.d(LOG_TAG, "field name index:" + index);
+                    Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+                    if (type != 2) {
+                        for (i = index; i < index + add; i++) {
+                            System.out.print(Integer.toHexString(0xff & arr[i]));
+                            System.out.print(' ');
+                        }
+                    } else {
+                        System.out.print(Integer.toHexString(0xff & arr[index]));
+                        System.out.print(' ');
+                        String str = new String(arr, index + 1, add - 2);
+                        for (i = 0; i < str.length(); i++) {
+                            System.out.print(str.charAt(i));
+                            System.out.print(' ');
+                        }
+                    }
+                    System.out.print('\n');
+                }
+                index += add;
+
+
+                /*
+                 * field value
+                 * 0: short int
+                 * 1: long int
+                 * 2: text
+                 * 3: uint
+                 */
+                if (addAppid) {
+                    type = 2;
+                } else {
+                    type = rd.nextInt(4);
+                }
+                switch (type) {
+                case 0: // header short integer
+                    add = 1;
+                    break;
+                case 1: // header long int
+                    add = 1 + rd.nextInt(29);
+                    break;
+                case 2: // header string
+                    if (addAppid) {
+                        if (randomStr) {
+                            add = 1 + rd.nextInt(10);
+                            byte[] randStr= new byte[add];
+                            for (i = 0; i < add; i++) {
+                                tmpVal = rd.nextInt(127);
+                                if (tmpVal < 32) tmpVal= (32 + tmpVal);
+                                randStr[i] = (byte) tmpVal;
+                            }
+                            mAppIdName = new String(randStr);
+                        }
+                        add = mAppIdName.length() + 1;
+                    } else {
+                        add = 2 + rd.nextInt(10);
+                    }
+                    break;
+                default: // uint32
+                    add = 6;
+                }
+                if (index + add >= len) break;
+
+                // fill field value
+                switch (type) {
+                case 0: // header short int
+                    arr[index] = (byte) (0x80 | rd.nextInt(128));
+                    break;
+                case 1: // header long int
+                    arr[index] = (byte) (add - 1);
+                    for (i = 1; i < add; i++)
+                        arr[index + i] = (byte) rd.nextInt(255);
+                    break;
+                case 2:// header string
+                    if (addAppid) {
+                        addAppid = false;
+                        appIdAdded = true;
+                        for (i = 0; i < add - 1; i++) {
+                            arr[index + i] = (byte) (mAppIdName.charAt(i));
+                        }
+                        Log.d(LOG_TAG, "mAppIdName added [" + mAppIdName + "]");
+                    } else {
+                        for (i = 0; i < add - 1; i++) {
+                            tmpVal = rd.nextInt(127);
+                            if (tmpVal < 32) tmpVal= (32 + tmpVal);
+                            arr[index + i] = (byte) tmpVal;
+                        }
+                    }
+                    arr[index + i] = (byte) 0x0;
+                    break;
+                default: // header uvarint
+                    arr[index] = (byte) 31;
+                    tmpVal = rd.nextInt(0x0FFFFFFF);
+                    add = 1 + encodeUint32(tmpVal, null, index + 1);
+                    encodeUint32(tmpVal, arr, index + 1);
+                    break;
+
+                }
+
+                if (LOCAL_LOGV) {
+                    Log.d(LOG_TAG, "field value index:" + index);
+                    Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+                    if (type != 2) {
+                        for (i = index; i < index + add; i++) {
+                            System.out.print(Integer.toHexString(0xff & arr[i]));
+                            System.out.print(' ');
+                        }
+                    } else {
+                        System.out.print(Integer.toHexString(0xff & arr[index]));
+                        System.out.print(' ');
+                        String str = new String(arr, index + 1, add - 2);
+                        for (i = 0; i < str.length(); i++) {
+                            System.out.print(str.charAt(i));
+                            System.out.print(' ');
+                        }
+                    }
+                    System.out.print('\n');
+                }
+                index += add;
+            }
+        }
+
+        Log.d(LOG_TAG, "headerStart = " + headerStart + ", mAppIdName = " + mAppIdName);
+        Log.d(LOG_TAG, HexDump.dumpHexString(arr));
+    }
+
+    protected byte[] createPDU(int testNum) {
+        byte[] array = null;
+        // byte[] wsp = null;
+
+        switch (testNum) {
+            // sample pdu
+        case 1:
+            byte[] array1 = {
+                    (byte) 0x00, // TID
+                    (byte) 0x06, // Type = wap push
+                    (byte) 0x00, // Length to be set later.
+
+                    // Content-Type
+                    (byte) 0x03, (byte) 0x02,
+                    (byte) ((mContentTypeValue >> 8) & 0xff),
+                    (byte) (mContentTypeValue & 0xff),
+
+                    // Application-id
+                    (byte) 0xaf, (byte) 0x02,
+                    (byte) ((mAppIdValue >> 8) & 0xff),
+                    (byte) (mAppIdValue& 0xff)
+            };
+            array1[2] = (byte) (array1.length - 3);
+            mWspHeader = array1;
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + 7;
+            mWspHeaderLen = array1.length;
+            break;
+
+            // invalid wsp header
+        case 2:
+            byte[] array2 = {
+                    (byte) 0x00, // invalid data
+            };
+            mWspHeader = array2;
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length;
+            mWspHeaderLen = array2.length;
+            break;
+
+            // random wsp header
+        case 3:
+            Random rd = new Random();
+            int arrSize = 150 + rd.nextInt(100);
+            byte[] array3 = new byte[arrSize];
+            int hdrEncodeLen;
+
+            array3[0] = (byte) 0x0;
+            array3[1] = (byte) 0x6;
+            hdrEncodeLen = encodeUint32(array3.length, null, 2);
+            hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+            array3[hdrEncodeLen + 2] = (byte) 0x3;
+            array3[hdrEncodeLen + 3] = (byte) 0x2;
+            array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+            array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+            createRandomWspHeader(array3, rd, hdrEncodeLen + 6, false);
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+            mWspHeaderLen = array3.length;
+
+            Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+                    + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+            mWspHeader = array3;
+            break;
+
+            // random wsp header w/o appid
+        case 4:
+            rd = new Random();
+            arrSize = 150 + rd.nextInt(100);
+            array3 = new byte[arrSize];
+
+            array3[0] = (byte) 0x0;
+            array3[1] = (byte) 0x6;
+            hdrEncodeLen = encodeUint32(array3.length, null, 2);
+            hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+            array3[hdrEncodeLen + 2] = (byte) 0x3;
+            array3[hdrEncodeLen + 3] = (byte) 0x2;
+            array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+            array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+            createRandomWspHeader(array3, rd, hdrEncodeLen + 6, true);
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+            mWspHeaderLen = array3.length;
+
+            Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+                    + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+            mWspHeader = array3;
+            break;
+
+            // random wsp header w/ random appid string
+        case 5:
+            rd = new Random();
+            arrSize = 150 + rd.nextInt(100);
+            array3 = new byte[arrSize];
+
+            array3[0] = (byte) 0x0;
+            array3[1] = (byte) 0x6;
+            hdrEncodeLen = encodeUint32(array3.length, null, 2);
+            hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+            array3[hdrEncodeLen + 2] = (byte) 0x3;
+            array3[hdrEncodeLen + 3] = (byte) 0x2;
+            array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+            array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+            createRandomWspHeaderStrAppId(array3, rd, hdrEncodeLen + 6, true);
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+            mWspHeaderLen = array3.length;
+
+            Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+                    + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+            mWspHeader = array3;
+            break;
+
+            // random wsp header w/ OMA appid string
+        case 6:
+            rd = new Random();
+            arrSize = 150 + rd.nextInt(100);
+            array3 = new byte[arrSize];
+
+            array3[0] = (byte) 0x0;
+            array3[1] = (byte) 0x6;
+            hdrEncodeLen = encodeUint32(array3.length, null, 2);
+            hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+            array3[hdrEncodeLen + 2] = (byte) 0x3;
+            array3[hdrEncodeLen + 3] = (byte) 0x2;
+            array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+            array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+            createRandomWspHeaderStrAppId(array3, rd, hdrEncodeLen + 6, false);
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+            mWspHeaderLen = array3.length;
+
+            Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+                    + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+            mWspHeader = array3;
+            break;
+
+            // random wsp header w/ OMA content type
+        case 7:
+            rd = new Random();
+            arrSize = 150 + rd.nextInt(100);
+            array3 = new byte[arrSize];
+
+            array3[0] = (byte) 0x0;
+            array3[1] = (byte) 0x6;
+            hdrEncodeLen = encodeUint32(array3.length, null, 2);
+            hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+
+            // encode content type
+            int contentLen = mContentTypeName.length();
+            int next = 2 + hdrEncodeLen;
+            mWspContentTypeStart = mGsmHeader.length + mUserDataHeader.length + next;
+            // next += encodeUint32(contentLen, array3, next);
+            int i;
+            Log.d(LOG_TAG, "mContentTypeName = " + mContentTypeName
+                    + ", contentLen = " + contentLen);
+
+            for (i = 0; i < contentLen; i++) {
+                array3[next + i] = (byte) mContentTypeName.charAt(i);
+            }
+            array3[next + i] = (byte) 0x0;
+
+            createRandomWspHeader(array3, rd, next + contentLen + 1, false);
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length
+                    + next + contentLen + 1;
+            mWspHeaderLen = array3.length;
+
+            mWspHeader = array3;
+            break;
+
+            // random wsp header w/ OMA content type, OMA app ID
+        case 8:
+            rd = new Random();
+            arrSize = 150 + rd.nextInt(100);
+            array3 = new byte[arrSize];
+
+            array3[0] = (byte) 0x0;
+            array3[1] = (byte) 0x6;
+            hdrEncodeLen = encodeUint32(array3.length, null, 2);
+            hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+
+            // encode content type
+            contentLen = mContentTypeName.length();
+            next = 2 + hdrEncodeLen;
+            mWspContentTypeStart = mGsmHeader.length + mUserDataHeader.length + next;
+            // next += encodeUint32(contentLen, array3, next);
+            Log.d(LOG_TAG, "mContentTypeName = " + mContentTypeName
+                    + ", contentLen = " + contentLen);
+
+            for (i = 0; i < contentLen; i++) {
+                array3[next + i] = (byte) mContentTypeName.charAt(i);
+            }
+            array3[next + i] = (byte) 0x0;
+
+            createRandomWspHeaderStrAppId(array3, rd, next + contentLen + 1, false);
+            mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length
+                    + next + contentLen + 1;
+            mWspHeaderLen = array3.length;
+
+            mWspHeader = array3;
+            break;
+
+        default:
+            return null;
+        }
+        array = new byte[mGsmHeader.length + mUserDataHeader.length + mWspHeader.length
+                + mMessageBody.length];
+        System.arraycopy(mGsmHeader, 0, array, 0, mGsmHeader.length);
+        System.arraycopy(mUserDataHeader, 0, array,
+                mGsmHeader.length, mUserDataHeader.length);
+        System.arraycopy(mWspHeader, 0, array,
+                mGsmHeader.length + mUserDataHeader.length, mWspHeader.length);
+        System.arraycopy(mMessageBody, 0, array,
+                mGsmHeader.length + mUserDataHeader.length + mWspHeader.length, 
+                mMessageBody.length);
+        return array;
+
+    }
+
+    Intent createIntent(int pduType, int tranId) {
+        Intent intent = new Intent();
+        intent.putExtra("transactionId", tranId);
+        intent.putExtra("pduType", pduType);
+        intent.putExtra("header", mGsmHeader);
+        intent.putExtra("data", mMessageBody);
+        // intent.putExtra("contentTypeParameters", null);
+        return intent;
+    }
+
+    /**
+     * Message processing test, start activity
+     */
+    public void testProcessMsg1() {
+        byte[] pdu = createPDU(1);
+        int headerLen = pdu.length -
+                (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+        int pduType = 6;
+        int tranId = 0;
+        String originalPackageName = mPackageName;
+        String originalClassName = mClassName;
+
+        try {
+
+            mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+
+            // set up data
+            IWapPushManager iwapman = getInterface();
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+            assertTrue((iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId))
+                    & WapPushManagerParams.MESSAGE_HANDLED) ==
+                    WapPushManagerParams.MESSAGE_HANDLED);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mPackageName = originalPackageName;
+        mClassName = originalClassName;
+    }
+
+    /**
+     * Message processing test, start service
+     */
+    public void testProcessMsg2() {
+        byte[] pdu = createPDU(1);
+        int headerLen = pdu.length - (mGsmHeader.length +
+                mUserDataHeader.length + mMessageBody.length);
+        int pduType = 6;
+        int tranId = 0;
+        String originalPackageName = mPackageName;
+        String originalClassName = mClassName;
+
+        try {
+
+            mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+            // set up data
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+            assertTrue((iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId))
+                    & WapPushManagerParams.MESSAGE_HANDLED) ==
+                    WapPushManagerParams.MESSAGE_HANDLED);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mPackageName = originalPackageName;
+        mClassName = originalClassName;
+    }
+
+    /**
+     * Message processing test, no signature
+     */
+    public void testProcessMsg3() {
+        byte[] pdu = createPDU(1);
+        int headerLen = pdu.length -
+                (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+        int pduType = 6;
+        int tranId = 0;
+        String originalPackageName = mPackageName;
+        String originalClassName = mClassName;
+
+        try {
+
+            mPackageName = "com.android.development";
+            mClassName = "com.android.development.Development";
+
+            // set up data
+            IWapPushManager iwapman = getInterface();
+
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_SERVICE, true, false);
+
+            assertFalse((iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId))
+                    & WapPushManagerParams.MESSAGE_HANDLED) ==
+                    WapPushManagerParams.MESSAGE_HANDLED);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mPackageName = originalPackageName;
+        mClassName = originalClassName;
+    }
+
+    IDataVerify getVerifyInterface() {
+        while (mIVerify == null) {
+            // wait for the activity receive data.
+            try {
+                Thread.sleep(TIME_WAIT);
+            } catch (InterruptedException e) {}
+        }
+        return mIVerify;
+    }
+
+
+    /**
+     * Message processing test, received body data verification test
+     */
+    public void testProcessMsg4() {
+        byte[] originalMessageBody = mMessageBody;
+        mMessageBody = new byte[] {
+                (byte) 0xee,
+                (byte) 0xff,
+                (byte) 0xee,
+                (byte) 0xff,
+                (byte) 0xee,
+                (byte) 0xff,
+                (byte) 0xee,
+                (byte) 0xff,
+                (byte) 0xee,
+                (byte) 0xff,
+                (byte) 0xee,
+                (byte) 0xff,
+        };
+
+        byte[] pdu = createPDU(1);
+        int headerLen = pdu.length -
+                (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+        int pduType = 6;
+        int tranId = 0;
+        String originalPackageName = mPackageName;
+        String originalClassName = mClassName;
+
+        try {
+            IWapPushManager iwapman = getInterface();
+            IDataVerify dataverify = getVerifyInterface();
+
+            dataverify.resetData();
+
+            // set up data
+            mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+            iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId));
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+            assertTrue(dataverify.verifyData(mMessageBody));
+
+            // set up data
+            dataverify.resetData();
+            mClassName = "com.android.smspush.unitTests.ReceiverService";
+            mMessageBody = new byte[] {
+                    (byte) 0xaa,
+                    (byte) 0xbb,
+                    (byte) 0x11,
+                    (byte) 0x22,
+                    (byte) 0xaa,
+                    (byte) 0xbb,
+                    (byte) 0x11,
+                    (byte) 0x22,
+                    (byte) 0xaa,
+                    (byte) 0xbb,
+                    (byte) 0x11,
+                    (byte) 0x22,
+                    (byte) 0xaa,
+                    (byte) 0xbb,
+                    (byte) 0x11,
+                    (byte) 0x22,
+                    (byte) 0xaa,
+                    (byte) 0xbb,
+                    (byte) 0x11,
+                    (byte) 0x22,
+                    (byte) 0xaa,
+                    (byte) 0xbb,
+                    (byte) 0x11,
+                    (byte) 0x22,
+            };
+            pdu = createPDU(1);
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+            iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId));
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+            // Log.d(LOG_TAG, HexDump.dumpHexString(mMessageBody));
+            assertTrue(dataverify.verifyData(mMessageBody));
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mPackageName = originalPackageName;
+        mClassName = originalClassName;
+        mMessageBody = originalMessageBody;
+    }
+
+    /**
+     * Message processing test, send invalid sms data
+     */
+    public void testProcessMsg5() {
+        byte[] pdu = createPDU(2);
+        int headerLen = pdu.length -
+                (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+        int pduType = 6;
+        int tranId = 0;
+        String originalPackageName = mPackageName;
+        String originalClassName = mClassName;
+
+        try {
+
+            mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+
+            // set up data
+            IWapPushManager iwapman = getInterface();
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+            assertTrue((iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId))
+                    & WapPushManagerParams.MESSAGE_HANDLED) ==
+                    WapPushManagerParams.MESSAGE_HANDLED);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mPackageName = originalPackageName;
+        mClassName = originalClassName;
+    }
+
+    /**
+     * Message processing test, no receiver application
+     */
+    public void testProcessMsg6() {
+        byte[] pdu = createPDU(1);
+        int headerLen = pdu.length -
+                (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+        int pduType = 6;
+        int tranId = 0;
+        String originalPackageName = mPackageName;
+        String originalClassName = mClassName;
+
+        try {
+
+            mClassName = "com.android.smspush.unitTests.NoReceiver";
+
+            // set up data
+            IWapPushManager iwapman = getInterface();
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+            assertFalse((iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId))
+                    & WapPushManagerParams.MESSAGE_HANDLED) ==
+                    WapPushManagerParams.MESSAGE_HANDLED);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+            // set up data
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+            assertFalse((iwapman.processMessage(
+                    Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue),
+                    createIntent(pduType, tranId))
+                    & WapPushManagerParams.MESSAGE_HANDLED) ==
+                    WapPushManagerParams.MESSAGE_HANDLED);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+
+        mPackageName = originalPackageName;
+        mClassName = originalClassName;
+    }
+
+    /**
+     * WspTypeDecoder test, normal pdu
+     */
+    public void testDecoder1() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        Random rd = new Random();
+
+        for (int i = 0; i < 10; i++) {
+            mAppIdValue = rd.nextInt(0xFFFF);
+            byte[] pdu = createPDU(1);
+            WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+            res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+                    mWspHeaderStart + mWspHeaderLen - 1);
+            assertTrue(res);
+
+            int index = (int) pduDecoder.getValue32();
+            res = pduDecoder.decodeXWapApplicationId(index);
+            assertTrue(res);
+
+            Log.d(LOG_TAG, "mAppIdValue: " + mAppIdValue
+                    + ", val: " + pduDecoder.getValue32());
+            assertTrue(mAppIdValue == (int) pduDecoder.getValue32());
+        }
+
+        mAppIdValue = originalAppIdValue;
+    }
+
+    /**
+     * WspTypeDecoder test, no header
+     */
+    public void testDecoder2() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        Random rd = new Random();
+
+        mAppIdValue = rd.nextInt(0xFFFF);
+        byte[] pdu = createPDU(2);
+        WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+        res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+                mWspHeaderStart + mWspHeaderLen - 1);
+        assertFalse(res);
+
+        mAppIdValue = originalAppIdValue;
+    }
+
+    /**
+     * WspTypeDecoder test, decode appid test
+     */
+    public void testDecoder3() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        Random rd = new Random();
+
+        for (int i = 0; i < 100; i++) {
+            mAppIdValue = rd.nextInt(0x0FFFFFFF);
+            mContentTypeValue = rd.nextInt(0x0FFF);
+            byte[] pdu = createPDU(3);
+            WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+            res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+                    mWspHeaderStart + mWspHeaderLen - 1);
+            assertTrue(res);
+
+            int index = (int) pduDecoder.getValue32();
+            res = pduDecoder.decodeXWapApplicationId(index);
+            assertTrue(res);
+
+            Log.d(LOG_TAG, "mAppIdValue: " + mAppIdValue
+                    + ", val: " + pduDecoder.getValue32());
+            assertTrue(mAppIdValue == (int) pduDecoder.getValue32());
+        }
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /*
+      public void testEnc() {
+      byte[] arr = new byte[20];
+      int index = 0;
+      index += encodeUint32(0x87a5, arr, index);
+      index += encodeUint32(0x1, arr, index);
+      index += encodeUint32(0x9b, arr, index);
+      index += encodeUint32(0x10, arr, index);
+      index += encodeUint32(0xe0887, arr, index);
+      index += encodeUint32(0x791a23d0, arr, index);
+
+      Log.d(LOG_TAG, HexDump.dumpHexString(arr));
+      }
+    */
+
+    /**
+     * WspTypeDecoder test, no appid test
+     */
+    public void testDecoder4() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        Random rd = new Random();
+
+        for (int i = 0; i < 100; i++) {
+            mAppIdValue = rd.nextInt(0x0FFFFFFF);
+            mContentTypeValue = rd.nextInt(0x0FFF);
+            byte[] pdu = createPDU(4);
+            WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+            res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+                    mWspHeaderStart + mWspHeaderLen - 1);
+            assertFalse(res);
+
+        }
+
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /**
+     * WspTypeDecoder test, decode string appid test
+     */
+    public void testDecoder5() {
+        boolean res;
+        String originalAppIdName = mAppIdName;
+        int originalContentTypeValue  = mContentTypeValue;
+        Random rd = new Random();
+
+        for (int i = 0; i < 10; i++) {
+            mAppIdValue = rd.nextInt(0x0FFFFFFF);
+            mContentTypeValue = rd.nextInt(0x0FFF);
+            byte[] pdu = createPDU(5);
+            WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+            res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+                    mWspHeaderStart + mWspHeaderLen - 1);
+            assertTrue(res);
+
+            int index = (int) pduDecoder.getValue32();
+            res = pduDecoder.decodeXWapApplicationId(index);
+            assertTrue(res);
+
+            Log.d(LOG_TAG, "mAppIdValue: [" + mAppIdName + "], val: ["
+                    + pduDecoder.getValueString() + "]");
+            assertTrue(mAppIdName.equals(pduDecoder.getValueString()));
+        }
+
+        mAppIdName = originalAppIdName;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /**
+     * WspTypeDecoder test, decode string appid test
+     */
+    public void testDecoder6() {
+        boolean res;
+        String originalAppIdName = mAppIdName;
+        int originalContentTypeValue  = mContentTypeValue;
+        Random rd = new Random();
+
+        for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length; i++) {
+            mAppIdName = OMA_APPLICATION_ID_NAMES[i];
+            mContentTypeValue = rd.nextInt(0x0FFF);
+            byte[] pdu = createPDU(6);
+            WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+            res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+                    mWspHeaderStart + mWspHeaderLen - 1);
+            assertTrue(res);
+
+            int index = (int) pduDecoder.getValue32();
+            res = pduDecoder.decodeXWapApplicationId(index);
+            assertTrue(res);
+
+            Log.d(LOG_TAG, "mAppIdValue: [" + mAppIdName + "], val: ["
+                    + pduDecoder.getValueString() + "]");
+            assertTrue(mAppIdName.equals(pduDecoder.getValueString()));
+        }
+
+        mAppIdName = originalAppIdName;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+    /**
+     * WspTypeDecoder test, decode OMA content type
+     */
+    public void testDecoder7() {
+        boolean res;
+        String originalAppIdName = mAppIdName;
+        int originalContentTypeValue  = mContentTypeValue;
+        Random rd = new Random();
+
+        for (int i = 0; i < OMA_CONTENT_TYPE_NAMES.length; i++) {
+            mContentTypeName = OMA_CONTENT_TYPE_NAMES[i];
+            byte[] pdu = createPDU(7);
+            WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+            res = pduDecoder.decodeContentType(mWspContentTypeStart);
+            assertTrue(res);
+
+            Log.d(LOG_TAG, "mContentTypeName: [" + mContentTypeName + "], val: ["
+                    + pduDecoder.getValueString() + "]");
+            assertTrue(mContentTypeName.equals(pduDecoder.getValueString()));
+        }
+
+        mAppIdName = originalAppIdName;
+        mContentTypeValue = originalContentTypeValue;
+    }
+
+
+    /**
+     * Copied from WapPushOverSms.
+     * The code flow is not changed from the original.
+     */
+    public int dispatchWapPdu(byte[] pdu, IWapPushManager wapPushMan) {
+
+        if (Config.DEBUG) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
+
+        int index = 0;
+        int transactionId = pdu[index++] & 0xFF;
+        int pduType = pdu[index++] & 0xFF;
+        int headerLength = 0;
+
+        if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
+                (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
+            if (Config.DEBUG) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
+            return Intents.RESULT_SMS_HANDLED;
+        }
+
+        WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+        /**
+         * Parse HeaderLen(unsigned integer).
+         * From wap-230-wsp-20010705-a section 8.1.2
+         * The maximum size of a uintvar is 32 bits.
+         * So it will be encoded in no more than 5 octets.
+         */
+        if (pduDecoder.decodeUintvarInteger(index) == false) {
+            if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Length error.");
+            return Intents.RESULT_SMS_GENERIC_ERROR;
+        }
+        headerLength = (int) pduDecoder.getValue32();
+        index += pduDecoder.getDecodedDataLength();
+
+        int headerStartIndex = index;
+
+        /**
+         * Parse Content-Type.
+         * From wap-230-wsp-20010705-a section 8.4.2.24
+         *
+         * Content-type-value = Constrained-media | Content-general-form
+         * Content-general-form = Value-length Media-type
+         * Media-type = (Well-known-media | Extension-Media) *(Parameter)
+         * Value-length = Short-length | (Length-quote Length)
+         * Short-length = <Any octet 0-30>   (octet <= WAP_PDU_SHORT_LENGTH_MAX)
+         * Length-quote = <Octet 31>         (WAP_PDU_LENGTH_QUOTE)
+         * Length = Uintvar-integer
+         */
+        if (pduDecoder.decodeContentType(index) == false) {
+            if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
+            return Intents.RESULT_SMS_GENERIC_ERROR;
+        }
+
+        String mimeType = pduDecoder.getValueString();
+        long binaryContentType = pduDecoder.getValue32();
+        index += pduDecoder.getDecodedDataLength();
+
+        byte[] header = new byte[headerLength];
+        System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
+
+        byte[] intentData;
+
+        if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+            intentData = pdu;
+        } else {
+            int dataIndex = headerStartIndex + headerLength;
+            intentData = new byte[pdu.length - dataIndex];
+            System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
+        }
+
+        /**
+         * Seek for application ID field in WSP header.
+         * If application ID is found, WapPushManager substitute the message
+         * processing. Since WapPushManager is optional module, if WapPushManager
+         * is not found, legacy message processing will be continued.
+         */
+        if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) {
+            index = (int) pduDecoder.getValue32();
+            pduDecoder.decodeXWapApplicationId(index);
+            String wapAppId = pduDecoder.getValueString();
+            if (wapAppId == null) {
+                wapAppId = Integer.toString((int) pduDecoder.getValue32());
+            }
+
+            String contentType = ((mimeType == null) ?
+                    Long.toString(binaryContentType) : mimeType);
+            if (Config.DEBUG) Log.v(LOG_TAG, "appid found: " + wapAppId + ":" + contentType);
+
+            try {
+                boolean processFurther = true;
+                // IWapPushManager wapPushMan = mWapConn.getWapPushManager();
+                if (wapPushMan == null) {
+                    if (Config.DEBUG) Log.w(LOG_TAG, "wap push manager not found!");
+                } else {
+                    Intent intent = new Intent();
+                    intent.putExtra("transactionId", transactionId);
+                    intent.putExtra("pduType", pduType);
+                    intent.putExtra("header", header);
+                    intent.putExtra("data", intentData);
+                    intent.putExtra("contentTypeParameters",
+                            pduDecoder.getContentParameters());
+
+                    int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
+                    if (Config.DEBUG) Log.v(LOG_TAG, "procRet:" + procRet);
+                    if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0
+                            && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) {
+                        processFurther = false;
+                    }
+                }
+                if (!processFurther) {
+                    return Intents.RESULT_SMS_HANDLED;
+                }
+            } catch (RemoteException e) {
+                if (Config.DEBUG) Log.w(LOG_TAG, "remote func failed...");
+            }
+        }
+        if (Config.DEBUG) Log.v(LOG_TAG, "fall back to existing handler");
+
+        return Activity.RESULT_OK;
+    }
+
+    protected byte[] retrieveWspBody() {
+        byte[] array = new byte[mWspHeader.length + mMessageBody.length];
+
+        System.arraycopy(mWspHeader, 0, array, 0, mWspHeader.length);
+        System.arraycopy(mMessageBody, 0, array, mWspHeader.length, mMessageBody.length);
+        return array;
+    }
+
+    protected String getContentTypeName(int ctypeVal) {
+        int i;
+
+        for (i = 0; i < OMA_CONTENT_TYPE_VALUES.length; i++) {
+            if (ctypeVal == OMA_CONTENT_TYPE_VALUES[i]) {
+                return OMA_CONTENT_TYPE_NAMES[i];
+            }
+        }
+        return null;
+    }
+
+    protected boolean isContentTypeMapped(int ctypeVal) {
+        int i;
+
+        for (i = 0; i < OMA_CONTENT_TYPE_VALUES.length; i++) {
+            if (ctypeVal == OMA_CONTENT_TYPE_VALUES[i]) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Integration test 1, simple case
+     */
+    public void testIntegration1() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        String originalAppIdName = mAppIdName;
+        String originalContentTypeName = mContentTypeName;
+        String originalClassName = mClassName;
+        byte[] originalMessageBody = mMessageBody;
+        Random rd = new Random();
+
+        mMessageBody = new byte[100 + rd.nextInt(100)];
+        rd.nextBytes(mMessageBody);
+
+        byte[] pdu = createPDU(1);
+        byte[] wappushPdu = retrieveWspBody();
+
+
+        mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+        // Phone dummy = new DummyPhone(getContext());
+        // Phone gsm = PhoneFactory.getGsmPhone();
+        // GSMPhone gsm = new GSMPhone(getContext(), new SimulatedCommands(), null, true);
+        // WapPushOverSms dispatcher = new WapPushOverSms(dummy, null);
+
+        try {
+            // set up data
+            IWapPushManager iwapman = getInterface();
+            IDataVerify dataverify = getVerifyInterface();
+
+            dataverify.resetData();
+
+            if (isContentTypeMapped(mContentTypeValue)) {
+                // content type is mapped
+                mContentTypeName = getContentTypeName(mContentTypeValue);
+                Log.d(LOG_TAG, "mContentTypeValue mapping "
+                        + mContentTypeName + ":" + mContentTypeValue);
+            } else {
+                mContentTypeName = Integer.toString(mContentTypeValue);
+            }
+            iwapman.addPackage(Integer.toString(mAppIdValue),
+                    mContentTypeName, mPackageName, mClassName,
+                    WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+            dispatchWapPdu(wappushPdu, iwapman);
+
+            // clean up data
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    mContentTypeName, mPackageName, mClassName);
+
+            assertTrue(dataverify.verifyData(mMessageBody));
+        } catch (RemoteException e) {
+        }
+
+
+        mClassName = originalClassName;
+        mAppIdName = originalAppIdName;
+        mContentTypeName = originalContentTypeName;
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        mMessageBody = originalMessageBody;
+    }
+
+    /**
+     * Integration test 2, random mAppIdValue(int), all OMA content type
+     */
+    public void testIntegration2() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        String originalAppIdName = mAppIdName;
+        String originalContentTypeName = mContentTypeName;
+        String originalClassName = mClassName;
+        byte[] originalMessageBody = mMessageBody;
+        Random rd = new Random();
+
+        IWapPushManager iwapman = getInterface();
+        IDataVerify dataverify = getVerifyInterface();
+        mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+
+        for (int i = 0; i < OMA_CONTENT_TYPE_NAMES.length; i++) {
+            mContentTypeName = OMA_CONTENT_TYPE_NAMES[i];
+            mAppIdValue = rd.nextInt(0x0FFFFFFF);
+
+            mMessageBody = new byte[100 + rd.nextInt(100)];
+            rd.nextBytes(mMessageBody);
+
+            byte[] pdu = createPDU(7);
+            byte[] wappushPdu = retrieveWspBody();
+
+            try {
+                dataverify.resetData();
+                // set up data
+                iwapman.addPackage(Integer.toString(mAppIdValue),
+                        mContentTypeName, mPackageName, mClassName,
+                        WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+                dispatchWapPdu(wappushPdu, iwapman);
+
+                // clean up data
+                iwapman.deletePackage(Integer.toString(mAppIdValue),
+                        mContentTypeName, mPackageName, mClassName);
+
+                if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+                    assertTrue(dataverify.verifyData(wappushPdu));
+                } else {
+                    assertTrue(dataverify.verifyData(mMessageBody));
+                }
+            } catch (RemoteException e) {
+            }
+        }
+
+
+        mClassName = originalClassName;
+        mAppIdName = originalAppIdName;
+        mContentTypeName = originalContentTypeName;
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        mMessageBody = originalMessageBody;
+    }
+
+    /**
+     * Integration test 3, iterate OmaApplication ID, random binary content type
+     */
+    public void testIntegration3() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        String originalAppIdName = mAppIdName;
+        String originalContentTypeName = mContentTypeName;
+        String originalClassName = mClassName;
+        byte[] originalMessageBody = mMessageBody;
+        Random rd = new Random();
+
+        IWapPushManager iwapman = getInterface();
+        IDataVerify dataverify = getVerifyInterface();
+        mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+        for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length; i++) {
+            mAppIdName = OMA_APPLICATION_ID_NAMES[i];
+            mContentTypeValue = rd.nextInt(0x0FFF);
+
+            mMessageBody = new byte[100 + rd.nextInt(100)];
+            rd.nextBytes(mMessageBody);
+
+            byte[] pdu = createPDU(6);
+            byte[] wappushPdu = retrieveWspBody();
+
+            try {
+                dataverify.resetData();
+                // set up data
+                if (isContentTypeMapped(mContentTypeValue)) {
+                    // content type is mapped to integer value
+                    mContentTypeName = getContentTypeName(mContentTypeValue);
+                    Log.d(LOG_TAG, "mContentTypeValue mapping "
+                            + mContentTypeValue + ":" + mContentTypeName);
+                } else {
+                    mContentTypeName = Integer.toString(mContentTypeValue);
+                }
+
+                iwapman.addPackage(mAppIdName,
+                        mContentTypeName, mPackageName, mClassName,
+                        WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+                dispatchWapPdu(wappushPdu, iwapman);
+
+                // clean up data
+                iwapman.deletePackage(mAppIdName,
+                        mContentTypeName, mPackageName, mClassName);
+
+                if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+                    assertTrue(dataverify.verifyData(wappushPdu));
+                } else {
+                    assertTrue(dataverify.verifyData(mMessageBody));
+                }
+            } catch (RemoteException e) {
+            }
+        }
+
+        mClassName = originalClassName;
+        mAppIdName = originalAppIdName;
+        mContentTypeName = originalContentTypeName;
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        mMessageBody = originalMessageBody;
+    }
+
+    /**
+     * Integration test 4, iterate OmaApplication ID, Oma content type
+     */
+    public void testIntegration4() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        String originalAppIdName = mAppIdName;
+        String originalContentTypeName = mContentTypeName;
+        String originalClassName = mClassName;
+        byte[] originalMessageBody = mMessageBody;
+        Random rd = new Random();
+
+        IWapPushManager iwapman = getInterface();
+        IDataVerify dataverify = getVerifyInterface();
+        mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+        for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length
+                + OMA_CONTENT_TYPE_NAMES.length; i++) {
+            mAppIdName = OMA_APPLICATION_ID_NAMES[rd.nextInt(OMA_APPLICATION_ID_NAMES.length)];
+            int contIndex = rd.nextInt(OMA_CONTENT_TYPE_NAMES.length);
+            mContentTypeName = OMA_CONTENT_TYPE_NAMES[contIndex];
+
+            mMessageBody = new byte[100 + rd.nextInt(100)];
+            rd.nextBytes(mMessageBody);
+
+            byte[] pdu = createPDU(8);
+            byte[] wappushPdu = retrieveWspBody();
+
+            try {
+                dataverify.resetData();
+                // set up data
+                iwapman.addPackage(mAppIdName,
+                        mContentTypeName, mPackageName, mClassName,
+                        WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+                dispatchWapPdu(wappushPdu, iwapman);
+
+                // clean up data
+                iwapman.deletePackage(mAppIdName,
+                        mContentTypeName, mPackageName, mClassName);
+
+                if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+                    assertTrue(dataverify.verifyData(wappushPdu));
+                } else {
+                    assertTrue(dataverify.verifyData(mMessageBody));
+                }
+            } catch (RemoteException e) {
+            }
+        }
+
+        mClassName = originalClassName;
+        mAppIdName = originalAppIdName;
+        mContentTypeName = originalContentTypeName;
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        mMessageBody = originalMessageBody;
+    }
+
+    /**
+     * Integration test 5, iterate binary OmaApplication ID, Oma binary content type
+     */
+    public void testIntegration5() {
+        boolean res;
+        int originalAppIdValue = mAppIdValue;
+        int originalContentTypeValue  = mContentTypeValue;
+        String originalAppIdName = mAppIdName;
+        String originalContentTypeName = mContentTypeName;
+        String originalClassName = mClassName;
+        byte[] originalMessageBody = mMessageBody;
+        Random rd = new Random();
+
+        IWapPushManager iwapman = getInterface();
+        IDataVerify dataverify = getVerifyInterface();
+        mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+        for (int i = 0; i < OMA_APPLICATION_ID_VALUES.length +
+                    OMA_CONTENT_TYPE_VALUES.length; i++) {
+            mAppIdValue = OMA_APPLICATION_ID_VALUES[rd.nextInt(
+                    OMA_APPLICATION_ID_VALUES.length)];
+            mContentTypeValue =
+                    OMA_CONTENT_TYPE_VALUES[rd.nextInt(OMA_CONTENT_TYPE_VALUES.length)];
+
+            mMessageBody = new byte[100 + rd.nextInt(100)];
+            rd.nextBytes(mMessageBody);
+
+            byte[] pdu = createPDU(3);
+            byte[] wappushPdu = retrieveWspBody();
+
+            try {
+                dataverify.resetData();
+                // set up data
+                if (isContentTypeMapped(mContentTypeValue)) {
+                    // content type is mapped to integer value
+                    mContentTypeName = getContentTypeName(mContentTypeValue);
+                    Log.d(LOG_TAG, "mContentTypeValue mapping "
+                            + mContentTypeValue + ":" + mContentTypeName);
+                } else {
+                    mContentTypeName = Integer.toString(mContentTypeValue);
+                }
+
+                iwapman.addPackage(Integer.toString(mAppIdValue),
+                        mContentTypeName, mPackageName, mClassName,
+                        WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+                dispatchWapPdu(wappushPdu, iwapman);
+
+                // clean up data
+                iwapman.deletePackage(Integer.toString(mAppIdValue),
+                        mContentTypeName, mPackageName, mClassName);
+
+                if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+                    assertTrue(dataverify.verifyData(wappushPdu));
+                } else {
+                    assertTrue(dataverify.verifyData(mMessageBody));
+                }
+            } catch (RemoteException e) {
+            }
+        }
+
+        mClassName = originalClassName;
+        mAppIdName = originalAppIdName;
+        mContentTypeName = originalContentTypeName;
+        mAppIdValue = originalAppIdValue;
+        mContentTypeValue = originalContentTypeValue;
+        mMessageBody = originalMessageBody;
+    }
+
+}
diff --git a/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 cf87a9d..88d94c2 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/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index f3b5f4b..9ee71e8 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -8023,14 +8023,6 @@
             sharedUser = orig.sharedUser;
         }
 
-        public void copyFrom(PackageSetting base) {
-            super.copyFrom((PackageSettingBase) base);
-
-            userId = base.userId;
-            sharedUser = base.sharedUser;
-            pkg = base.pkg;
-        }
-
         @Override
         public String toString() {
             return "PackageSetting{"
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index a3c3b86..2613988 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -928,9 +928,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 254a19b..c24fe8e 100755
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -4504,12 +4504,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/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index fd3f0c2..40882d8 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2470,7 +2470,7 @@
             }
             break;
         }
-        if (++name > 31)
+        if (++name >= SharedBufferStack::NUM_LAYERS_MAX)
             name = NO_MEMORY;
     } while(name >= 0);
 
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 5e4c136..259acdb 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -1289,18 +1289,25 @@
     void setCdmaSubscription(int cdmaSubscriptionType, Message response);
 
     /**
-     *  Set the TTY mode for the CDMA phone
+     *  Set the TTY mode
      *
-     * @param enable is true to enable, false to disable
+     * @param ttyMode one of the following:
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
      * @param response is callback message
      */
     void setTTYMode(int ttyMode, Message response);
 
     /**
-     *  Query the TTY mode for the CDMA phone
+     *  Query the TTY mode
      * (AsyncResult)response.obj).result is an int[] with element [0] set to
-     * 0 for disabled, 1 for enabled.
-     *
+     * tty mode:
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
      * @param response is callback message
      */
     void queryTTYMode(Message response);
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/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index d0b5540..ef9e783 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -1430,9 +1430,11 @@
     /**
      * setTTYMode
      * sets a TTY mode option.
-     *
-     * @param ttyMode is a boolean representing the state that you are
-     *        requesting, true for enabled, false for disabled.
+     * @param ttyMode is a one of the following:
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
      * @param onComplete a callback message when the action is completed
      */
     void setTTYMode(int ttyMode, Message onComplete);
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 5887130..cbff130 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -702,13 +702,11 @@
     }
 
     public void setTTYMode(int ttyMode, Message onComplete) {
-        // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        logUnexpectedCdmaMethodCall("setTTYMode");
+        mCM.setTTYMode(ttyMode, onComplete);
     }
 
     public void queryTTYMode(Message onComplete) {
-        // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        logUnexpectedCdmaMethodCall("queryTTYMode");
+        mCM.queryTTYMode(onComplete);
     }
 
     public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) {
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 99123af..befee8c7
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -66,6 +66,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;
@@ -162,6 +163,8 @@
     protected boolean mSmsReceiveDisabled;
     protected boolean mSmsSendDisabled;
 
+    protected static int mRemainingMessages = -1;
+
     protected static int getNextConcatenatedRef() {
         sConcatenatedRef += 1;
         return sConcatenatedRef;
@@ -409,6 +412,7 @@
                 mCm.reportSmsMemoryStatus(mStorageAvailable,
                         obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
             }
+            break;
 
         case EVENT_NEW_BROADCAST_SMS:
             handleBroadcastSms((AsyncResult)msg.obj);
@@ -486,7 +490,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 {
@@ -525,8 +539,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/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index a1d5121..f5aaea2 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -725,7 +725,14 @@
         String number = null;
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         // TODO: The default value of voicemail number should be read from a system property
-        number = sp.getString(VM_NUMBER_CDMA, "*86");
+
+        // Read platform settings for dynamic voicemail number
+        if (getContext().getResources().getBoolean(com.android.internal
+                .R.bool.config_telephony_use_own_number_for_voicemail)) {
+            number = sp.getString(VM_NUMBER_CDMA, getLine1Number());
+        } else {
+            number = sp.getString(VM_NUMBER_CDMA, "*86");
+        }
         return number;
     }
 
@@ -1103,20 +1110,6 @@
     }
 
     /**
-     * Set the TTY mode of the CDMAPhone
-     */
-    public void setTTYMode(int ttyMode, Message onComplete) {
-        this.mCM.setTTYMode(ttyMode, onComplete);
-    }
-
-    /**
-     * Queries the TTY mode of the CDMAPhone
-     */
-    public void queryTTYMode(Message onComplete) {
-        this.mCM.queryTTYMode(onComplete);
-    }
-
-    /**
      * Activate or deactivate cell broadcast SMS.
      *
      * @param activate 0 = activate, 1 = deactivate
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index fe7a5cb..2962e0f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -155,7 +155,7 @@
     static final int MATCH_GROUP_SIC = 9;
     static final int MATCH_GROUP_PWD_CONFIRM = 11;
     static final int MATCH_GROUP_DIALING_NUMBER = 12;
-
+    static private String[] sTwoDigitNumberPattern;
 
     //***** Public Class methods
 
@@ -198,6 +198,9 @@
 
             ret = new GsmMmiCode(phone);
             ret.poundString = dialString;
+        } else if (isTwoDigitShortCode(phone.getContext(), dialString)) {
+            //Is a country-specific exception to short codes as defined in TS 22.030, 6.5.3.2
+            ret = null;
         } else if (isShortCode(dialString, phone)) {
             // this may be a short code, as defined in TS 22.030, 6.5.3.2
             ret = new GsmMmiCode(phone);
@@ -452,6 +455,28 @@
 
     }
 
+    static private boolean
+    isTwoDigitShortCode(Context context, String dialString) {
+        Log.d(LOG_TAG, "isTwoDigitShortCode");
+
+        if (dialString == null || dialString.length() != 2) return false;
+
+        if (sTwoDigitNumberPattern == null) {
+            sTwoDigitNumberPattern = context.getResources().getStringArray(
+                    com.android.internal.R.array.config_twoDigitNumberPattern);
+        }
+
+        for (String dialnumber : sTwoDigitNumberPattern) {
+            Log.d(LOG_TAG, "Two Digit Number Pattern " + dialnumber);
+            if (dialString.equals(dialnumber)) {
+                Log.d(LOG_TAG, "Two Digit Number Pattern -true");
+                return true;
+            }
+        }
+        Log.d(LOG_TAG, "Two Digit Number Pattern -false");
+        return false;
+    }
+
     /**
      * Helper function for newFromDialString. Returns true if dialString appears
      * to be a short code AND conditions are correct for it to be treated as
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 bbe579d..f576b4e
--- 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;
@@ -69,13 +70,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));
@@ -195,6 +200,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
@@ -284,6 +291,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/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
index 41f3b23..a9efc98 100755
--- a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
@@ -305,8 +305,15 @@
         fileIds = mPbrFile.mFileIds.get(recNum);
         if (fileIds == null || fileIds.isEmpty()) return;
 
+
+        int extEf = 0;
+        // Only call fileIds.get while EFEXT1_TAG is available
+        if (fileIds.containsKey(USIM_EFEXT1_TAG)) {
+            extEf = fileIds.get(USIM_EFEXT1_TAG);
+        }
+
         mAdnCache.requestLoadAllAdnLike(fileIds.get(USIM_EFADN_TAG),
-            fileIds.get(USIM_EFEXT1_TAG), obtainMessage(EVENT_USIM_ADN_LOAD_DONE));
+            extEf, obtainMessage(EVENT_USIM_ADN_LOAD_DONE));
         try {
             mLock.wait();
         } catch (InterruptedException e) {
diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
index 37867b2..8b3a3ad 100644
--- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -1392,23 +1392,28 @@
     }
 
     /**
-     *  Set the TTY mode for the CDMA phone
+     *  Set the TTY mode
      *
-     * @param enable is true to enable, false to disable
-     * @param serviceClass is a sum of SERVICE_CLASS_*
+     * @param ttyMode is one of the following:
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
      * @param response is callback message
      */
     public void setTTYMode(int ttyMode, Message response) {
-        Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands");
+        Log.w(LOG_TAG, "Not implemented in SimulatedCommands");
         unimplemented(response);
     }
 
     /**
-     *  Query the TTY mode for the CDMA phone
+     *  Query the TTY mode
      * (AsyncResult)response.obj).result is an int[] with element [0] set to
-     * 0 for disabled, 1 for enabled.
-     *
-     * @param serviceClass is a sum of SERVICE_CLASS_*
+     * tty mode:
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
      * @param response is callback message
      */
     public void queryTTYMode(Message response) {
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/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 7f84df6..730bd711 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1723,7 +1723,8 @@
 
 static status_t writeSymbolClass(
     FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
-    const sp<AaptSymbols>& symbols, const String8& className, int indent)
+    const sp<AaptSymbols>& symbols, const String8& className, int indent,
+    bool nonConstantId)
 {
     fprintf(fp, "%spublic %sfinal class %s {\n",
             getIndentSpace(indent),
@@ -1733,6 +1734,10 @@
     size_t i;
     status_t err = NO_ERROR;
 
+    const char * id_format = nonConstantId ?
+            "%spublic static int %s=0x%08x;\n" :
+            "%spublic static final int %s=0x%08x;\n";
+
     size_t N = symbols->getSymbols().size();
     for (i=0; i<N; i++) {
         const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
@@ -1785,7 +1790,7 @@
         if (deprecated) {
             fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
         }
-        fprintf(fp, "%spublic static final int %s=0x%08x;\n",
+        fprintf(fp, id_format,
                 getIndentSpace(indent),
                 String8(name).string(), (int)sym.int32Val);
     }
@@ -1836,7 +1841,7 @@
         if (nclassName == "styleable") {
             styleableSymbols = nsymbols;
         } else {
-            err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent);
+            err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId);
         }
         if (err != NO_ERROR) {
             return err;
@@ -1907,7 +1912,7 @@
         "\n"
         "package %s;\n\n", package.string());
 
-        status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0);
+        status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0, bundle->getNonConstantId());
         if (err != NO_ERROR) {
             return err;
         }
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");
             }
diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath
index 2eaf8e3..5cfda29 100644
--- a/tools/layoutlib/bridge/.classpath
+++ b/tools/layoutlib/bridge/.classpath
@@ -8,6 +8,6 @@
 	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/>
 	<classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
 	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/ninepatch/ninepatch-prebuilt.jar"/>
-	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/common/common-prebuilt.jar"/>
+	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/tools-common/tools-common-prebuilt.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk
index ca7db8c..7e70f33 100644
--- a/tools/layoutlib/bridge/Android.mk
+++ b/tools/layoutlib/bridge/Android.mk
@@ -23,7 +23,7 @@
 LOCAL_JAVA_LIBRARIES := \
 	kxml2-2.3.0 \
 	layoutlib_api-prebuilt \
-	common-prebuilt
+	tools-common-prebuilt
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	temp_layoutlib \
diff --git a/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml b/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml
index 5211b0a..d3c492e 100644
--- a/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml
+++ b/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml
@@ -9,5 +9,7 @@
 			android:layout_width="wrap_content"/>
 	<ImageView
 			android:layout_height="wrap_content"
-			android:layout_width="wrap_content"/>
+			android:layout_width="wrap_content"
+			android:layout_marginLeft="3dip"
+			android:layout_marginRight="5dip"/>
 </merge>
diff --git a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
index 7d41d1c..7b444aa 100644
--- a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
@@ -17,6 +17,7 @@
 package android.animation;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.animation.PropertyValuesHolder
@@ -34,20 +35,24 @@
  */
 /*package*/ class PropertyValuesHolder_Delegate {
 
+    @LayoutlibDelegate
     /*package*/ static int nGetIntMethod(Class<?> targetClass, String methodName) {
         // return 0 to force PropertyValuesHolder to use Java reflection.
         return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nGetFloatMethod(Class<?> targetClass, String methodName) {
         // return 0 to force PropertyValuesHolder to use Java reflection.
         return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nCallIntMethod(Object target, int methodID, int arg) {
         // do nothing
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nCallFloatMethod(Object target, int methodID, float arg) {
         // do nothing
     }
diff --git a/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java b/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java
index 60ad645..aabd3f1 100644
--- a/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.content.Context;
 import android.os.Bundle;
@@ -48,6 +49,7 @@
      * Like {@link #instantiate(Context, String, Bundle)} but with a null
      * argument Bundle.
      */
+    @LayoutlibDelegate
     /*package*/ static Fragment instantiate(Context context, String fname) {
         return instantiate(context, fname, null);
     }
@@ -66,6 +68,7 @@
      * the given fragment class.  This is a runtime exception; it is not
      * normally expected to happen.
      */
+    @LayoutlibDelegate
     /*package*/ static Fragment instantiate(Context context, String fname, Bundle args) {
         try {
             if (sProjectCallback != null) {
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
index 03f3980..413894b 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
@@ -17,6 +17,7 @@
 package android.content.res;
 
 import com.android.layoutlib.bridge.impl.RenderSessionImpl;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.content.res.Resources.NotFoundException;
 import android.content.res.Resources.Theme;
@@ -32,12 +33,14 @@
  */
 public class Resources_Theme_Delegate {
 
+    @LayoutlibDelegate
     /*package*/ static TypedArray obtainStyledAttributes(
             Resources thisResources, Theme thisTheme,
             int[] attrs) {
         return RenderSessionImpl.getCurrentContext().obtainStyledAttributes(attrs);
     }
 
+    @LayoutlibDelegate
     /*package*/ static TypedArray obtainStyledAttributes(
             Resources thisResources, Theme thisTheme,
             int resid, int[] attrs)
@@ -45,6 +48,7 @@
         return RenderSessionImpl.getCurrentContext().obtainStyledAttributes(resid, attrs);
     }
 
+    @LayoutlibDelegate
     /*package*/ static TypedArray obtainStyledAttributes(
             Resources thisResources, Theme thisTheme,
             AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
@@ -52,6 +56,7 @@
                 set, attrs, defStyleAttr, defStyleRes);
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean resolveAttribute(
             Resources thisResources, Theme thisTheme,
             int resid, TypedValue outValue,
diff --git a/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java
index 190eb37..a50a2bd 100644
--- a/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import java.awt.Composite;
 
@@ -59,9 +60,10 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate(int opColor, int tolerance, int nativeMode) {
         AvoidXfermode_Delegate newDelegate = new AvoidXfermode_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
index c4fffc86..080b85f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -21,6 +21,7 @@
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.ninepatch.NinePatchChunk;
 import com.android.resources.Density;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.BitmapFactory.Options;
 
@@ -42,6 +43,7 @@
 
     // ------ Java delegates ------
 
+    @LayoutlibDelegate
     /*package*/ static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
         if (bm == null || opts == null) {
             return bm;
@@ -82,10 +84,12 @@
 
     // ------ Native Delegates ------
 
+    @LayoutlibDelegate
     /*package*/ static void nativeSetDefaultConfig(int nativeConfig) {
         // pass
     }
 
+    @LayoutlibDelegate
     /*package*/ static Bitmap nativeDecodeStream(InputStream is, byte[] storage,
             Rect padding, Options opts) {
         Bitmap bm = null;
@@ -129,29 +133,34 @@
         return bm;
     }
 
+    @LayoutlibDelegate
     /*package*/ static Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
             Rect padding, Options opts) {
         opts.inBitmap = null;
         return null;
     }
 
+    @LayoutlibDelegate
     /*package*/ static Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts) {
         opts.inBitmap = null;
         return null;
     }
 
+    @LayoutlibDelegate
     /*package*/ static Bitmap nativeDecodeByteArray(byte[] data, int offset,
             int length, Options opts) {
         opts.inBitmap = null;
         return null;
     }
 
+    @LayoutlibDelegate
     /*package*/ static byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad) {
         // don't scale for now. This should not be called anyway since we re-implement
         // BitmapFactory.finishDecode();
         return chunk;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeIsSeekable(FileDescriptor fd) {
         return true;
     }
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
index 73c5a1a..9a8cf04 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
@@ -19,6 +19,7 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.Shader.TileMode;
 
@@ -63,6 +64,7 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate(int native_bitmap, int shaderTileModeX,
             int shaderTileModeY) {
         Bitmap_Delegate bitmap = Bitmap_Delegate.getDelegate(native_bitmap);
@@ -74,9 +76,10 @@
                 bitmap.getImage(),
                 Shader_Delegate.getTileMode(shaderTileModeX),
                 Shader_Delegate.getTileMode(shaderTileModeY));
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativePostCreate(int native_shader, int native_bitmap,
             int shaderTileModeX, int shaderTileModeY) {
         // pass, not needed.
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index 3e80614..66e59d8 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -20,6 +20,7 @@
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.resources.Density;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
@@ -53,7 +54,7 @@
 
     // ---- delegate manager ----
     private static final DelegateManager<Bitmap_Delegate> sManager =
-            new DelegateManager<Bitmap_Delegate>();
+            new DelegateManager<Bitmap_Delegate>(Bitmap_Delegate.class);
 
     // ---- delegate helper data ----
 
@@ -196,6 +197,7 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
             int height, int nativeConfig, boolean mutable) {
         int imageType = getBufferedImageType(nativeConfig);
@@ -213,6 +215,7 @@
         return createBitmap(delegate, mutable, Bitmap.getDefaultDensity());
     }
 
+    @LayoutlibDelegate
     /*package*/ static Bitmap nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable) {
         Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap);
         if (srcBmpDelegate == null) {
@@ -240,14 +243,17 @@
         return createBitmap(delegate, isMutable, Bitmap.getDefaultDensity());
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeDestructor(int nativeBitmap) {
-        sManager.removeDelegate(nativeBitmap);
+        sManager.removeJavaReferenceFor(nativeBitmap);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeRecycle(int nativeBitmap) {
-        sManager.removeDelegate(nativeBitmap);
+        sManager.removeJavaReferenceFor(nativeBitmap);
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeCompress(int nativeBitmap, int format, int quality,
             OutputStream stream, byte[] tempStorage) {
         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
@@ -255,6 +261,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeErase(int nativeBitmap, int color) {
         // get the delegate from the native int.
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
@@ -274,6 +281,7 @@
         }
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativeWidth(int nativeBitmap) {
         // get the delegate from the native int.
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
@@ -284,6 +292,7 @@
         return delegate.mImage.getWidth();
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativeHeight(int nativeBitmap) {
         // get the delegate from the native int.
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
@@ -294,6 +303,7 @@
         return delegate.mImage.getHeight();
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativeRowBytes(int nativeBitmap) {
         // get the delegate from the native int.
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
@@ -304,6 +314,7 @@
         return delegate.mImage.getWidth();
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativeConfig(int nativeBitmap) {
         // get the delegate from the native int.
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
@@ -314,6 +325,7 @@
         return delegate.mConfig.nativeInt;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeHasAlpha(int nativeBitmap) {
         // get the delegate from the native int.
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
@@ -324,6 +336,7 @@
         return delegate.mHasAlpha;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativeGetPixel(int nativeBitmap, int x, int y) {
         // get the delegate from the native int.
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
@@ -334,6 +347,7 @@
         return delegate.mImage.getRGB(x, y);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeGetPixels(int nativeBitmap, int[] pixels, int offset,
             int stride, int x, int y, int width, int height) {
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
@@ -345,6 +359,7 @@
     }
 
 
+    @LayoutlibDelegate
     /*package*/ static void nativeSetPixel(int nativeBitmap, int x, int y, int color) {
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
         if (delegate == null) {
@@ -354,6 +369,7 @@
         delegate.getImage().setRGB(x, y, color);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeSetPixels(int nativeBitmap, int[] colors, int offset,
             int stride, int x, int y, int width, int height) {
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
@@ -364,16 +380,21 @@
         delegate.getImage().setRGB(x, y, width, height, colors, offset, stride);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeCopyPixelsToBuffer(int nativeBitmap, Buffer dst) {
         // FIXME implement native delegate
-        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeCopyPixelsToBuffer");
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Bitmap.copyPixelsToBuffer is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeCopyPixelsFromBuffer(int nb, Buffer src) {
         // FIXME implement native delegate
-        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeCopyPixelsFromBuffer");
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Bitmap.copyPixelsFromBuffer is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativeGenerationId(int nativeBitmap) {
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
         if (delegate == null) {
@@ -383,6 +404,7 @@
         return delegate.mGenerationId;
     }
 
+    @LayoutlibDelegate
     /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) {
         // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only
         // used during aidl call so really this should not be called.
@@ -392,6 +414,7 @@
         return null;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeWriteToParcel(int nativeBitmap, boolean isMutable,
             int density, Parcel p) {
         // This is only called when sending a bitmap through aidl, so really this should not
@@ -402,6 +425,7 @@
         return false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static Bitmap nativeExtractAlpha(int nativeBitmap, int nativePaint,
             int[] offsetXY) {
         Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap);
@@ -429,10 +453,12 @@
                 Density.DEFAULT_DENSITY /*density*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativePrepareToDraw(int nativeBitmap) {
         // nothing to be done here.
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeSetHasAlpha(int nativeBitmap, boolean hasAlpha) {
         // get the delegate from the native int.
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
@@ -443,6 +469,7 @@
         delegate.mHasAlpha = hasAlpha;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeSameAs(int nb0, int nb1) {
         Bitmap_Delegate delegate1 = sManager.getDelegate(nb0);
         if (delegate1 == null) {
@@ -495,7 +522,7 @@
 
     private static Bitmap createBitmap(Bitmap_Delegate delegate, boolean isMutable, int density) {
         // get its native_int
-        int nativeInt = sManager.addDelegate(delegate);
+        int nativeInt = sManager.addNewDelegate(delegate);
 
         // and create/return a new Bitmap with it
         return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/, density);
diff --git a/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java
index 34824b4..4becba1 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.BlurMaskFilter
@@ -53,9 +54,10 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeConstructor(float radius, int style) {
         BlurMaskFilter_Delegate newDelegate = new BlurMaskFilter_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 5a6902c..4decd1a 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -20,6 +20,7 @@
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.layoutlib.bridge.impl.GcSnapshot;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.Bitmap.Config;
 import android.graphics.Paint_Delegate.FontInfo;
@@ -53,7 +54,7 @@
 
     // ---- delegate manager ----
     private static final DelegateManager<Canvas_Delegate> sManager =
-            new DelegateManager<Canvas_Delegate>();
+            new DelegateManager<Canvas_Delegate>(Canvas_Delegate.class);
 
     // ---- delegate helper data ----
 
@@ -63,7 +64,7 @@
     private Bitmap_Delegate mBitmap;
     private GcSnapshot mSnapshot;
 
-    private int mDrawFilter = 0;
+    private DrawFilter_Delegate mDrawFilter = null;
 
     // ---- Public Helper methods ----
 
@@ -94,11 +95,12 @@
      * @return the delegate or null.
      */
     public DrawFilter_Delegate getDrawFilter() {
-        return DrawFilter_Delegate.getDelegate(mDrawFilter);
+        return mDrawFilter;
     }
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static boolean isOpaque(Canvas thisCanvas) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
@@ -109,6 +111,7 @@
         return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int getWidth(Canvas thisCanvas) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
@@ -119,6 +122,7 @@
         return canvasDelegate.mBitmap.getImage().getWidth();
     }
 
+    @LayoutlibDelegate
     /*package*/ static int getHeight(Canvas thisCanvas) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
@@ -129,7 +133,8 @@
         return canvasDelegate.mBitmap.getImage().getHeight();
     }
 
-    /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
+    @LayoutlibDelegate
+   /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
         if (canvasDelegate == null) {
@@ -139,6 +144,7 @@
         canvasDelegate.getSnapshot().translate(dx, dy);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
@@ -149,7 +155,8 @@
         canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
     }
 
-    /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
+    @LayoutlibDelegate
+   /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
         if (canvasDelegate == null) {
@@ -159,7 +166,8 @@
         canvasDelegate.getSnapshot().scale(sx, sy);
     }
 
-    /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
+    @LayoutlibDelegate
+   /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
         if (canvasDelegate == null) {
@@ -182,15 +190,18 @@
         g.setTransform(currentTx);
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) {
         return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) {
         return clipRect(thisCanvas, (float) rect.left, (float) rect.top,
                 (float) rect.right, (float) rect.bottom);
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
             float bottom) {
         // get the delegate from the native int.
@@ -202,16 +213,19 @@
         return canvasDelegate.clipRect(left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right,
             int bottom) {
 
         return clipRect(thisCanvas, (float) left, (float) top, (float) right, (float) bottom);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int save(Canvas thisCanvas) {
         return save(thisCanvas, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
@@ -222,6 +236,7 @@
         return canvasDelegate.save(saveFlags);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void restore(Canvas thisCanvas) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
@@ -232,6 +247,7 @@
         canvasDelegate.restore();
     }
 
+    @LayoutlibDelegate
     /*package*/ static int getSaveCount(Canvas thisCanvas) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
@@ -242,6 +258,7 @@
         return canvasDelegate.getSnapshot().size();
     }
 
+    @LayoutlibDelegate
     /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
@@ -252,17 +269,22 @@
         canvasDelegate.restoreTo(saveCount);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count,
             Paint paint) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPoint is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPoint is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void drawLines(Canvas thisCanvas,
             final float[] pts, final int offset, final int count,
             Paint paint) {
@@ -277,10 +299,12 @@
                 });
     }
 
+    @LayoutlibDelegate
     /*package*/ static void freeCaches() {
         // nothing to be done here.
     }
 
+    @LayoutlibDelegate
     /*package*/ static int initRaster(int nativeBitmapOrZero) {
         if (nativeBitmapOrZero > 0) {
             // get the Bitmap from the int
@@ -289,15 +313,16 @@
             // create a new Canvas_Delegate with the given bitmap and return its new native int.
             Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate);
 
-            return sManager.addDelegate(newDelegate);
+            return sManager.addNewDelegate(newDelegate);
         } else {
             // create a new Canvas_Delegate and return its new native int.
             Canvas_Delegate newDelegate = new Canvas_Delegate();
 
-            return sManager.addDelegate(newDelegate);
+            return sManager.addNewDelegate(newDelegate);
         }
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setBitmap(int nativeCanvas, int bitmap) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
@@ -314,6 +339,7 @@
         canvasDelegate.setBitmap(bitmapDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds,
                                                int paint, int layerFlags) {
         // get the delegate from the native int.
@@ -330,6 +356,7 @@
         return canvasDelegate.saveLayer(bounds, paintDelegate, layerFlags);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_saveLayer(int nativeCanvas, float l,
                                                float t, float r, float b,
                                                int paint, int layerFlags) {
@@ -348,6 +375,7 @@
                 paintDelegate, layerFlags);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_saveLayerAlpha(int nativeCanvas,
                                                     RectF bounds, int alpha,
                                                     int layerFlags) {
@@ -360,6 +388,7 @@
         return canvasDelegate.saveLayerAlpha(bounds, alpha, layerFlags);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_saveLayerAlpha(int nativeCanvas, float l,
                                                     float t, float r, float b,
                                                     int alpha, int layerFlags) {
@@ -373,6 +402,7 @@
     }
 
 
+    @LayoutlibDelegate
     /*package*/ static void native_concat(int nCanvas, int nMatrix) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
@@ -400,6 +430,7 @@
         snapshot.setTransform(currentTx);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
@@ -429,6 +460,7 @@
         }
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_clipRect(int nCanvas,
                                                   float left, float top,
                                                   float right, float bottom,
@@ -443,6 +475,7 @@
         return canvasDelegate.clipRect(left, top, right, bottom, regionOp);
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_clipPath(int nativeCanvas,
                                                   int nativePath,
                                                   int regionOp) {
@@ -459,6 +492,7 @@
         return canvasDelegate.mSnapshot.clip(pathDelegate.getJavaShape(), regionOp);
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_clipRegion(int nativeCanvas,
                                                     int nativeRegion,
                                                     int regionOp) {
@@ -475,29 +509,23 @@
         return canvasDelegate.mSnapshot.clip(region.getJavaArea(), regionOp);
     }
 
-    /*package*/ static void nativeSetDrawFilter(int nativeCanvas,
-                                                   int nativeFilter) {
+    @LayoutlibDelegate
+    /*package*/ static void nativeSetDrawFilter(int nativeCanvas, int nativeFilter) {
         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return;
         }
 
-        canvasDelegate.mDrawFilter = nativeFilter;
+        canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter);
 
-        // get the delegate only because we don't support them at all for the moment, so
-        // we can display the message now.
-
-        DrawFilter_Delegate filterDelegate = DrawFilter_Delegate.getDelegate(nativeFilter);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        if (filterDelegate.isSupported() == false) {
+        if (canvasDelegate.mDrawFilter != null &&
+                canvasDelegate.mDrawFilter.isSupported() == false) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER,
-                    filterDelegate.getSupportMessage(), null, null /*data*/);
+                    canvasDelegate.mDrawFilter.getSupportMessage(), null, null /*data*/);
         }
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_getClipBounds(int nativeCanvas,
                                                        Rect bounds) {
         // get the delegate from the native int.
@@ -518,6 +546,7 @@
         return false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_getCTM(int canvas, int matrix) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
@@ -534,6 +563,7 @@
         matrixDelegate.set(Matrix_Delegate.makeValues(transform));
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_quickReject(int nativeCanvas,
                                                      RectF rect,
                                                      int native_edgeType) {
@@ -541,6 +571,7 @@
         return false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_quickReject(int nativeCanvas,
                                                      int path,
                                                      int native_edgeType) {
@@ -548,6 +579,7 @@
         return false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_quickReject(int nativeCanvas,
                                                      float left, float top,
                                                      float right, float bottom,
@@ -556,21 +588,25 @@
         return false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawRGB(int nativeCanvas, int r, int g, int b) {
         native_drawColor(nativeCanvas, 0xFF000000 | r << 16 | (g&0xFF) << 8 | (b&0xFF),
                 PorterDuff.Mode.SRC_OVER.nativeInt);
 
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawARGB(int nativeCanvas, int a, int r, int g, int b) {
         native_drawColor(nativeCanvas, a << 24 | (r&0xFF) << 16 | (g&0xFF) << 8 | (b&0xFF),
                 PorterDuff.Mode.SRC_OVER.nativeInt);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawColor(int nativeCanvas, int color) {
         native_drawColor(nativeCanvas, color, PorterDuff.Mode.SRC_OVER.nativeInt);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawColor(int nativeCanvas, final int color, final int mode) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
@@ -600,11 +636,14 @@
         });
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawPaint(int nativeCanvas, int paint) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPaint is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawLine(int nativeCanvas,
             final float startX, final float startY, final float stopX, final float stopY,
             int paint) {
@@ -617,11 +656,13 @@
         });
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawRect(int nativeCanvas, RectF rect,
                                                int paint) {
         native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawRect(int nativeCanvas,
             final float left, final float top, final float right, final float bottom, int paint) {
 
@@ -646,6 +687,7 @@
         });
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawOval(int nativeCanvas, final RectF oval, int paint) {
         if (oval.right > oval.left && oval.bottom > oval.top) {
             draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
@@ -670,21 +712,23 @@
         }
     }
 
-    /*package*/ static void native_drawCircle(int nativeCanvas, float cx,
-                                                 float cy, float radius,
-                                                 int paint) {
+    @LayoutlibDelegate
+    /*package*/ static void native_drawCircle(int nativeCanvas,
+            float cx, float cy, float radius, int paint) {
         native_drawOval(nativeCanvas,
                 new RectF(cx - radius, cy - radius, radius*2, radius*2),
                 paint);
     }
 
-    /*package*/ static void native_drawArc(int nativeCanvas, RectF oval,
-                                              float startAngle, float sweep,
-                                              boolean useCenter, int paint) {
+    @LayoutlibDelegate
+    /*package*/ static void native_drawArc(int nativeCanvas,
+            RectF oval, float startAngle, float sweep, boolean useCenter, int paint) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawArc is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawRoundRect(int nativeCanvas,
             final RectF rect, final float rx, final float ry, int paint) {
 
@@ -713,8 +757,8 @@
         });
     }
 
-    /*package*/ static void native_drawPath(int nativeCanvas, int path,
-                                               int paint) {
+    @LayoutlibDelegate
+    /*package*/ static void native_drawPath(int nativeCanvas, int path, int paint) {
         final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
         if (pathDelegate == null) {
             return;
@@ -739,6 +783,7 @@
         });
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
                                                  float left, float top,
                                                  int nativePaintOrZero,
@@ -760,6 +805,7 @@
                 (int)left, (int)top, (int)right, (int)bottom);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
                                                  Rect src, RectF dst,
                                                  int nativePaintOrZero,
@@ -784,6 +830,7 @@
         }
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap,
                                                  Rect src, Rect dst,
                                                  int nativePaintOrZero,
@@ -808,6 +855,7 @@
         }
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors,
                                                 int offset, int stride, final float x,
                                                  final float y, int width, int height,
@@ -832,6 +880,7 @@
         });
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap,
                                                       int nMatrix, int nPaint) {
         // get the delegate from the native int.
@@ -871,29 +920,35 @@
         }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap,
-                                                    int meshWidth, int meshHeight,
-                                                    float[] verts, int vertOffset,
-                                                    int[] colors, int colorOffset, int nPaint) {
+            int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
+            int colorOffset, int nPaint) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawBitmapMesh is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n,
-                   float[] verts, int vertOffset, float[] texs, int texOffset,
-                   int[] colors, int colorOffset, short[] indices,
-                   int indexOffset, int indexCount, int nPaint) {
+            float[] verts, int vertOffset,
+            float[] texs, int texOffset,
+            int[] colors, int colorOffset,
+            short[] indices, int indexOffset,
+            int indexCount, int nPaint) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawVertices is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawText(int nativeCanvas,
             final char[] text, final int index, final int count,
             final float startX, final float startY, int flags, int paint) {
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                 new GcSnapshot.Drawable() {
             public void draw(Graphics2D graphics, Paint_Delegate paint) {
-                // WARNING: the logic in this method is similar to Paint.measureText.
+                // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
                 // Any change to this method should be reflected in Paint.measureText
                 // Paint.TextAlign indicates how the text is positioned relative to X.
                 // LEFT is the default and there's nothing to do.
@@ -986,6 +1041,7 @@
         });
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawText(int nativeCanvas, String text,
                                                int start, int end, float x,
                                                float y, int flags, int paint) {
@@ -996,6 +1052,7 @@
         native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawTextRun(int nativeCanvas, String text,
             int start, int end, int contextStart, int contextEnd,
             float x, float y, int flags, int paint) {
@@ -1006,27 +1063,33 @@
         native_drawText(nativeCanvas, buffer, start, end, x, y, flags, paint);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text,
             int start, int count, int contextStart, int contextCount,
             float x, float y, int flags, int paint) {
         native_drawText(nativeCanvas, text, start, count, x, y, flags, paint);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawPosText(int nativeCanvas,
                                                   char[] text, int index,
                                                   int count, float[] pos,
                                                   int paint) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPosText is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawPosText(int nativeCanvas,
                                                   String text, float[] pos,
                                                   int paint) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPosText is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawTextOnPath(int nativeCanvas,
                                                      char[] text, int index,
                                                      int count, int path,
@@ -1034,24 +1097,30 @@
                                                      float vOffset, int bidiFlags,
                                                      int paint) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawTextOnPath(int nativeCanvas,
                                                      String text, int path,
                                                      float hOffset,
                                                      float vOffset,
                                                      int flags, int paint) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_drawPicture(int nativeCanvas,
                                                   int nativePicture) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPicture is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void finalizer(int nativeCanvas) {
         // get the delegate from the native int so that it can be disposed.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
@@ -1062,7 +1131,7 @@
         canvasDelegate.dispose();
 
         // remove it from the manager.
-        sManager.removeDelegate(nativeCanvas);
+        sManager.removeJavaReferenceFor(nativeCanvas);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
index 3df170f..e5a7ab6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.ColorFilter
@@ -37,7 +38,7 @@
 
     // ---- delegate manager ----
     protected static final DelegateManager<ColorFilter_Delegate> sManager =
-            new DelegateManager<ColorFilter_Delegate>();
+            new DelegateManager<ColorFilter_Delegate>(ColorFilter_Delegate.class);
 
     // ---- delegate helper data ----
 
@@ -54,8 +55,9 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static void finalizer(int native_instance, int nativeColorFilter) {
-        sManager.removeDelegate(native_instance);
+        sManager.removeJavaReferenceFor(native_instance);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
index 42843279..2de344b 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.ColorMatrixColorFilter
@@ -53,11 +54,13 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeColorMatrixFilter(float[] array) {
         ColorMatrixColorFilter_Delegate newDelegate = new ColorMatrixColorFilter_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nColorMatrixFilter(int nativeFilter, float[] array) {
         // pass
         return 0;
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java
index 39cbbc6..7c04a87 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import java.awt.Stroke;
 
@@ -60,9 +61,10 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate(int outerpe, int innerpe) {
         ComposePathEffect_Delegate newDelegate = new ComposePathEffect_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
index b4baa6f..f6e1d00 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import java.awt.Paint;
 
@@ -61,33 +62,36 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate1(int native_shaderA, int native_shaderB,
             int native_mode) {
         // FIXME not supported yet.
         ComposeShader_Delegate newDelegate = new ComposeShader_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate2(int native_shaderA, int native_shaderB,
             int porterDuffMode) {
         // FIXME not supported yet.
         ComposeShader_Delegate newDelegate = new ComposeShader_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativePostCreate1(int native_shader, int native_skiaShaderA,
             int native_skiaShaderB, int native_mode) {
         // pass, not needed.
         return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativePostCreate2(int native_shader, int native_skiaShaderA,
             int native_skiaShaderB, int porterDuffMode) {
         // pass, not needed.
         return 0;
     }
 
-
     // ---- Private delegate/helper methods ----
 
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java
index 0307cfb..b0f8168 100644
--- a/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import java.awt.Stroke;
 
@@ -60,9 +61,10 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate(float radius) {
         CornerPathEffect_Delegate newDelegate = new CornerPathEffect_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java
index 5a704a7..d97c2ec 100644
--- a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import java.awt.BasicStroke;
 import java.awt.Stroke;
@@ -71,9 +72,10 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate(float intervals[], float phase) {
         DashPathEffect_Delegate newDelegate = new DashPathEffect_Delegate(intervals, phase);
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java
index 04d7170..ec4a810 100644
--- a/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import java.awt.Stroke;
 
@@ -60,9 +61,10 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate(float length, float deviation) {
         DiscretePathEffect_Delegate newDelegate = new DiscretePathEffect_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java
index ddf20b6..870c46b 100644
--- a/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.DrawFilter
@@ -37,7 +38,7 @@
 
     // ---- delegate manager ----
     protected static final DelegateManager<DrawFilter_Delegate> sManager =
-            new DelegateManager<DrawFilter_Delegate>();
+            new DelegateManager<DrawFilter_Delegate>(DrawFilter_Delegate.class);
 
     // ---- delegate helper data ----
 
@@ -54,8 +55,9 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static void nativeDestructor(int nativeDrawFilter) {
-        sManager.removeDelegate(nativeDrawFilter);
+        sManager.removeJavaReferenceFor(nativeDrawFilter);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java
index 82f1da3..ebc1c1d 100644
--- a/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.EmbossMaskFilter
@@ -53,10 +54,11 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeConstructor(float[] direction, float ambient,
             float specular, float blurRadius) {
         EmbossMaskFilter_Delegate newDelegate = new EmbossMaskFilter_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java
index 132004f9..51e0576 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.LayerRasterizer
@@ -53,11 +54,13 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeConstructor() {
         LayerRasterizer_Delegate newDelegate = new LayerRasterizer_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeAddLayer(int native_layer, int native_paint, float dx, float dy) {
 
     }
diff --git a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
index ba2cfad..0ee883d 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.LightingColorFilter
@@ -53,11 +54,13 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int native_CreateLightingFilter(int mul, int add) {
         LightingColorFilter_Delegate newDelegate = new LightingColorFilter_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nCreateLightingFilter(int nativeFilter, int mul, int add) {
         // pass
         return 0;
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
index 9525dcf..a2ba758 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -19,6 +19,7 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.Shader.TileMode;
 
@@ -52,13 +53,16 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate1(LinearGradient thisGradient,
             float x0, float y0, float x1, float y1,
             int colors[], float positions[], int tileMode) {
         LinearGradient_Delegate newDelegate = new LinearGradient_Delegate(x0, y0, x1, y1,
                 colors, positions, Shader_Delegate.getTileMode(tileMode));
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
+
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate2(LinearGradient thisGradient,
             float x0, float y0, float x1, float y1,
             int color0, int color1, int tileMode) {
@@ -66,12 +70,16 @@
                 x0, y0, x1, y1, new int[] { color0, color1}, null /*positions*/,
                 tileMode);
     }
+
+    @LayoutlibDelegate
     /*package*/ static int nativePostCreate1(LinearGradient thisGradient,
             int native_shader, float x0, float y0, float x1, float y1,
             int colors[], float positions[], int tileMode) {
         // nothing to be done here.
         return 0;
     }
+
+    @LayoutlibDelegate
     /*package*/ static int nativePostCreate2(LinearGradient thisGradient,
             int native_shader, float x0, float y0, float x1, float y1,
             int color0, int color1, int tileMode) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java
index c582a91..c2f27e4 100644
--- a/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.MaskFilter
@@ -37,7 +38,7 @@
 
     // ---- delegate manager ----
     protected static final DelegateManager<MaskFilter_Delegate> sManager =
-            new DelegateManager<MaskFilter_Delegate>();
+            new DelegateManager<MaskFilter_Delegate>(MaskFilter_Delegate.class);
 
     // ---- delegate helper data ----
 
@@ -54,8 +55,9 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static void nativeDestructor(int native_filter) {
-        sManager.removeDelegate(native_filter);
+        sManager.removeJavaReferenceFor(native_filter);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
index 2d77d40..451edd2 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -20,6 +20,7 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.Matrix.ScaleToFit;
 
@@ -45,7 +46,7 @@
 
     // ---- delegate manager ----
     private static final DelegateManager<Matrix_Delegate> sManager =
-            new DelegateManager<Matrix_Delegate>();
+            new DelegateManager<Matrix_Delegate>(Matrix_Delegate.class);
 
     // ---- delegate data ----
     private float mValues[] = new float[MATRIX_SIZE];
@@ -172,6 +173,7 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int native_create(int native_src_or_zero) {
         // create the delegate
         Matrix_Delegate newDelegate = new Matrix_Delegate();
@@ -187,9 +189,10 @@
             }
         }
 
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_isIdentity(int native_object) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -199,6 +202,7 @@
         return d.isIdentity();
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_rectStaysRect(int native_object) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -208,6 +212,7 @@
         return (d.computeTypeMask() & kRectStaysRect_Mask) != 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_reset(int native_object) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -217,6 +222,7 @@
         reset(d.mValues);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_set(int native_object, int other) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -231,6 +237,7 @@
         System.arraycopy(src.mValues, 0, d.mValues, 0, MATRIX_SIZE);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setTranslate(int native_object, float dx, float dy) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -240,6 +247,7 @@
         setTranslate(d.mValues, dx, dy);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setScale(int native_object, float sx, float sy,
             float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
@@ -250,6 +258,7 @@
         d.mValues = getScale(sx, sy, px, py);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setScale(int native_object, float sx, float sy) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -267,6 +276,7 @@
         d.mValues[8] = 1;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setRotate(int native_object, float degrees, float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -276,6 +286,7 @@
         d.mValues = getRotate(degrees, px, py);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setRotate(int native_object, float degrees) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -285,6 +296,7 @@
         setRotate(d.mValues, degrees);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setSinCos(int native_object, float sinValue, float cosValue,
             float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
@@ -303,6 +315,7 @@
         d.postTransform(getTranslate(px, py));
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setSinCos(int native_object, float sinValue, float cosValue) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -312,6 +325,7 @@
         setRotate(d.mValues, sinValue, cosValue);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setSkew(int native_object, float kx, float ky,
             float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
@@ -322,6 +336,7 @@
         d.mValues = getSkew(kx, ky, px, py);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setSkew(int native_object, float kx, float ky) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -339,6 +354,7 @@
         d.mValues[8] = 1;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_setConcat(int native_object, int a, int b) {
         if (a == native_object) {
             return native_preConcat(native_object, b);
@@ -366,6 +382,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_preTranslate(int native_object, float dx, float dy) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -376,6 +393,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_preScale(int native_object, float sx, float sy,
             float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
@@ -387,6 +405,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_preScale(int native_object, float sx, float sy) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -397,6 +416,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_preRotate(int native_object, float degrees,
             float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
@@ -408,6 +428,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_preRotate(int native_object, float degrees) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -422,6 +443,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_preSkew(int native_object, float kx, float ky,
             float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
@@ -433,6 +455,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_preSkew(int native_object, float kx, float ky) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -443,6 +466,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_preConcat(int native_object, int other_matrix) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -458,6 +482,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_postTranslate(int native_object, float dx, float dy) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -468,6 +493,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_postScale(int native_object, float sx, float sy,
             float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
@@ -479,6 +505,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_postScale(int native_object, float sx, float sy) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -489,6 +516,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_postRotate(int native_object, float degrees,
             float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
@@ -500,6 +528,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_postRotate(int native_object, float degrees) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -510,6 +539,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_postSkew(int native_object, float kx, float ky,
             float px, float py) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
@@ -521,6 +551,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_postSkew(int native_object, float kx, float ky) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -531,6 +562,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_postConcat(int native_object, int other_matrix) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -546,6 +578,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_setRectToRect(int native_object, RectF src,
             RectF dst, int stf) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
@@ -610,6 +643,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_setPolyToPoly(int native_object, float[] src, int srcIndex,
             float[] dst, int dstIndex, int pointCount) {
         // FIXME
@@ -619,6 +653,7 @@
         return false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_invert(int native_object, int inverse) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -646,6 +681,7 @@
         }
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_mapPoints(int native_object, float[] dst, int dstIndex,
             float[] src, int srcIndex, int ptCount, boolean isPts) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
@@ -660,6 +696,7 @@
         }
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_mapRect(int native_object, RectF dst, RectF src) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -669,6 +706,7 @@
         return d.mapRect(dst, src);
     }
 
+    @LayoutlibDelegate
     /*package*/ static float native_mapRadius(int native_object, float radius) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -684,6 +722,7 @@
         return (float) Math.sqrt(l1 * l2);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_getValues(int native_object, float[] values) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -693,6 +732,7 @@
         System.arraycopy(d.mValues, 0, d.mValues, 0, MATRIX_SIZE);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setValues(int native_object, float[] values) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
@@ -702,6 +742,7 @@
         System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE);
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_equals(int native_a, int native_b) {
         Matrix_Delegate a = sManager.getDelegate(native_a);
         if (a == null) {
@@ -722,8 +763,9 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void finalizer(int native_instance) {
-        sManager.removeDelegate(native_instance);
+        sManager.removeJavaReferenceFor(native_instance);
     }
 
     // ---- Private helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
index 61ed71e..5e882ce 100644
--- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -21,6 +21,7 @@
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.layoutlib.bridge.impl.GcSnapshot;
 import com.android.ninepatch.NinePatchChunk;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.drawable.NinePatchDrawable;
 
@@ -137,6 +138,7 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static boolean isNinePatchChunk(byte[] chunk) {
         NinePatchChunk chunkObject = getChunk(chunk);
         if (chunkObject != null) {
@@ -146,12 +148,14 @@
         return false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void validateNinePatchChunk(int bitmap, byte[] chunk) {
         // the default JNI implementation only checks that the byte[] has the same
         // size as the C struct it represent. Since we cannot do the same check (serialization
         // will return different size depending on content), we do nothing.
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance,
             byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) {
         draw(canvas_instance,
@@ -160,6 +164,7 @@
                 destDensity, srcDensity);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeDraw(int canvas_instance, Rect loc, int bitmap_instance,
             byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) {
         draw(canvas_instance,
@@ -168,54 +173,53 @@
                 destDensity, srcDensity);
     }
 
-   private static void draw(int canvas_instance,
-           final int left, final int top, final int right, final int bottom,
-           int bitmap_instance, byte[] c, int paint_instance_or_null,
-           final int destDensity, final int srcDensity) {
-       // get the delegate from the native int.
-       final Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance);
-       if (bitmap_delegate == null) {
-           return;
-       }
-
-       if (c == null) {
-           // not a 9-patch?
-           BufferedImage image = bitmap_delegate.getImage();
-           Canvas_Delegate.native_drawBitmap(canvas_instance, bitmap_instance,
-                   new Rect(0, 0, image.getWidth(), image.getHeight()),
-                   new Rect(left, top, right, bottom),
-                   paint_instance_or_null, destDensity, srcDensity);
-           return;
-       }
-
-       final NinePatchChunk chunkObject = getChunk(c);
-       assert chunkObject != null;
-       if (chunkObject == null) {
-           return;
-       }
-
-       Canvas_Delegate canvas_delegate = Canvas_Delegate.getDelegate(canvas_instance);
-       if (canvas_delegate == null) {
-           return;
-       }
-
-       // this one can be null
-       Paint_Delegate paint_delegate = Paint_Delegate.getDelegate(paint_instance_or_null);
-
-       canvas_delegate.getSnapshot().draw(new GcSnapshot.Drawable() {
-               public void draw(Graphics2D graphics, Paint_Delegate paint) {
-                   chunkObject.draw(bitmap_delegate.getImage(), graphics,
-                           left, top, right - left, bottom - top, destDensity, srcDensity);
-               }
-           }, paint_delegate, true /*compositeOnly*/, false /*forceSrcMode*/);
-
-    }
-
+    @LayoutlibDelegate
     /*package*/ static int nativeGetTransparentRegion(int bitmap, byte[] chunk, Rect location) {
         return 0;
     }
 
     // ---- Private Helper methods ----
 
+    private static void draw(int canvas_instance,
+            final int left, final int top, final int right, final int bottom,
+            int bitmap_instance, byte[] c, int paint_instance_or_null,
+            final int destDensity, final int srcDensity) {
+        // get the delegate from the native int.
+        final Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance);
+        if (bitmap_delegate == null) {
+            return;
+        }
 
+        if (c == null) {
+            // not a 9-patch?
+            BufferedImage image = bitmap_delegate.getImage();
+            Canvas_Delegate.native_drawBitmap(canvas_instance, bitmap_instance,
+                    new Rect(0, 0, image.getWidth(), image.getHeight()),
+                    new Rect(left, top, right, bottom),
+                    paint_instance_or_null, destDensity, srcDensity);
+            return;
+        }
+
+        final NinePatchChunk chunkObject = getChunk(c);
+        assert chunkObject != null;
+        if (chunkObject == null) {
+            return;
+        }
+
+        Canvas_Delegate canvas_delegate = Canvas_Delegate.getDelegate(canvas_instance);
+        if (canvas_delegate == null) {
+            return;
+        }
+
+        // this one can be null
+        Paint_Delegate paint_delegate = Paint_Delegate.getDelegate(paint_instance_or_null);
+
+        canvas_delegate.getSnapshot().draw(new GcSnapshot.Drawable() {
+                public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                    chunkObject.draw(bitmap_delegate.getImage(), graphics,
+                            left, top, right - left, bottom - top, destDensity, srcDensity);
+                }
+            }, paint_delegate, true /*compositeOnly*/, false /*forceSrcMode*/);
+
+     }
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java
index ec92507..71d346a 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.PaintFlagsDrawFilter
@@ -53,9 +54,10 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeConstructor(int clearBits, int setBits) {
         PaintFlagsDrawFilter_Delegate newDelegate = new PaintFlagsDrawFilter_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 87164fb..373f482 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -19,6 +19,7 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.Paint.FontMetrics;
 import android.graphics.Paint.FontMetricsInt;
@@ -31,6 +32,7 @@
 import java.awt.Toolkit;
 import java.awt.font.FontRenderContext;
 import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -60,7 +62,7 @@
 
     // ---- delegate manager ----
     private static final DelegateManager<Paint_Delegate> sManager =
-            new DelegateManager<Paint_Delegate>();
+            new DelegateManager<Paint_Delegate>(Paint_Delegate.class);
 
     // ---- delegate helper data ----
     private List<FontInfo> mFonts;
@@ -74,19 +76,19 @@
     private int mCap;
     private int mJoin;
     private int mTextAlign;
-    private int mTypeface;
+    private Typeface_Delegate mTypeface;
     private float mStrokeWidth;
     private float mStrokeMiter;
     private float mTextSize;
     private float mTextScaleX;
     private float mTextSkewX;
 
-    private int mXfermode;
-    private int mColorFilter;
-    private int mShader;
-    private int mPathEffect;
-    private int mMaskFilter;
-    private int mRasterizer;
+    private Xfermode_Delegate mXfermode;
+    private ColorFilter_Delegate mColorFilter;
+    private Shader_Delegate mShader;
+    private PathEffect_Delegate mPathEffect;
+    private MaskFilter_Delegate mMaskFilter;
+    private Rasterizer_Delegate mRasterizer;
 
 
     // ---- Public Helper methods ----
@@ -171,17 +173,16 @@
     }
 
     public Stroke getJavaStroke() {
-        PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(mPathEffect);
-        if (effectDelegate != null) {
-            if (effectDelegate.isSupported()) {
-                Stroke stroke = effectDelegate.getStroke(this);
+        if (mPathEffect != null) {
+            if (mPathEffect.isSupported()) {
+                Stroke stroke = mPathEffect.getStroke(this);
                 assert stroke != null;
                 if (stroke != null) {
                     return stroke;
                 }
             } else {
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_PATHEFFECT,
-                        effectDelegate.getSupportMessage(),
+                        mPathEffect.getSupportMessage(),
                         null, null /*data*/);
             }
         }
@@ -200,7 +201,7 @@
      * @return the delegate or null.
      */
     public Xfermode_Delegate getXfermode() {
-        return  Xfermode_Delegate.getDelegate(mXfermode);
+        return mXfermode;
     }
 
     /**
@@ -209,7 +210,7 @@
      * @return the delegate or null.
      */
     public ColorFilter_Delegate getColorFilter() {
-        return ColorFilter_Delegate.getDelegate(mColorFilter);
+        return mColorFilter;
     }
 
     /**
@@ -218,7 +219,7 @@
      * @return the delegate or null.
      */
     public Shader_Delegate getShader() {
-        return Shader_Delegate.getDelegate(mShader);
+        return mShader;
     }
 
     /**
@@ -227,7 +228,7 @@
      * @return the delegate or null.
      */
     public MaskFilter_Delegate getMaskFilter() {
-        return MaskFilter_Delegate.getDelegate(mMaskFilter);
+        return mMaskFilter;
     }
 
     /**
@@ -236,11 +237,12 @@
      * @return the delegate or null.
      */
     public Rasterizer_Delegate getRasterizer() {
-        return Rasterizer_Delegate.getDelegate(mRasterizer);
+        return mRasterizer;
     }
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int getFlags(Paint thisPaint) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -251,6 +253,7 @@
         return delegate.mFlags;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setFlags(Paint thisPaint, int flags) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -261,38 +264,47 @@
         delegate.mFlags = flags;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) {
         setFlag(thisPaint, Paint.FILTER_BITMAP_FLAG, filter);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) {
         setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) {
         setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) {
         setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) {
         setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) {
         setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setDither(Paint thisPaint, boolean dither) {
         setFlag(thisPaint, Paint.DITHER_FLAG, dither);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) {
         setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int getColor(Paint thisPaint) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -303,6 +315,7 @@
         return delegate.mColor;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setColor(Paint thisPaint, int color) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -313,6 +326,7 @@
         delegate.mColor = color;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int getAlpha(Paint thisPaint) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -323,6 +337,7 @@
         return delegate.getAlpha();
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setAlpha(Paint thisPaint, int a) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -333,6 +348,7 @@
         delegate.setAlpha(a);
     }
 
+    @LayoutlibDelegate
     /*package*/ static float getStrokeWidth(Paint thisPaint) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -343,6 +359,7 @@
         return delegate.mStrokeWidth;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setStrokeWidth(Paint thisPaint, float width) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -353,6 +370,7 @@
         delegate.mStrokeWidth = width;
     }
 
+    @LayoutlibDelegate
     /*package*/ static float getStrokeMiter(Paint thisPaint) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -363,6 +381,7 @@
         return delegate.mStrokeMiter;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -373,6 +392,7 @@
         delegate.mStrokeMiter = miter;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy,
             int color) {
         // FIXME
@@ -380,6 +400,7 @@
                 "Paint.setShadowLayer is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static float getTextSize(Paint thisPaint) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -390,6 +411,7 @@
         return delegate.mTextSize;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setTextSize(Paint thisPaint, float textSize) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -401,6 +423,7 @@
         delegate.updateFontObject();
     }
 
+    @LayoutlibDelegate
     /*package*/ static float getTextScaleX(Paint thisPaint) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -411,6 +434,7 @@
         return delegate.mTextScaleX;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -422,6 +446,7 @@
         delegate.updateFontObject();
     }
 
+    @LayoutlibDelegate
     /*package*/ static float getTextSkewX(Paint thisPaint) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -432,6 +457,7 @@
         return delegate.mTextSkewX;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -443,6 +469,7 @@
         delegate.updateFontObject();
     }
 
+    @LayoutlibDelegate
     /*package*/ static float ascent(Paint thisPaint) {
         // get the delegate
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -459,6 +486,7 @@
         return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static float descent(Paint thisPaint) {
         // get the delegate
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -475,6 +503,7 @@
 
     }
 
+    @LayoutlibDelegate
     /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) {
         // get the delegate
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -485,6 +514,7 @@
         return delegate.getFontMetrics(metrics);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) {
         // get the delegate
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -509,11 +539,9 @@
         return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
             int count) {
-        // WARNING: the logic in this method is similar to Canvas.drawText.
-        // Any change to this method should be reflected in Canvas.drawText
-
         // get the delegate
         Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
         if (delegate == null) {
@@ -523,32 +551,73 @@
         return delegate.measureText(text, index, count);
     }
 
+    @LayoutlibDelegate
     /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) {
         return native_measureText(thisPaint, text.toCharArray(), start, end - start);
     }
 
+    @LayoutlibDelegate
     /*package*/ static float native_measureText(Paint thisPaint, String text) {
         return native_measureText(thisPaint, text.toCharArray(), 0, text.length());
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
             float maxWidth, float[] measuredWidth) {
-        // FIXME
-        throw new UnsupportedOperationException();
+
+        // get the delegate
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return 0;
+        }
+
+        int inc = count > 0 ? 1 : -1;
+
+        int measureIndex = 0;
+        float measureAcc = 0;
+        for (int i = index; i != index + count; i += inc, measureIndex++) {
+            int start, end;
+            if (i < index) {
+                start = i;
+                end = index;
+            } else {
+                start = index;
+                end = i;
+            }
+
+            // measure from start to end
+            float res = delegate.measureText(text, start, end - start + 1);
+
+            if (measuredWidth != null) {
+                measuredWidth[measureIndex] = res;
+            }
+
+            measureAcc += res;
+            if (res > maxWidth) {
+                // we should not return this char index, but since it's 0-based
+                // and we need to return a count, we simply return measureIndex;
+                return measureIndex;
+            }
+
+        }
+
+        return measureIndex;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards,
             float maxWidth, float[] measuredWidth) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        return native_breakText(thisPaint, text.toCharArray(), 0, text.length(), maxWidth,
+                measuredWidth);
     }
 
-
+    @LayoutlibDelegate
     /*package*/ static int native_init() {
         Paint_Delegate newDelegate = new Paint_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_initWithPaint(int paint) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(paint);
@@ -557,9 +626,10 @@
         }
 
         Paint_Delegate newDelegate = new Paint_Delegate(delegate);
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_reset(int native_object) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -570,6 +640,7 @@
         delegate.reset();
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_set(int native_dst, int native_src) {
         // get the delegate from the native int.
         Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
@@ -586,6 +657,7 @@
         delegate_dst.set(delegate_src);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_getStyle(int native_object) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -596,6 +668,7 @@
         return delegate.mStyle;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setStyle(int native_object, int style) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -606,6 +679,7 @@
         delegate.mStyle = style;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_getStrokeCap(int native_object) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -616,6 +690,7 @@
         return delegate.mCap;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setStrokeCap(int native_object, int cap) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -626,6 +701,7 @@
         delegate.mCap = cap;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_getStrokeJoin(int native_object) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -636,6 +712,7 @@
         return delegate.mJoin;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setStrokeJoin(int native_object, int join) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -646,6 +723,7 @@
         delegate.mJoin = join;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) {
         Paint_Delegate paint = sManager.getDelegate(native_object);
         if (paint == null) {
@@ -671,6 +749,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_setShader(int native_object, int shader) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -678,9 +757,12 @@
             return shader;
         }
 
-        return delegate.mShader = shader;
+        delegate.mShader = Shader_Delegate.getDelegate(shader);
+
+        return shader;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_setColorFilter(int native_object, int filter) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -688,18 +770,18 @@
             return filter;
         }
 
-        delegate.mColorFilter = filter;
+        delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);;
 
         // since none of those are supported, display a fidelity warning right away
-        ColorFilter_Delegate filterDelegate = delegate.getColorFilter();
-        if (filterDelegate != null && filterDelegate.isSupported() == false) {
+        if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER,
-                    filterDelegate.getSupportMessage(), null, null /*data*/);
+                    delegate.mColorFilter.getSupportMessage(), null, null /*data*/);
         }
 
         return filter;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_setXfermode(int native_object, int xfermode) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -707,9 +789,12 @@
             return xfermode;
         }
 
-        return delegate.mXfermode = xfermode;
+        delegate.mXfermode = Xfermode_Delegate.getDelegate(xfermode);
+
+        return xfermode;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_setPathEffect(int native_object, int effect) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -717,9 +802,12 @@
             return effect;
         }
 
-        return delegate.mPathEffect = effect;
+        delegate.mPathEffect = PathEffect_Delegate.getDelegate(effect);
+
+        return effect;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -727,18 +815,18 @@
             return maskfilter;
         }
 
-        delegate.mMaskFilter = maskfilter;
+        delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter);
 
         // since none of those are supported, display a fidelity warning right away
-        MaskFilter_Delegate filterDelegate = delegate.getMaskFilter();
-        if (filterDelegate != null && filterDelegate.isSupported() == false) {
+        if (delegate.mMaskFilter != null && delegate.mMaskFilter.isSupported() == false) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
-                    filterDelegate.getSupportMessage(), null, null /*data*/);
+                    delegate.mMaskFilter.getSupportMessage(), null, null /*data*/);
         }
 
         return maskfilter;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_setTypeface(int native_object, int typeface) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -746,11 +834,12 @@
             return 0;
         }
 
-        delegate.mTypeface = typeface;
+        delegate.mTypeface = Typeface_Delegate.getDelegate(typeface);
         delegate.updateFontObject();
-        return delegate.mTypeface;
+        return typeface;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_setRasterizer(int native_object, int rasterizer) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -758,18 +847,18 @@
             return rasterizer;
         }
 
-        delegate.mRasterizer = rasterizer;
+        delegate.mRasterizer = Rasterizer_Delegate.getDelegate(rasterizer);
 
         // since none of those are supported, display a fidelity warning right away
-        Rasterizer_Delegate rasterizerDelegate = delegate.getRasterizer();
-        if (rasterizerDelegate != null && rasterizerDelegate.isSupported() == false) {
+        if (delegate.mRasterizer != null && delegate.mRasterizer.isSupported() == false) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER,
-                    rasterizerDelegate.getSupportMessage(), null, null /*data*/);
+                    delegate.mRasterizer.getSupportMessage(), null, null /*data*/);
         }
 
         return rasterizer;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_getTextAlign(int native_object) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -780,6 +869,7 @@
         return delegate.mTextAlign;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setTextAlign(int native_object, int align) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -790,6 +880,7 @@
         delegate.mTextAlign = align;
     }
 
+    @LayoutlibDelegate
     /*package*/ static float native_getFontMetrics(int native_paint, FontMetrics metrics) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_paint);
@@ -800,18 +891,55 @@
         return delegate.getFontMetrics(metrics);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_getTextWidths(int native_object, char[] text, int index,
             int count, float[] widths) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return 0;
+        }
+
+        if (delegate.mFonts.size() > 0) {
+            // FIXME: handle multi-char characters (see measureText)
+            float totalAdvance = 0;
+            for (int i = 0; i < count; i++) {
+                char c = text[i + index];
+                boolean found = false;
+                for (FontInfo info : delegate.mFonts) {
+                    if (info.mFont.canDisplay(c)) {
+                        float adv = info.mMetrics.charWidth(c);
+                        totalAdvance += adv;
+                        if (widths != null) {
+                            widths[i] = adv;
+                        }
+
+                        found = true;
+                        break;
+                    }
+                }
+
+                if (found == false) {
+                    // no advance for this char.
+                    if (widths != null) {
+                        widths[i] = 0.f;
+                    }
+                }
+            }
+
+            return (int) totalAdvance;
+        }
+
+        return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_getTextWidths(int native_object, String text, int start,
             int end, float[] widths) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        return native_getTextWidths(native_object, text.toCharArray(), start, end - start, widths);
     }
 
+    @LayoutlibDelegate
     /*package*/ static float native_getTextRunAdvances(int native_object,
             char[] text, int index, int count, int contextIndex, int contextCount,
             int flags, float[] advances, int advancesIndex) {
@@ -855,6 +983,7 @@
 
     }
 
+    @LayoutlibDelegate
     /*package*/ static float native_getTextRunAdvances(int native_object,
             String text, int start, int end, int contextStart, int contextEnd,
             int flags, float[] advances, int advancesIndex) {
@@ -867,44 +996,70 @@
                 contextEnd - contextStart, flags, advances, advancesIndex);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text,
             int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Paint.getTextRunCursor is not supported.", null, null /*data*/);
+        return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text,
             int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Paint.getTextRunCursor is not supported.", null, null /*data*/);
+        return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
                 char[] text, int index, int count, float x, float y, int path) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Paint.getTextPath is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
             String text, int start, int end, float x, float y, int path) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Paint.getTextPath is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start,
             int end, Rect bounds) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        nativeGetCharArrayBounds(nativePaint, text.toCharArray(), start, end - start, bounds);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index,
             int count, Rect bounds) {
-        // FIXME
-        throw new UnsupportedOperationException();
+
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        // FIXME should test if the main font can display all those characters.
+        // See MeasureText
+        if (delegate.mFonts.size() > 0) {
+            FontInfo mainInfo = delegate.mFonts.get(0);
+
+            Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count,
+                    delegate.mFontContext);
+            bounds.set(0, 0, (int) rect.getWidth(), (int) rect.getHeight());
+        }
     }
 
+    @LayoutlibDelegate
     /*package*/ static void finalizer(int nativePaint) {
-        sManager.removeDelegate(nativePaint);
+        sManager.removeJavaReferenceFor(nativePaint);
     }
 
     // ---- Private delegate/helper methods ----
@@ -946,18 +1101,18 @@
         mCap = Paint.Cap.BUTT.nativeInt;
         mJoin = Paint.Join.MITER.nativeInt;
         mTextAlign = 0;
-        mTypeface = Typeface.sDefaults[0].native_instance;
+        mTypeface = Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance);
         mStrokeWidth = 1.f;
         mStrokeMiter = 4.f;
         mTextSize = 20.f;
         mTextScaleX = 1.f;
         mTextSkewX = 0.f;
-        mXfermode = 0;
-        mColorFilter = 0;
-        mShader = 0;
-        mPathEffect = 0;
-        mMaskFilter = 0;
-        mRasterizer = 0;
+        mXfermode = null;
+        mColorFilter = null;
+        mShader = null;
+        mPathEffect = null;
+        mMaskFilter = null;
+        mRasterizer = null;
         updateFontObject();
     }
 
@@ -966,9 +1121,9 @@
      */
     @SuppressWarnings("deprecation")
     private void updateFontObject() {
-        if (mTypeface != 0) {
+        if (mTypeface != null) {
             // Get the fonts from the TypeFace object.
-            List<Font> fonts = Typeface_Delegate.getFonts(mTypeface);
+            List<Font> fonts = mTypeface.getFonts();
 
             // create new font objects as well as FontMetrics, based on the current text size
             // and skew info.
@@ -991,6 +1146,10 @@
     }
 
     /*package*/ float measureText(char[] text, int index, int count) {
+
+        // WARNING: the logic in this method is similar to Canvas_Delegate.native_drawText
+        // Any change to this method should be reflected there as well
+
         if (mFonts.size() > 0) {
             FontInfo mainFont = mFonts.get(0);
             int i = index;
@@ -1038,6 +1197,8 @@
                     i += size;
                 }
             }
+
+            return total;
         }
 
         return 0;
diff --git a/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java
index d12bfea..c448f0e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import java.awt.Stroke;
 
@@ -60,10 +61,11 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate(int native_path, float advance, float phase,
             int native_style) {
         PathDashPathEffect_Delegate newDelegate = new PathDashPathEffect_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java
index c588423..bd2b6de 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import java.awt.Stroke;
 
@@ -39,7 +40,7 @@
 
     // ---- delegate manager ----
     protected static final DelegateManager<PathEffect_Delegate> sManager =
-            new DelegateManager<PathEffect_Delegate>();
+            new DelegateManager<PathEffect_Delegate>(PathEffect_Delegate.class);
 
     // ---- delegate helper data ----
 
@@ -58,8 +59,9 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static void nativeDestructor(int native_patheffect) {
-        sManager.removeDelegate(native_patheffect);
+        sManager.removeJavaReferenceFor(native_patheffect);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index a4e43c1..6c9f48f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -19,6 +19,7 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.Path.Direction;
 import android.graphics.Path.FillType;
@@ -49,7 +50,7 @@
 
     // ---- delegate manager ----
     private static final DelegateManager<Path_Delegate> sManager =
-            new DelegateManager<Path_Delegate>();
+            new DelegateManager<Path_Delegate>(Path_Delegate.class);
 
     // ---- delegate data ----
     private FillType mFillType = FillType.WINDING;
@@ -84,13 +85,15 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int init1() {
         // create the delegate
         Path_Delegate newDelegate = new Path_Delegate();
 
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int init2(int nPath) {
         // create the delegate
         Path_Delegate newDelegate = new Path_Delegate();
@@ -101,9 +104,10 @@
             newDelegate.set(pathDelegate);
         }
 
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_reset(int nPath) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -113,12 +117,14 @@
         pathDelegate.mPath.reset();
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_rewind(int nPath) {
         // call out to reset since there's nothing to optimize in
         // terms of data structs.
         native_reset(nPath);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_set(int native_dst, int native_src) {
         Path_Delegate pathDstDelegate = sManager.getDelegate(native_dst);
         if (pathDstDelegate == null) {
@@ -133,6 +139,7 @@
         pathDstDelegate.set(pathSrcDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int native_getFillType(int nPath) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -142,6 +149,7 @@
         return pathDelegate.mFillType.nativeInt;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setFillType(int nPath, int ft) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -151,6 +159,7 @@
         pathDelegate.mFillType = Path.sFillTypeArray[ft];
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_isEmpty(int nPath) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -160,6 +169,7 @@
         return pathDelegate.isEmpty();
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean native_isRect(int nPath, RectF rect) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -179,6 +189,7 @@
         return false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_computeBounds(int nPath, RectF bounds) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -188,11 +199,13 @@
         pathDelegate.fillBounds(bounds);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_incReserve(int nPath, int extraPtCount) {
         // since we use a java2D path, there's no way to pre-allocate new points,
         // so we do nothing.
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_moveTo(int nPath, float x, float y) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -202,6 +215,7 @@
         pathDelegate.moveTo(x, y);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_rMoveTo(int nPath, float dx, float dy) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -211,6 +225,7 @@
         pathDelegate.rMoveTo(dx, dy);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_lineTo(int nPath, float x, float y) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -220,6 +235,7 @@
         pathDelegate.lineTo(x, y);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_rLineTo(int nPath, float dx, float dy) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -229,6 +245,7 @@
         pathDelegate.rLineTo(dx, dy);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_quadTo(int nPath, float x1, float y1, float x2, float y2) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -238,6 +255,7 @@
         pathDelegate.quadTo(x1, y1, x2, y2);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_rQuadTo(int nPath, float dx1, float dy1, float dx2, float dy2) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -247,6 +265,7 @@
         pathDelegate.rQuadTo(dx1, dy1, dx2, dy2);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_cubicTo(int nPath, float x1, float y1,
             float x2, float y2, float x3, float y3) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
@@ -257,6 +276,7 @@
         pathDelegate.cubicTo(x1, y1, x2, y2, x3, y3);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_rCubicTo(int nPath, float x1, float y1,
             float x2, float y2, float x3, float y3) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
@@ -267,6 +287,7 @@
         pathDelegate.rCubicTo(x1, y1, x2, y2, x3, y3);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_arcTo(int nPath, RectF oval,
                     float startAngle, float sweepAngle, boolean forceMoveTo) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
@@ -277,6 +298,7 @@
         pathDelegate.arcTo(oval, startAngle, sweepAngle, forceMoveTo);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_close(int nPath) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -286,6 +308,7 @@
         pathDelegate.close();
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_addRect(int nPath, RectF rect, int dir) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -295,6 +318,7 @@
         pathDelegate.addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_addRect(int nPath,
             float left, float top, float right, float bottom, int dir) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
@@ -305,47 +329,63 @@
         pathDelegate.addRect(left, top, right, bottom, dir);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_addOval(int nPath, RectF oval, int dir) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addOval is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_addCircle(int nPath, float x, float y, float radius, int dir) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addCircle is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_addArc(int nPath, RectF oval,
             float startAngle, float sweepAngle) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addArc is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_addRoundRect(int nPath, RectF rect,
             float rx, float ry, int dir) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addRoundRect is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_addRoundRect(int nPath, RectF r, float[] radii, int dir) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addRoundRect is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_addPath(int nPath, int src, float dx, float dy) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addPath is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_addPath(int nPath, int src) {
         native_addPath(nPath, src, 0, 0);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_addPath(int nPath, int src, int matrix) {
         // FIXME
-        throw new UnsupportedOperationException();
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Path.addPath is not supported.", null, null /*data*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_offset(int nPath, float dx, float dy, int dst_path) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -358,10 +398,12 @@
         pathDelegate.offset(dx, dy, dstDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_offset(int nPath, float dx, float dy) {
         native_offset(nPath, dx, dy, 0);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_setLastPoint(int nPath, float dx, float dy) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -372,6 +414,7 @@
         pathDelegate.mLastY = dy;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_transform(int nPath, int matrix,
                                                 int dst_path) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
@@ -390,12 +433,14 @@
         pathDelegate.transform(matrixDelegate, dstDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void native_transform(int nPath, int matrix) {
         native_transform(nPath, matrix, 0);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void finalizer(int nPath) {
-        sManager.removeDelegate(nPath);
+        sManager.removeJavaReferenceFor(nPath);
     }
 
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java
index 516a2b9..4ab044b 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import java.awt.Composite;
 
@@ -59,9 +60,10 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate(int opColor) {
         PixelXorXfermode_Delegate newDelegate = new PixelXorXfermode_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
index 9038636..c45dbaa 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.PorterDuffColorFilter
@@ -53,11 +54,13 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int native_CreatePorterDuffFilter(int srcColor, int porterDuffMode) {
         PorterDuffColorFilter_Delegate newDelegate = new PorterDuffColorFilter_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nCreatePorterDuffFilter(int nativeFilter, int srcColor,
             int porterDuffMode) {
         // pass
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
index 147e1d0..4301c1a 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
@@ -19,6 +19,7 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import java.awt.AlphaComposite;
 import java.awt.Composite;
@@ -125,9 +126,10 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreateXfermode(int mode) {
         PorterDuffXfermode_Delegate newDelegate = new PorterDuffXfermode_Delegate(mode);
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
index ffdf5dd..9bf78b4 100644
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
@@ -19,6 +19,7 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.Shader.TileMode;
 
@@ -52,25 +53,29 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate1(float x, float y, float radius,
             int colors[], float positions[], int tileMode) {
         RadialGradient_Delegate newDelegate = new RadialGradient_Delegate(x, y, radius,
                 colors, positions, Shader_Delegate.getTileMode(tileMode));
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate2(float x, float y, float radius,
             int color0, int color1, int tileMode) {
         return nativeCreate1(x, y, radius, new int[] { color0, color1 }, null /*positions*/,
                 tileMode);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativePostCreate1(int native_shader, float x, float y, float radius,
             int colors[], float positions[], int tileMode) {
         // nothing to be done here.
         return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativePostCreate2(int native_shader, float x, float y, float radius,
             int color0, int color1, int tileMode) {
         // nothing to be done here.
diff --git a/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java
index 9fd67be..2812b6b 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.Rasterizer
@@ -37,7 +38,7 @@
 
     // ---- delegate manager ----
     protected static final DelegateManager<Rasterizer_Delegate> sManager =
-            new DelegateManager<Rasterizer_Delegate>();
+            new DelegateManager<Rasterizer_Delegate>(Rasterizer_Delegate.class);
 
     // ---- delegate helper data ----
 
@@ -54,8 +55,9 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static void finalizer(int native_instance) {
-        sManager.removeDelegate(native_instance);
+        sManager.removeJavaReferenceFor(native_instance);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java
index d2b6b27..cb31b8f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java
@@ -19,6 +19,7 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.os.Parcel;
 
@@ -47,7 +48,7 @@
 
     // ---- delegate manager ----
     protected static final DelegateManager<Region_Delegate> sManager =
-            new DelegateManager<Region_Delegate>();
+            new DelegateManager<Region_Delegate>(Region_Delegate.class);
 
     // ---- delegate helper data ----
 
@@ -136,6 +137,7 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static boolean isEmpty(Region thisRegion) {
         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
         if (regionDelegate == null) {
@@ -145,6 +147,7 @@
         return regionDelegate.mArea.isEmpty();
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean isRect(Region thisRegion) {
         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
         if (regionDelegate == null) {
@@ -154,6 +157,7 @@
         return regionDelegate.mArea.isRectangular();
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean isComplex(Region thisRegion) {
         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
         if (regionDelegate == null) {
@@ -163,6 +167,7 @@
         return regionDelegate.mArea.isSingular() == false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean contains(Region thisRegion, int x, int y) {
         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
         if (regionDelegate == null) {
@@ -172,6 +177,7 @@
         return regionDelegate.mArea.contains(x, y);
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean quickContains(Region thisRegion,
             int left, int top, int right, int bottom) {
         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
@@ -183,6 +189,7 @@
                 regionDelegate.mArea.contains(left, top, right - left, bottom - top);
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean quickReject(Region thisRegion,
             int left, int top, int right, int bottom) {
         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
@@ -194,6 +201,7 @@
                 regionDelegate.mArea.intersects(left, top, right - left, bottom - top) == false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean quickReject(Region thisRegion, Region rgn) {
         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
         if (regionDelegate == null) {
@@ -211,6 +219,7 @@
 
     }
 
+    @LayoutlibDelegate
     /*package*/ static void translate(Region thisRegion, int dx, int dy, Region dst) {
         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
         if (regionDelegate == null) {
@@ -232,6 +241,7 @@
         }
     }
 
+    @LayoutlibDelegate
     /*package*/ static void scale(Region thisRegion, float scale, Region dst) {
         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
         if (regionDelegate == null) {
@@ -253,15 +263,18 @@
         }
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativeConstructor() {
         Region_Delegate newDelegate = new Region_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeDestructor(int native_region) {
-        sManager.removeDelegate(native_region);
+        sManager.removeJavaReferenceFor(native_region);
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeSetRegion(int native_dst, int native_src) {
         Region_Delegate dstRegion = sManager.getDelegate(native_dst);
         if (dstRegion == null) {
@@ -279,6 +292,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeSetRect(int native_dst,
             int left, int top, int right, int bottom) {
         Region_Delegate dstRegion = sManager.getDelegate(native_dst);
@@ -290,6 +304,7 @@
         return dstRegion.mArea.getBounds().isEmpty() == false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeSetPath(int native_dst, int native_path, int native_clip) {
         Region_Delegate dstRegion = sManager.getDelegate(native_dst);
         if (dstRegion == null) {
@@ -311,6 +326,7 @@
         return dstRegion.mArea.getBounds().isEmpty() == false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeGetBounds(int native_region, Rect rect) {
         Region_Delegate region = sManager.getDelegate(native_region);
         if (region == null) {
@@ -330,6 +346,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeGetBoundaryPath(int native_region, int native_path) {
         Region_Delegate region = sManager.getDelegate(native_region);
         if (region == null) {
@@ -350,6 +367,7 @@
         return true;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeOp(int native_dst,
             int left, int top, int right, int bottom, int op) {
         Region_Delegate region = sManager.getDelegate(native_dst);
@@ -368,6 +386,7 @@
         return region.mArea.getBounds().isEmpty() == false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeOp(int native_dst, Rect rect, int native_region, int op) {
         Region_Delegate region = sManager.getDelegate(native_dst);
         if (region == null) {
@@ -385,6 +404,7 @@
         return region.mArea.getBounds().isEmpty() == false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeOp(int native_dst,
             int native_region1, int native_region2, int op) {
         Region_Delegate dstRegion = sManager.getDelegate(native_dst);
@@ -413,6 +433,7 @@
 
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreateFromParcel(Parcel p) {
         // This is only called by Region.CREATOR (Parcelable.Creator<Region>), which is only
         // used during aidl call so really this should not be called.
@@ -422,6 +443,7 @@
         return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeWriteToParcel(int native_region,
                                                       Parcel p) {
         // This is only called when sending a region through aidl, so really this should not
@@ -432,6 +454,7 @@
         return false;
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean nativeEquals(int native_r1, int native_r2) {
         Region_Delegate region1 = sManager.getDelegate(native_r1);
         if (region1 == null) {
@@ -446,6 +469,7 @@
         return region1.mArea.equals(region2.mArea);
     }
 
+    @LayoutlibDelegate
     /*package*/ static String nativeToString(int native_region) {
         Region_Delegate region = sManager.getDelegate(native_region);
         if (region == null) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
index 7903ac9..368c0384 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.Shader.TileMode;
 
@@ -39,12 +40,12 @@
 
     // ---- delegate manager ----
     protected static final DelegateManager<Shader_Delegate> sManager =
-            new DelegateManager<Shader_Delegate>();
+            new DelegateManager<Shader_Delegate>(Shader_Delegate.class);
 
     // ---- delegate helper data ----
 
     // ---- delegate data ----
-    private int mLocalMatrix = 0;
+    private Matrix_Delegate mLocalMatrix = null;
 
     // ---- Public Helper methods ----
 
@@ -74,36 +75,12 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static void nativeDestructor(int native_shader, int native_skiaShader) {
-        sManager.removeDelegate(native_shader);
+        sManager.removeJavaReferenceFor(native_shader);
     }
 
-    /*package*/ static boolean nativeGetLocalMatrix(int native_shader, int matrix_instance) {
-        // get the delegate from the native int.
-        Shader_Delegate shaderDelegate = sManager.getDelegate(native_shader);
-        if (shaderDelegate == null) {
-            return false;
-        }
-
-        // can be null if shader has no matrix (int is 0)
-        Matrix_Delegate localMatrixDelegate = Matrix_Delegate.getDelegate(
-                shaderDelegate.mLocalMatrix);
-
-        // can be null if the int is 0.
-        Matrix_Delegate destMatrixDelegate = Matrix_Delegate.getDelegate(matrix_instance);
-        if (destMatrixDelegate != null) {
-            if (localMatrixDelegate != null) {
-                destMatrixDelegate.set(localMatrixDelegate);
-            } else {
-                // since there's no local matrix, it's considered to be the identity, reset
-                // the destination matrix
-                destMatrixDelegate.reset();
-            }
-        }
-
-        return localMatrixDelegate == null || localMatrixDelegate.isIdentity();
-    }
-
+    @LayoutlibDelegate
     /*package*/ static void nativeSetLocalMatrix(int native_shader, int native_skiaShader,
             int matrix_instance) {
         // get the delegate from the native int.
@@ -112,15 +89,14 @@
             return;
         }
 
-        shaderDelegate.mLocalMatrix = matrix_instance;
+        shaderDelegate.mLocalMatrix = Matrix_Delegate.getDelegate(matrix_instance);
     }
 
     // ---- Private delegate/helper methods ----
 
     protected java.awt.geom.AffineTransform getLocalMatrix() {
-        Matrix_Delegate localMatrixDelegate = Matrix_Delegate.getDelegate(mLocalMatrix);
-        if (localMatrixDelegate != null) {
-            return localMatrixDelegate.getAffineTransform();
+        if (mLocalMatrix != null) {
+            return mLocalMatrix.getAffineTransform();
         }
 
         return new java.awt.geom.AffineTransform();
diff --git a/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java
index 0f92ca5..410df0c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import java.awt.Stroke;
 
@@ -60,9 +61,10 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate(int first, int second) {
         SumPathEffect_Delegate newDelegate = new SumPathEffect_Delegate();
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
index 048990a..966e06e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
@@ -19,6 +19,7 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.SweepGradient
@@ -50,21 +51,25 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate1(float x, float y, int colors[], float positions[]) {
         SweepGradient_Delegate newDelegate = new SweepGradient_Delegate(x, y, colors, positions);
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativeCreate2(float x, float y, int color0, int color1) {
         return nativeCreate1(x, y, new int[] { color0, color1 }, null /*positions*/);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativePostCreate1(int native_shader, float cx, float cy,
             int[] colors, float[] positions) {
         // nothing to be done here.
         return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativePostCreate2(int native_shader, float cx, float cy,
             int color0, int color1) {
         // nothing to be done here.
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index 00a2a57..0f084f7 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -20,6 +20,7 @@
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.layoutlib.bridge.impl.FontLoader;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.content.res.AssetManager;
 
@@ -45,7 +46,7 @@
 
     // ---- delegate manager ----
     private static final DelegateManager<Typeface_Delegate> sManager =
-            new DelegateManager<Typeface_Delegate>();
+            new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class);
 
     // ---- delegate helper data ----
     private static final String DEFAULT_FAMILY = "sans-serif";
@@ -73,6 +74,10 @@
         sPostInitDelegate.clear();
     }
 
+    public static Typeface_Delegate getDelegate(int nativeTypeface) {
+        return sManager.getDelegate(nativeTypeface);
+    }
+
     public static List<Font> getFonts(Typeface typeface) {
         return getFonts(typeface.native_instance);
     }
@@ -83,11 +88,16 @@
             return null;
         }
 
-        return delegate.mFonts;
+        return delegate.getFonts();
+    }
+
+    public List<Font> getFonts() {
+        return mFonts;
     }
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static synchronized int nativeCreate(String familyName, int style) {
         if (familyName == null) {
             familyName = DEFAULT_FAMILY;
@@ -103,9 +113,10 @@
             sPostInitDelegate.add(newDelegate);
         }
 
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static synchronized int nativeCreateFromTypeface(int native_instance, int style) {
         Typeface_Delegate delegate = sManager.getDelegate(native_instance);
         if (delegate == null) {
@@ -122,25 +133,29 @@
             sPostInitDelegate.add(newDelegate);
         }
 
-        return sManager.addDelegate(newDelegate);
+        return sManager.addNewDelegate(newDelegate);
     }
 
+    @LayoutlibDelegate
     /*package*/ static synchronized int nativeCreateFromAsset(AssetManager mgr, String path) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Typeface.createFromAsset() is not supported.", null /*throwable*/, null /*data*/);
         return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static synchronized int nativeCreateFromFile(String path) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Typeface.createFromFile() is not supported.", null /*throwable*/, null /*data*/);
         return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void nativeUnref(int native_instance) {
-        sManager.removeDelegate(native_instance);
+        sManager.removeJavaReferenceFor(native_instance);
     }
 
+    @LayoutlibDelegate
     /*package*/ static int nativeGetStyle(int native_instance) {
         Typeface_Delegate delegate = sManager.getDelegate(native_instance);
         if (delegate == null) {
@@ -150,6 +165,7 @@
         return delegate.mStyle;
     }
 
+    @LayoutlibDelegate
     /*package*/ static void setGammaForText(float blackGamma, float whiteGamma) {
         // This is for device testing only: pass
     }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java
index 312318a..962d69c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import java.awt.Composite;
 
@@ -39,7 +40,7 @@
 
     // ---- delegate manager ----
     protected static final DelegateManager<Xfermode_Delegate> sManager =
-            new DelegateManager<Xfermode_Delegate>();
+            new DelegateManager<Xfermode_Delegate>(Xfermode_Delegate.class);
 
     // ---- delegate helper data ----
 
@@ -58,8 +59,9 @@
 
     // ---- native methods ----
 
+    @LayoutlibDelegate
     /*package*/ static void finalizer(int native_instance) {
-        sManager.removeDelegate(native_instance);
+        sManager.removeJavaReferenceFor(native_instance);
     }
 
     // ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/os/Build_Delegate.java b/tools/layoutlib/bridge/src/android/os/Build_Delegate.java
index f71860f..ff82a5e 100644
--- a/tools/layoutlib/bridge/src/android/os/Build_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/os/Build_Delegate.java
@@ -18,6 +18,7 @@
 
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import java.util.Map;
 
@@ -33,6 +34,7 @@
  */
 public class Build_Delegate {
 
+    @LayoutlibDelegate
     /*package*/ static String getString(String property) {
         Map<String, String> properties = Bridge.getPlatformProperties();
         String value = properties.get(property);
diff --git a/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java b/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java
index 4d4ec7f4..2152c8a 100644
--- a/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
 
 /**
  * Delegate overriding selected methods of android.os.Handler
@@ -29,6 +31,7 @@
 
     // -------- Delegate methods
 
+    @LayoutlibDelegate
     /*package*/ static boolean sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
         // get the callback
         IHandlerCallback callback = sCallbacks.get();
diff --git a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
index be222fc..63711a7 100644
--- a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.os.SystemClock
@@ -31,6 +32,7 @@
 public class SystemClock_Delegate {
     private static long sBootTime = System.currentTimeMillis();
 
+    @LayoutlibDelegate
     /*package*/ static boolean setCurrentTimeMillis(long millis) {
         return true;
     }
@@ -42,6 +44,7 @@
      *
      * @return milliseconds of non-sleep uptime since boot.
      */
+    @LayoutlibDelegate
     /*package*/ static long uptimeMillis() {
         return System.currentTimeMillis() - sBootTime;
     }
@@ -51,6 +54,7 @@
      *
      * @return elapsed milliseconds since boot.
      */
+    @LayoutlibDelegate
     /*package*/ static long elapsedRealtime() {
         return System.currentTimeMillis() - sBootTime;
     }
@@ -60,6 +64,7 @@
      *
      * @return elapsed milliseconds in the thread
      */
+    @LayoutlibDelegate
     /*package*/ static long currentThreadTimeMillis() {
         return System.currentTimeMillis();
     }
diff --git a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
index 9ca1338..1df78c2 100644
--- a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
@@ -17,6 +17,7 @@
 package android.util;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 /**
  * Delegate implementing the native methods of android.util.FloatMath
@@ -40,6 +41,7 @@
      * @param value to be converted
      * @return the floor of value
      */
+    @LayoutlibDelegate
     /*package*/ static float floor(float value) {
         return (float)Math.floor(value);
     }
@@ -51,6 +53,7 @@
      * @param value to be converted
      * @return the ceiling of value
      */
+    @LayoutlibDelegate
     /*package*/ static float ceil(float value) {
         return (float)Math.ceil(value);
     }
@@ -61,6 +64,7 @@
      * @param angle to compute the cosine of, in radians
      * @return the sine of angle
      */
+    @LayoutlibDelegate
     /*package*/ static  float sin(float angle) {
         return (float)Math.sin(angle);
     }
@@ -71,6 +75,7 @@
      * @param angle to compute the cosine of, in radians
      * @return the cosine of angle
      */
+    @LayoutlibDelegate
     /*package*/ static float cos(float angle) {
         return (float)Math.cos(angle);
     }
@@ -82,6 +87,7 @@
      * @param value to compute sqrt of
      * @return the square root of value
      */
+    @LayoutlibDelegate
     /*package*/ static float sqrt(float value) {
         return (float)Math.sqrt(value);
     }
diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
index 3946a2f5..0f3cf571 100644
--- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import com.android.layoutlib.bridge.android.BridgeInflater;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -38,6 +39,7 @@
      * Recursive method used to descend down the xml hierarchy and instantiate
      * views, instantiate their children, and then call onFinishInflate().
      */
+    @LayoutlibDelegate
     /*package*/ static void rInflate(LayoutInflater thisInflater,
             XmlPullParser parser, View parent, final AttributeSet attrs,
             boolean finishInflate) throws XmlPullParserException, IOException {
diff --git a/tools/layoutlib/bridge/src/android/view/View_Delegate.java b/tools/layoutlib/bridge/src/android/view/View_Delegate.java
index ee6694c..8215f7c 100644
--- a/tools/layoutlib/bridge/src/android/view/View_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/View_Delegate.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
 /**
  * Delegate used to provide new implementation of a select few methods of {@link View}
  *
@@ -25,6 +27,7 @@
  */
 public class View_Delegate {
 
+    @LayoutlibDelegate
     /*package*/ static boolean isInEditMode(View thisView) {
         return true;
     }
diff --git a/tools/layoutlib/bridge/src/com/android/internal/util/XmlUtils_Delegate.java b/tools/layoutlib/bridge/src/com/android/internal/util/XmlUtils_Delegate.java
index 7fa1679..bf998b8 100644
--- a/tools/layoutlib/bridge/src/com/android/internal/util/XmlUtils_Delegate.java
+++ b/tools/layoutlib/bridge/src/com/android/internal/util/XmlUtils_Delegate.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.util;
 
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
 
 /**
  * Delegate used to provide new implementation of a select few methods of {@link XmlUtils}
@@ -25,6 +27,8 @@
  *
  */
 public class XmlUtils_Delegate {
+
+    @LayoutlibDelegate
     /*package*/ static final int convertValueToInt(CharSequence charSeq, int defaultValue) {
         if (null == charSeq)
             return defaultValue;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 90dcc27..acc7379 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -192,6 +192,7 @@
                 Capability.UNBOUND_RENDERING,
                 Capability.CUSTOM_BACKGROUND_COLOR,
                 Capability.RENDER,
+                Capability.LAYOUT_ONLY,
                 Capability.EMBEDDED_LAYOUT,
                 Capability.VIEW_MANIPULATION,
                 Capability.PLAY_ANIMATION,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 33dd2144..7d794bd 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -425,36 +425,72 @@
             }
         }
 
-        if (defStyleValues == null && defStyleAttr != 0) {
-            // get the name from the int.
-            String defStyleName = searchAttr(defStyleAttr);
+        if (defStyleValues == null) {
+            if (defStyleAttr != 0) {
+                // get the name from the int.
+                String defStyleName = searchAttr(defStyleAttr);
 
-            if (defaultPropMap != null) {
-                defaultPropMap.put("style", defStyleName);
-            }
-
-            // look for the style in the current theme, and its parent:
-            ResourceValue item = mRenderResources.findItemInTheme(defStyleName);
-
-            if (item != null) {
-                // item is a reference to a style entry. Search for it.
-                item = mRenderResources.findResValue(item.getValue(),
-                        false /*forceFrameworkOnly*/);
-
-                if (item instanceof StyleResourceValue) {
-                    defStyleValues = (StyleResourceValue)item;
+                if (defaultPropMap != null) {
+                    defaultPropMap.put("style", defStyleName);
                 }
-            } else {
-                Bridge.getLog().error(null,
-                        String.format(
-                                "Failed to find style '%s' in current theme", defStyleName),
-                        null /*data*/);
-            }
-        }
 
-        if (defStyleRes != 0) {
-            // FIXME: See what we need to do with this.
-            throw new UnsupportedOperationException();
+                // look for the style in the current theme, and its parent:
+                ResourceValue item = mRenderResources.findItemInTheme(defStyleName);
+
+                if (item != null) {
+                    // item is a reference to a style entry. Search for it.
+                    item = mRenderResources.findResValue(item.getValue(),
+                            false /*forceFrameworkOnly*/);
+
+                    if (item instanceof StyleResourceValue) {
+                        defStyleValues = (StyleResourceValue)item;
+                    }
+                } else {
+                    Bridge.getLog().error(null,
+                            String.format(
+                                    "Failed to find style '%s' in current theme", defStyleName),
+                            null /*data*/);
+                }
+            } else if (defStyleRes != 0) {
+                Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes);
+                if (value == null) {
+                    value = mProjectCallback.resolveResourceId(defStyleRes);
+                }
+
+                if (value != null) {
+                    if (value.getFirst() == ResourceType.STYLE) {
+                        // look for the style in the current theme, and its parent:
+                        ResourceValue item = mRenderResources.findItemInTheme(value.getSecond());
+                        if (item != null) {
+                            if (item instanceof StyleResourceValue) {
+                                if (defaultPropMap != null) {
+                                    defaultPropMap.put("style", item.getName());
+                                }
+
+                                defStyleValues = (StyleResourceValue)item;
+                            }
+                        } else {
+                            Bridge.getLog().error(null,
+                                    String.format(
+                                            "Style with id 0x%x (resolved to '%s') does not exist.",
+                                            defStyleRes, value.getSecond()),
+                                    null /*data*/);
+                        }
+                    } else {
+                        Bridge.getLog().error(null,
+                                String.format(
+                                        "Resouce id 0x%x is not of type STYLE (instead %s)",
+                                        defStyleRes, value.getFirst().toString()),
+                                null /*data*/);
+                    }
+                } else {
+                    Bridge.getLog().error(null,
+                            String.format(
+                                    "Failed to find style with id 0x%x in current theme",
+                                    defStyleRes),
+                            null /*data*/);
+                }
+            }
         }
 
         String namespace = BridgeConstants.NS_RESOURCES;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
index 734f5ad..c1d7600 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
@@ -188,6 +188,10 @@
 
         String s = mResourceData[index].getValue();
 
+        if (RenderResources.REFERENCE_NULL.equals(s)) {
+            return defValue;
+        }
+
         try {
             return (s == null) ? defValue : XmlUtils.convertValueToInt(s, defValue);
         } catch (NumberFormatException e) {
@@ -301,6 +305,10 @@
             return null;
         }
 
+        if (RenderResources.REFERENCE_NULL.equals(value)) {
+            return null;
+        }
+
         // let the framework inflate the ColorStateList from the XML file.
         File f = new File(value);
         if (f.isFile()) {
@@ -337,8 +345,6 @@
             Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e, null /*data*/);
         }
 
-        assert false;
-
         return null;
     }
 
@@ -408,6 +414,10 @@
             return LayoutParams.WRAP_CONTENT;
         }
 
+        if (RenderResources.REFERENCE_NULL.equals(s)) {
+            return defValue;
+        }
+
         if (ResourceHelper.stringToFloat(s, mValue)) {
             return mValue.getDimension(mBridgeResources.mMetrics);
         }
@@ -418,8 +428,6 @@
                     "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
                     s, mNames[index]), null /*data*/);
 
-        assert false;
-
         return defValue;
     }
 
@@ -480,6 +488,10 @@
             return LayoutParams.WRAP_CONTENT;
         }
 
+        if (RenderResources.REFERENCE_NULL.equals(s)) {
+            return defValue;
+        }
+
         // FIXME huh?
 
         float f = getDimension(index, defValue);
@@ -488,8 +500,11 @@
         if (f == 0) return 0;
         if (f > 0) return 1;
 
-        throw new UnsupportedOperationException("Can't convert to dimension: " +
-                Integer.toString(index));
+        Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                "Can't convert to dimension: " + Integer.toString(index),
+                null, null /*data*/);
+
+        return defValue;
     }
 
     /**
@@ -550,8 +565,6 @@
                     "\"%1$s\" in attribute \"%2$s\" cannont be converted to a fraction.",
                     value, mNames[index]), null /*data*/);
 
-        assert false;
-
         return defValue;
     }
 
@@ -585,6 +598,10 @@
             return mContext.getDynamicIdByStyle((StyleResourceValue)resValue);
         }
 
+        if (RenderResources.REFERENCE_NULL.equals(resValue.getValue())) {
+            return defValue;
+        }
+
         // if the attribute was a reference to a resource, and not a declaration of an id (@+id),
         // then the xml attribute value was "resolved" which leads us to a ResourceValue with a
         // valid getType() and getName() returning a resource name.
@@ -663,8 +680,6 @@
                     "Unable to resolve id \"%1$s\" for attribute \"%2$s\"", value, mNames[index]),
                     resValue);
 
-        assert false;
-
         return defValue;
     }
 
@@ -712,6 +727,10 @@
 
         String value = mResourceData[index].getValue();
         if (value != null) {
+            if (RenderResources.REFERENCE_NULL.equals(value)) {
+                return null;
+            }
+
             return new CharSequence[] { value };
         }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java
index 04d06e4b..9fab51a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java
@@ -30,7 +30,7 @@
 public class PhoneSystemBar extends CustomBar {
 
     public PhoneSystemBar(Context context, Density density) throws XmlPullParserException {
-        super(context, density, "/bars/tablet_system_bar.xml");
+        super(context, density, "/bars/phone_system_bar.xml");
 
         setGravity(mGravity | Gravity.RIGHT);
         setBackgroundColor(0xFF000000);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
index 05a258d..ae1217d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
@@ -16,8 +16,15 @@
 
 package com.android.layoutlib.bridge.impl;
 
+import com.android.layoutlib.bridge.util.Debug;
+import com.android.layoutlib.bridge.util.SparseWeakArray;
+
 import android.util.SparseArray;
 
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Manages native delegates.
  *
@@ -44,15 +51,39 @@
  * will do is call {@link #getDelegate(int)} to get the Java object matching the int.
  *
  * Typical native init methods are returning a new int back to the Java class, so
- * {@link #addDelegate(Object)} does the same.
+ * {@link #addNewDelegate(Object)} does the same.
+ *
+ * The JNI references are counted, so we do the same through a {@link WeakReference}. Because
+ * the Java object needs to count as a reference (even though it only holds an int), we use the
+ * following mechanism:
+ *
+ * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(int)} adds and removes
+ *   the delegate to/from a list. This list hold the reference and prevents the GC from reclaiming
+ *   the delegate.
+ *
+ * - {@link #addNewDelegate(Object)} also adds the delegate to a {@link SparseArray} that holds a
+ *   {@link WeakReference} to the delegate. This allows the delegate to be deleted automatically
+ *   when nothing references it. This means that any class that holds a delegate (except for the
+ *   Java main class) must not use the int but the Delegate class instead. The integers must
+ *   only be used in the API between the main Java class and the Delegate.
  *
  * @param <T> the delegate class to manage
  */
 public final class DelegateManager<T> {
-
-    private final SparseArray<T> mDelegates = new SparseArray<T>();
+    private final Class<T> mClass;
+    private final SparseWeakArray<T> mDelegates = new SparseWeakArray<T>();
+    /** list used to store delegates when their main object holds a reference to them.
+     * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed
+     * @see #addNewDelegate(Object)
+     * @see #removeJavaReferenceFor(int)
+     */
+    private final List<T> mJavaReferences = new ArrayList<T>();
     private int mDelegateCounter = 0;
 
+    public DelegateManager(Class<T> theClass) {
+        mClass = theClass;
+    }
+
     /**
      * Returns the delegate from the given native int.
      * <p>
@@ -66,6 +97,14 @@
     public T getDelegate(int native_object) {
         if (native_object > 0) {
             T delegate =  mDelegates.get(native_object);
+
+            if (Debug.DEBUG) {
+                if (delegate == null) {
+                    System.out.println("Unknown " + mClass.getSimpleName() + " with int " +
+                            native_object);
+                }
+            }
+
             assert delegate != null;
             return delegate;
         }
@@ -77,17 +116,31 @@
      * @param newDelegate the delegate to add
      * @return a unique native int to identify the delegate
      */
-    public int addDelegate(T newDelegate) {
+    public int addNewDelegate(T newDelegate) {
         int native_object = ++mDelegateCounter;
         mDelegates.put(native_object, newDelegate);
+        assert !mJavaReferences.contains(newDelegate);
+        mJavaReferences.add(newDelegate);
+
+        if (Debug.DEBUG) {
+            System.out.println("New " + mClass.getSimpleName() + " with int " + native_object);
+        }
+
         return native_object;
     }
 
     /**
-     * Removes the delegate matching the given native int.
-     * @param native_object the native int.
+     * Removes the main reference on the given delegate.
+     * @param native_object the native integer representing the delegate.
      */
-    public void removeDelegate(int native_object) {
-        mDelegates.remove(native_object);
+    public void removeJavaReferenceFor(int native_object) {
+        T delegate = getDelegate(native_object);
+
+        if (Debug.DEBUG) {
+            System.out.println("Removing main Java ref on " + mClass.getSimpleName() +
+                    " with int " + native_object);
+        }
+
+        mJavaReferences.remove(delegate);
     }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index fedd789f..2fd58e4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -421,57 +421,63 @@
             // now do the layout.
             mViewRoot.layout(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
 
-            mViewRoot.mAttachInfo.mTreeObserver.dispatchOnPreDraw();
+            if (params.isLayoutOnly()) {
+                // delete the canvas and image to reset them on the next full rendering
+                mImage = null;
+                mCanvas = null;
+            } else {
+                mViewRoot.mAttachInfo.mTreeObserver.dispatchOnPreDraw();
 
-            // draw the views
-            // create the BufferedImage into which the layout will be rendered.
-            boolean newImage = false;
-            if (newRenderSize || mCanvas == null) {
-                if (params.getImageFactory() != null) {
-                    mImage = params.getImageFactory().getImage(
-                            mMeasuredScreenWidth,
-                            mMeasuredScreenHeight);
-                } else {
-                    mImage = new BufferedImage(
-                            mMeasuredScreenWidth,
-                            mMeasuredScreenHeight,
-                            BufferedImage.TYPE_INT_ARGB);
-                    newImage = true;
+                // draw the views
+                // create the BufferedImage into which the layout will be rendered.
+                boolean newImage = false;
+                if (newRenderSize || mCanvas == null) {
+                    if (params.getImageFactory() != null) {
+                        mImage = params.getImageFactory().getImage(
+                                mMeasuredScreenWidth,
+                                mMeasuredScreenHeight);
+                    } else {
+                        mImage = new BufferedImage(
+                                mMeasuredScreenWidth,
+                                mMeasuredScreenHeight,
+                                BufferedImage.TYPE_INT_ARGB);
+                        newImage = true;
+                    }
+
+                    if (params.isBgColorOverridden()) {
+                        // since we override the content, it's the same as if it was a new image.
+                        newImage = true;
+                        Graphics2D gc = mImage.createGraphics();
+                        gc.setColor(new Color(params.getOverrideBgColor(), true));
+                        gc.setComposite(AlphaComposite.Src);
+                        gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
+                        gc.dispose();
+                    }
+
+                    // create an Android bitmap around the BufferedImage
+                    Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
+                            true /*isMutable*/, params.getDensity());
+
+                    // create a Canvas around the Android bitmap
+                    mCanvas = new Canvas(bitmap);
+                    mCanvas.setDensity(params.getDensity().getDpiValue());
                 }
 
-                if (params.isBgColorOverridden()) {
-                    // since we override the content, it's the same as if it was a new image.
-                    newImage = true;
+                if (freshRender && newImage == false) {
                     Graphics2D gc = mImage.createGraphics();
-                    gc.setColor(new Color(params.getOverrideBgColor(), true));
                     gc.setComposite(AlphaComposite.Src);
-                    gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
+
+                    gc.setColor(new Color(0x00000000, true));
+                    gc.fillRect(0, 0,
+                            mMeasuredScreenWidth, mMeasuredScreenHeight);
+
+                    // done
                     gc.dispose();
                 }
 
-                // create an Android bitmap around the BufferedImage
-                Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
-                        true /*isMutable*/, params.getDensity());
-
-                // create a Canvas around the Android bitmap
-                mCanvas = new Canvas(bitmap);
-                mCanvas.setDensity(params.getDensity().getDpiValue());
+                mViewRoot.draw(mCanvas);
             }
 
-            if (freshRender && newImage == false) {
-                Graphics2D gc = mImage.createGraphics();
-                gc.setComposite(AlphaComposite.Src);
-
-                gc.setColor(new Color(0x00000000, true));
-                gc.fillRect(0, 0,
-                        mMeasuredScreenWidth, mMeasuredScreenHeight);
-
-                // done
-                gc.dispose();
-            }
-
-            mViewRoot.draw(mCanvas);
-
             mViewInfoList = startVisitingViews(mViewRoot, 0);
 
             // success!
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/Debug.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/Debug.java
new file mode 100644
index 0000000..82eab85
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/Debug.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ * 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.layoutlib.bridge.util;
+
+public class Debug {
+
+    public final static boolean DEBUG = false;
+
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java
new file mode 100644
index 0000000..4d0c9ce
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java
@@ -0,0 +1,376 @@
+/*
+ * 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.
+ * 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.layoutlib.bridge.util;
+
+
+import com.android.internal.util.ArrayUtils;
+
+import android.util.SparseArray;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This is a custom {@link SparseArray} that uses {@link WeakReference} around the objects added
+ * to it. When the array is compacted, not only deleted indices but also empty references
+ * are removed, making the array efficient at removing references that were reclaimed.
+ *
+ * The code is taken from {@link SparseArray} directly and adapted to use weak references.
+ *
+ * Because our usage means that we never actually call {@link #remove(int)} or {@link #delete(int)},
+ * we must manually check if there are reclaimed references to trigger an internal compact step
+ * (which is normally only triggered when an item is manually removed).
+ *
+ * SparseArrays map integers to Objects.  Unlike a normal array of Objects,
+ * there can be gaps in the indices.  It is intended to be more efficient
+ * than using a HashMap to map Integers to Objects.
+ */
+@SuppressWarnings("unchecked")
+public class SparseWeakArray<E> {
+
+    private static final Object DELETED_REF = new Object();
+    private static final WeakReference<?> DELETED = new WeakReference(DELETED_REF);
+    private boolean mGarbage = false;
+
+    /**
+     * Creates a new SparseArray containing no mappings.
+     */
+    public SparseWeakArray() {
+        this(10);
+    }
+
+    /**
+     * Creates a new SparseArray containing no mappings that will not
+     * require any additional memory allocation to store the specified
+     * number of mappings.
+     */
+    public SparseWeakArray(int initialCapacity) {
+        initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
+
+        mKeys = new int[initialCapacity];
+        mValues = new WeakReference[initialCapacity];
+        mSize = 0;
+    }
+
+    /**
+     * Gets the Object mapped from the specified key, or <code>null</code>
+     * if no such mapping has been made.
+     */
+    public E get(int key) {
+        return get(key, null);
+    }
+
+    /**
+     * Gets the Object mapped from the specified key, or the specified Object
+     * if no such mapping has been made.
+     */
+    public E get(int key, E valueIfKeyNotFound) {
+        int i = binarySearch(mKeys, 0, mSize, key);
+
+        if (i < 0 || mValues[i] == DELETED || mValues[i].get() == null) {
+            return valueIfKeyNotFound;
+        } else {
+            return (E) mValues[i].get();
+        }
+    }
+
+    /**
+     * Removes the mapping from the specified key, if there was any.
+     */
+    public void delete(int key) {
+        int i = binarySearch(mKeys, 0, mSize, key);
+
+        if (i >= 0) {
+            if (mValues[i] != DELETED) {
+                mValues[i] = DELETED;
+                mGarbage = true;
+            }
+        }
+    }
+
+    /**
+     * Alias for {@link #delete(int)}.
+     */
+    public void remove(int key) {
+        delete(key);
+    }
+
+    /**
+     * Removes the mapping at the specified index.
+     */
+    public void removeAt(int index) {
+        if (mValues[index] != DELETED) {
+            mValues[index] = DELETED;
+            mGarbage = true;
+        }
+    }
+
+    private void gc() {
+        int n = mSize;
+        int o = 0;
+        int[] keys = mKeys;
+        WeakReference<?>[] values = mValues;
+
+        for (int i = 0; i < n; i++) {
+            WeakReference<?> val = values[i];
+
+            // Don't keep any non DELETED values, but only the one that still have a valid
+            // reference.
+            if (val != DELETED && val.get() != null) {
+                if (i != o) {
+                    keys[o] = keys[i];
+                    values[o] = val;
+                }
+
+                o++;
+            }
+        }
+
+        mGarbage = false;
+        mSize = o;
+
+        int newSize = ArrayUtils.idealIntArraySize(mSize);
+        if (newSize < mKeys.length) {
+            int[] nkeys = new int[newSize];
+            WeakReference<?>[] nvalues = new WeakReference[newSize];
+
+            System.arraycopy(mKeys, 0, nkeys, 0, newSize);
+            System.arraycopy(mValues, 0, nvalues, 0, newSize);
+
+            mKeys = nkeys;
+            mValues = nvalues;
+        }
+    }
+
+    /**
+     * Adds a mapping from the specified key to the specified value,
+     * replacing the previous mapping from the specified key if there
+     * was one.
+     */
+    public void put(int key, E value) {
+        int i = binarySearch(mKeys, 0, mSize, key);
+
+        if (i >= 0) {
+            mValues[i] = new WeakReference(value);
+        } else {
+            i = ~i;
+
+            if (i < mSize && (mValues[i] == DELETED || mValues[i].get() == null)) {
+                mKeys[i] = key;
+                mValues[i] = new WeakReference(value);
+                return;
+            }
+
+            if (mSize >= mKeys.length && (mGarbage || hasReclaimedRefs())) {
+                gc();
+
+                // Search again because indices may have changed.
+                i = ~binarySearch(mKeys, 0, mSize, key);
+            }
+
+            if (mSize >= mKeys.length) {
+                int n = ArrayUtils.idealIntArraySize(mSize + 1);
+
+                int[] nkeys = new int[n];
+                WeakReference<?>[] nvalues = new WeakReference[n];
+
+                // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+                System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+                System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+                mKeys = nkeys;
+                mValues = nvalues;
+            }
+
+            if (mSize - i != 0) {
+                // Log.e("SparseArray", "move " + (mSize - i));
+                System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+                System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+            }
+
+            mKeys[i] = key;
+            mValues[i] = new WeakReference(value);
+            mSize++;
+        }
+    }
+
+    /**
+     * Returns the number of key-value mappings that this SparseArray
+     * currently stores.
+     */
+    public int size() {
+        if (mGarbage) {
+            gc();
+        }
+
+        return mSize;
+    }
+
+    /**
+     * Given an index in the range <code>0...size()-1</code>, returns
+     * the key from the <code>index</code>th key-value mapping that this
+     * SparseArray stores.
+     */
+    public int keyAt(int index) {
+        if (mGarbage) {
+            gc();
+        }
+
+        return mKeys[index];
+    }
+
+    /**
+     * Given an index in the range <code>0...size()-1</code>, returns
+     * the value from the <code>index</code>th key-value mapping that this
+     * SparseArray stores.
+     */
+    public E valueAt(int index) {
+        if (mGarbage) {
+            gc();
+        }
+
+        return (E) mValues[index].get();
+    }
+
+    /**
+     * Given an index in the range <code>0...size()-1</code>, sets a new
+     * value for the <code>index</code>th key-value mapping that this
+     * SparseArray stores.
+     */
+    public void setValueAt(int index, E value) {
+        if (mGarbage) {
+            gc();
+        }
+
+        mValues[index] = new WeakReference(value);
+    }
+
+    /**
+     * Returns the index for which {@link #keyAt} would return the
+     * specified key, or a negative number if the specified
+     * key is not mapped.
+     */
+    public int indexOfKey(int key) {
+        if (mGarbage) {
+            gc();
+        }
+
+        return binarySearch(mKeys, 0, mSize, key);
+    }
+
+    /**
+     * Returns an index for which {@link #valueAt} would return the
+     * specified key, or a negative number if no keys map to the
+     * specified value.
+     * Beware that this is a linear search, unlike lookups by key,
+     * and that multiple keys can map to the same value and this will
+     * find only one of them.
+     */
+    public int indexOfValue(E value) {
+        if (mGarbage) {
+            gc();
+        }
+
+        for (int i = 0; i < mSize; i++)
+            if (mValues[i].get() == value)
+                return i;
+
+        return -1;
+    }
+
+    /**
+     * Removes all key-value mappings from this SparseArray.
+     */
+    public void clear() {
+        int n = mSize;
+        WeakReference<?>[] values = mValues;
+
+        for (int i = 0; i < n; i++) {
+            values[i] = null;
+        }
+
+        mSize = 0;
+        mGarbage = false;
+    }
+
+    /**
+     * Puts a key/value pair into the array, optimizing for the case where
+     * the key is greater than all existing keys in the array.
+     */
+    public void append(int key, E value) {
+        if (mSize != 0 && key <= mKeys[mSize - 1]) {
+            put(key, value);
+            return;
+        }
+
+        if (mSize >= mKeys.length && (mGarbage || hasReclaimedRefs())) {
+            gc();
+        }
+
+        int pos = mSize;
+        if (pos >= mKeys.length) {
+            int n = ArrayUtils.idealIntArraySize(pos + 1);
+
+            int[] nkeys = new int[n];
+            WeakReference<?>[] nvalues = new WeakReference[n];
+
+            // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+            System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+            System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+            mKeys = nkeys;
+            mValues = nvalues;
+        }
+
+        mKeys[pos] = key;
+        mValues[pos] = new WeakReference(value);
+        mSize = pos + 1;
+    }
+
+    private boolean hasReclaimedRefs() {
+        for (int i = 0 ; i < mSize ; i++) {
+            if (mValues[i].get() == null) { // DELETED.get() never returns null.
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private static int binarySearch(int[] a, int start, int len, int key) {
+        int high = start + len, low = start - 1, guess;
+
+        while (high - low > 1) {
+            guess = (high + low) / 2;
+
+            if (a[guess] < key)
+                low = guess;
+            else
+                high = guess;
+        }
+
+        if (high == start + len)
+            return ~(start + len);
+        else if (a[high] == key)
+            return high;
+        else
+            return ~high;
+    }
+
+    private int[] mKeys;
+    private WeakReference<?>[] mValues;
+    private int mSize;
+}
diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
index ab01a394..e6dc646 100644
--- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
+++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
@@ -16,6 +16,8 @@
 
 package libcore.icu;
 
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
 import java.util.Locale;
 
 /**
@@ -27,80 +29,101 @@
  */
 public class ICU_Delegate {
 
+    // --- Java delegates
+
+    @LayoutlibDelegate
     /*package*/ static String toLowerCase(String s, String localeName) {
         return s.toLowerCase();
     }
 
+    @LayoutlibDelegate
     /*package*/ static String toUpperCase(String s, String localeName) {
         return s.toUpperCase();
     }
 
     // --- Native methods accessing ICU's database.
 
+    @LayoutlibDelegate
     /*package*/ static String[] getAvailableBreakIteratorLocalesNative() {
         return new String[0];
     }
 
+    @LayoutlibDelegate
     /*package*/ static String[] getAvailableCalendarLocalesNative() {
         return new String[0];
     }
 
+    @LayoutlibDelegate
     /*package*/ static String[] getAvailableCollatorLocalesNative() {
         return new String[0];
     }
 
+    @LayoutlibDelegate
     /*package*/ static String[] getAvailableDateFormatLocalesNative() {
         return new String[0];
     }
 
+    @LayoutlibDelegate
     /*package*/ static String[] getAvailableLocalesNative() {
         return new String[0];
     }
 
+    @LayoutlibDelegate
     /*package*/ static String[] getAvailableNumberFormatLocalesNative() {
         return new String[0];
     }
 
+    @LayoutlibDelegate
     /*package*/ static String getCurrencyCodeNative(String locale) {
         return "";
     }
 
+    @LayoutlibDelegate
     /*package*/ static int getCurrencyFractionDigitsNative(String currencyCode) {
         return 0;
     }
 
+    @LayoutlibDelegate
     /*package*/ static String getCurrencySymbolNative(String locale, String currencyCode) {
         return "";
     }
 
+    @LayoutlibDelegate
     /*package*/ static String getDisplayCountryNative(String countryCode, String locale) {
         return "";
     }
 
+    @LayoutlibDelegate
     /*package*/ static String getDisplayLanguageNative(String languageCode, String locale) {
         return "";
     }
 
+    @LayoutlibDelegate
     /*package*/ static String getDisplayVariantNative(String variantCode, String locale) {
         return "";
     }
 
+    @LayoutlibDelegate
     /*package*/ static String getISO3CountryNative(String locale) {
         return "";
     }
 
+    @LayoutlibDelegate
     /*package*/ static String getISO3LanguageNative(String locale) {
         return "";
     }
 
+    @LayoutlibDelegate
     /*package*/ static String[] getISOLanguagesNative() {
         return Locale.getISOLanguages();
     }
 
+    @LayoutlibDelegate
     /*package*/ static String[] getISOCountriesNative() {
         return Locale.getISOCountries();
     }
 
+    @LayoutlibDelegate
     /*package*/ static boolean initLocaleDataImpl(String locale, LocaleData result) {
 
         // Used by Calendar.
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
deleted file mode 100644
index a068ae2..0000000
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
+++ /dev/null
@@ -1,175 +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.
- */
-
-package com.android.layoutlib.bridge;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Type;
-
-import junit.framework.TestCase;
-
-public class TestClassReplacement extends TestCase {
-
-    public void testClassReplacements() {
-        // TODO: we want to test all the classes. For now only, no classes pass the test.
-//        final String[] classes = CreateInfo.RENAMED_CLASSES;
-        final String[] classes = new String[] {
-//                "android.graphics.Paint",               "android.graphics._Original_Paint",
-//                "android.graphics.Canvas",               "android.graphics._Original_Canvas",
-        };
-        final int count = classes.length;
-        for (int i = 0 ; i < count ; i += 2) {
-            loadAndCompareClasses(classes[i], classes[i+1]);
-        }
-    }
-
-    private void loadAndCompareClasses(String newClassName, String oldClassName) {
-        // load the classes
-        try {
-            Class<?> newClass = TestClassReplacement.class.getClassLoader().loadClass(newClassName);
-            Class<?> oldClass = TestClassReplacement.class.getClassLoader().loadClass(oldClassName);
-
-            compare(newClass, oldClass);
-        } catch (ClassNotFoundException e) {
-            fail("Failed to load class: " + e.getMessage());
-        }
-    }
-
-    private void compare(Class<?> newClass, Class<?> oldClass) {
-        // first compare the methods.
-        Method[] newClassMethods = newClass.getDeclaredMethods();
-        Method[] oldClassMethods = oldClass.getDeclaredMethods();
-
-        for (Method oldMethod : oldClassMethods) {
-            // we ignore anything that starts with native. This is because the class we are looking
-            // at has already been modified to remove the native modifiers.
-            if (oldMethod.getName().startsWith("native")) {
-                continue;
-            }
-
-            // or static and private
-            int privateStatic = Modifier.STATIC | Modifier.PRIVATE;
-            if ((oldMethod.getModifiers() & privateStatic) == privateStatic) {
-                continue;
-            }
-
-            boolean found = false;
-            for (Method newMethod : newClassMethods) {
-
-                if (compareMethods(newClass, newMethod, oldClass, oldMethod)) {
-                    found = true;
-                    break;
-                }
-            }
-
-            if (found == false) {
-                // compute a full class name that's long but not too long.
-                StringBuilder sb = new StringBuilder(oldMethod.getName() + "(");
-                Type[] params = oldMethod.getGenericParameterTypes();
-                for (int j = 0; j < params.length; j++) {
-                    if (params[j] instanceof Class) {
-                        Class theClass = (Class)params[j];
-                        sb.append(theClass.getName());
-                        int dimensions = 0;
-                        while (theClass.isArray()) {
-                            dimensions++;
-                            theClass = theClass.getComponentType();
-                        }
-                        for (int i = 0; i < dimensions; i++) {
-                            sb.append("[]");
-                        }
-
-                    } else {
-                        sb.append(params[j].toString());
-                    }
-                if (j < (params.length - 1))
-                    sb.append(",");
-                }
-                sb.append(")");
-
-                fail(String.format("Missing %1$s.%2$s", newClass.getName(), sb.toString()));
-            }
-        }
-
-        // TODO: check (somehow?) that the methods that were removed from the original class
-        // have been put back in the new class!
-        // For this we need the original unmodified class (ie renamed, but w/o the methods removed)
-    }
-
-    private boolean compareMethods(Class<?> newClass, Method newMethod,
-            Class<?> oldClass, Method oldMethod) {
-        // first check the name of the method
-        if (newMethod.getName().equals(oldMethod.getName()) == false) {
-            return false;
-        }
-
-        // check the return value
-        Class<?> oldReturnType = oldMethod.getReturnType();
-        // if it's the old class, or if it's a inner class of the oldclass, we need to change this.
-        oldReturnType = adapt(oldReturnType, newClass, oldClass);
-
-        // compare the return types
-        Class<?> newReturnType = newMethod.getReturnType();
-        if (newReturnType.equals(oldReturnType) == false) {
-            return false;
-        }
-
-        // now check the parameters type.
-        Class<?>[] oldParameters = oldMethod.getParameterTypes();
-        Class<?>[] newParemeters = newMethod.getParameterTypes();
-        if (oldParameters.length != newParemeters.length) {
-            return false;
-        }
-
-        for (int i = 0 ; i < oldParameters.length ; i++) {
-            if (newParemeters[i].equals(adapt(oldParameters[i], newClass, oldClass)) == false) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Adapts a class to deal with renamed classes.
-     * <p/>For instance if old class is <code>android.graphics._Original_Paint</code> and the
-     * new class is <code>android.graphics.Paint</code> and the class to adapt is
-     * <code>android.graphics._Original_Paint$Cap</code>, then the method will return a
-     * {@link Class} object representing <code>android.graphics.Paint$Cap</code>.
-     * <p/>
-     * This method will also ensure that all renamed classes contains all the proper inner classes
-     * that they should be declaring.
-     * @param theClass the class to adapt
-     * @param newClass the new class object
-     * @param oldClass the old class object
-     * @return the adapted class.
-     * @throws ClassNotFoundException
-     */
-    private Class<?> adapt(Class<?> theClass, Class<?> newClass, Class<?> oldClass) {
-        // only look for a new class if it's not primitive as Class.forName() would fail otherwise.
-        if (theClass.isPrimitive() == false) {
-            String n = theClass.getName().replace(oldClass.getName(), newClass.getName());
-            try {
-                return Class.forName(n);
-            } catch (ClassNotFoundException e) {
-                fail("Missing class: " + n);
-            }
-        }
-
-        return theClass;
-    }
-}
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java
index a4140e3..d3218db 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java
@@ -21,6 +21,8 @@
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
 
 import junit.framework.TestCase;
 
@@ -78,10 +80,15 @@
     }
 
     private void compare(Class<?> originalClass, Class<?> delegateClass) throws SecurityException {
-        Method[] originalMethods = originalClass.getDeclaredMethods();
+        List<Method> checkedDelegateMethods = new ArrayList<Method>();
 
+        // loop on the methods of the original class, and for the ones that are annotated
+        // with @LayoutlibDelegate, look for a matching method in the delegate class.
+        // The annotation is automatically added by layoutlib_create when it replace a method
+        // by a call to a delegate
+        Method[] originalMethods = originalClass.getDeclaredMethods();
         for (Method originalMethod : originalMethods) {
-            // look for methods that were native: they have the LayoutlibDelegate annotation
+            // look for methods that are delegated: they have the LayoutlibDelegate annotation
             if (originalMethod.getAnnotation(LayoutlibDelegate.class) == null) {
                 continue;
             }
@@ -114,6 +121,14 @@
                 Method delegateMethod = delegateClass.getDeclaredMethod(originalMethod.getName(),
                         parameters);
 
+                // check that the method has the annotation
+                assertNotNull(
+                        String.format(
+                                "Delegate method %1$s for class %2$s does not have the @LayoutlibDelegate annotation",
+                                delegateMethod.getName(),
+                                originalClass.getName()),
+                        delegateMethod.getAnnotation(LayoutlibDelegate.class));
+
                 // check that the method is static
                 assertTrue(
                         String.format(
@@ -121,28 +136,62 @@
                                 delegateMethod.getName(),
                                 originalClass.getName()),
                         (delegateMethod.getModifiers() & Modifier.STATIC) == Modifier.STATIC);
-            } catch (NoSuchMethodException e) {
-                // compute a full class name that's long but not too long.
-                StringBuilder sb = new StringBuilder(originalMethod.getName() + "(");
-                for (int j = 0; j < parameters.length; j++) {
-                    Class<?> theClass = parameters[j];
-                    sb.append(theClass.getName());
-                    int dimensions = 0;
-                    while (theClass.isArray()) {
-                        dimensions++;
-                        theClass = theClass.getComponentType();
-                    }
-                    for (int i = 0; i < dimensions; i++) {
-                        sb.append("[]");
-                    }
-                    if (j < (parameters.length - 1)) {
-                        sb.append(",");
-                    }
-                }
-                sb.append(")");
 
-                fail(String.format("Missing %1$s.%2$s", delegateClass.getName(), sb.toString()));
+                // add the method as checked.
+                checkedDelegateMethods.add(delegateMethod);
+            } catch (NoSuchMethodException e) {
+                String name = getMethodName(originalMethod, parameters);
+                fail(String.format("Missing %1$s.%2$s", delegateClass.getName(), name));
             }
         }
+
+        // look for dead (delegate) code.
+        // This looks for all methods in the delegate class, and if they have the
+        // @LayoutlibDelegate annotation, make sure they have been previously found as a
+        // match for a method in the original class.
+        // If not, this means the method is a delegate for a method that either doesn't exist
+        // anymore or is not delegated anymore.
+        Method[] delegateMethods = delegateClass.getDeclaredMethods();
+        for (Method delegateMethod : delegateMethods) {
+            // look for methods that are delegates: they have the LayoutlibDelegate annotation
+            if (delegateMethod.getAnnotation(LayoutlibDelegate.class) == null) {
+                continue;
+            }
+
+            assertTrue(
+                    String.format(
+                            "Delegate method %1$s.%2$s is not used anymore and must be removed",
+                            delegateClass.getName(),
+                            getMethodName(delegateMethod)),
+                    checkedDelegateMethods.contains(delegateMethod));
+        }
+
+    }
+
+    private String getMethodName(Method method) {
+        return getMethodName(method, method.getParameterTypes());
+    }
+
+    private String getMethodName(Method method, Class<?>[] parameters) {
+        // compute a full class name that's long but not too long.
+        StringBuilder sb = new StringBuilder(method.getName() + "(");
+        for (int j = 0; j < parameters.length; j++) {
+            Class<?> theClass = parameters[j];
+            sb.append(theClass.getName());
+            int dimensions = 0;
+            while (theClass.isArray()) {
+                dimensions++;
+                theClass = theClass.getComponentType();
+            }
+            for (int i = 0; i < dimensions; i++) {
+                sb.append("[]");
+            }
+            if (j < (parameters.length - 1)) {
+                sb.append(",");
+            }
+        }
+        sb.append(")");
+
+        return sb.toString();
     }
 }