am 3d7a9707: Merge "Fix 3210299: Extend lock screen wake timeout to 10s" into honeycomb

* commit '3d7a9707a58bb043147f30dc81c61deeeffeeef7':
  Fix 3210299: Extend lock screen wake timeout to 10s
diff --git a/NOTICE b/NOTICE
index d857ba3..152be20 100644
--- a/NOTICE
+++ b/NOTICE
@@ -72,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/api/current.xml b/api/current.xml
index 1309201..6387ef9 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -118641,6 +118641,36 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<method name="disableForegroundDispatch"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+</method>
+<method name="enableForegroundDispatch"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="intent" type="android.app.PendingIntent">
+</parameter>
+<parameter name="filters" type="android.content.IntentFilter...">
+</parameter>
+</method>
 <method name="getDefaultAdapter"
  return="android.nfc.NfcAdapter"
  abstract="false"
@@ -118676,6 +118706,17 @@
  visibility="public"
 >
 </method>
+<field name="ACTION_NDEF_DISCOVERED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.nfc.action.NDEF_DISCOVERED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTION_TAG_DISCOVERED"
  type="java.lang.String"
  transient="false"
@@ -118687,6 +118728,17 @@
  visibility="public"
 >
 </field>
+<field name="ACTION_TECHNOLOGY_DISCOVERED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.nfc.action.TECH_DISCOVERED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EXTRA_ID"
  type="java.lang.String"
  transient="false"
@@ -118709,6 +118761,17 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_TAG"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.nfc.extra.TAG&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="NfcManager"
  extends="java.lang.Object"
@@ -118730,6 +118793,1406 @@
 >
 </method>
 </class>
+<class name="Tag"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="createMockTag"
+ return="android.nfc.Tag"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="byte[]">
+</parameter>
+<parameter name="techList" type="int[]">
+</parameter>
+<parameter name="techListExtras" type="android.os.Bundle[]">
+</parameter>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getId"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTechnology"
+ return="android.nfc.technology.TagTechnology"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.nfc.NfcAdapter">
+</parameter>
+<parameter name="tech" type="int">
+</parameter>
+</method>
+<method name="getTechnologyList"
+ return="int[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+</package>
+<package name="android.nfc.technology"
+>
+<class name="BasicTagTechnology"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility=""
+>
+<implements name="android.nfc.technology.TagTechnology">
+</implements>
+<method name="checkConnected"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="close"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="connect"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="getTag"
+ return="android.nfc.Tag"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTechnologyId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isConnected"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="reconnect"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="transceive"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="byte[]">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
+<class name="IsoDep"
+ extends="android.nfc.technology.BasicTagTechnology"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="IsoDep"
+ type="android.nfc.technology.IsoDep"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.nfc.NfcAdapter">
+</parameter>
+<parameter name="tag" type="android.nfc.Tag">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</constructor>
+<method name="getHiLayerResponse"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getHistoricalBytes"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="MifareClassic"
+ extends="android.nfc.technology.BasicTagTechnology"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="MifareClassic"
+ type="android.nfc.technology.MifareClassic"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.nfc.NfcAdapter">
+</parameter>
+<parameter name="tag" type="android.nfc.Tag">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</constructor>
+<method name="authenticateBlock"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="block" type="int">
+</parameter>
+<parameter name="key" type="byte[]">
+</parameter>
+<parameter name="keyA" type="boolean">
+</parameter>
+</method>
+<method name="authenticateSector"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sector" type="int">
+</parameter>
+<parameter name="key" type="byte[]">
+</parameter>
+<parameter name="keyA" type="boolean">
+</parameter>
+</method>
+<method name="decrement"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="block" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="getBlockCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sector" type="int">
+</parameter>
+</method>
+<method name="getSectorCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSectorSize"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sector" type="int">
+</parameter>
+</method>
+<method name="getSize"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTotalBlockCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="increment"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="block" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="isEmulated"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="readBlock"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sector" type="int">
+</parameter>
+<parameter name="block" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="readBlock"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="block" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="restore"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="block" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="transfer"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="block" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="writeBlock"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="block" type="int">
+</parameter>
+<parameter name="data" type="byte[]">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="writeBlock"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sector" type="int">
+</parameter>
+<parameter name="block" type="int">
+</parameter>
+<parameter name="data" type="byte[]">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<field name="KEY_DEFAULT"
+ type="byte[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEY_MIFARE_APPLICATION_DIRECTORY"
+ type="byte[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEY_NFC_FORUM"
+ type="byte[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SIZE_1K"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1024"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SIZE_2K"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2048"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SIZE_4K"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4096"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SIZE_MINI"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="320"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SIZE_UNKNOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_CLASSIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_DESFIRE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_PLUS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_PRO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_ULTRALIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_UNKNOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="MifareUltralight"
+ extends="android.nfc.technology.BasicTagTechnology"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="MifareUltralight"
+ type="android.nfc.technology.MifareUltralight"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.nfc.NfcAdapter">
+</parameter>
+<parameter name="tag" type="android.nfc.Tag">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</constructor>
+<method name="getType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="readBlock"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="block" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="readOTP"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="writeBlock"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="block" type="int">
+</parameter>
+<parameter name="data" type="byte[]">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="writePage"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="block" type="int">
+</parameter>
+<parameter name="data" type="byte[]">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<field name="TYPE_ULTRALIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_ULTRALIGHT_C"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_UNKNOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Ndef"
+ extends="android.nfc.technology.BasicTagTechnology"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="canMakeReadonly"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCachedNdefMessage"
+ return="android.nfc.NdefMessage"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExtraNdefMessage"
+ return="android.nfc.NdefMessage[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="FormatException" type="android.nfc.FormatException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="getMaxSize"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getNdefMessage"
+ return="android.nfc.NdefMessage"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="FormatException" type="android.nfc.FormatException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="getType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isWritable"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="makeReadonly"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="writeExtraNdefMessage"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="i" type="int">
+</parameter>
+<parameter name="msg" type="android.nfc.NdefMessage">
+</parameter>
+<exception name="FormatException" type="android.nfc.FormatException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="writeNdefMessage"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="msg" type="android.nfc.NdefMessage">
+</parameter>
+<exception name="FormatException" type="android.nfc.FormatException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<field name="MIFARE_CLASSIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="105"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NFC_FORUM_TYPE_1"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NFC_FORUM_TYPE_2"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NFC_FORUM_TYPE_3"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NFC_FORUM_TYPE_4"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OTHER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="NdefFormatable"
+ extends="android.nfc.technology.BasicTagTechnology"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="format"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="firstMessage" type="android.nfc.NdefMessage">
+</parameter>
+<exception name="FormatException" type="android.nfc.FormatException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
+<class name="NfcA"
+ extends="android.nfc.technology.BasicTagTechnology"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="NfcA"
+ type="android.nfc.technology.NfcA"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.nfc.NfcAdapter">
+</parameter>
+<parameter name="tag" type="android.nfc.Tag">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</constructor>
+<method name="getAtqa"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSak"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="NfcB"
+ extends="android.nfc.technology.BasicTagTechnology"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="NfcB"
+ type="android.nfc.technology.NfcB"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.nfc.NfcAdapter">
+</parameter>
+<parameter name="tag" type="android.nfc.Tag">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</constructor>
+<method name="getApplicationData"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getProtocolInfo"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="NfcF"
+ extends="android.nfc.technology.BasicTagTechnology"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="NfcF"
+ type="android.nfc.technology.NfcF"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.nfc.NfcAdapter">
+</parameter>
+<parameter name="tag" type="android.nfc.Tag">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</constructor>
+<method name="getManufacturer"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSystemCode"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="NfcV"
+ extends="android.nfc.technology.BasicTagTechnology"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="NfcV"
+ type="android.nfc.technology.NfcV"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.nfc.NfcAdapter">
+</parameter>
+<parameter name="tag" type="android.nfc.Tag">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</constructor>
+<method name="getDsfId"
+ return="byte"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getResponseFlags"
+ return="byte"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<interface name="TagTechnology"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="close"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="connect"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="getTag"
+ return="android.nfc.Tag"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTechnologyId"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="reconnect"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<field name="ISO_DEP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MIFARE_CLASSIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MIFARE_ULTRALIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="9"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NDEF"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NDEF_FORMATABLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NFC_A"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NFC_B"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NFC_F"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NFC_V"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
 </package>
 <package name="android.opengl"
 >
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index ec2f771..6f0cb45 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -654,7 +654,7 @@
     boolean mCalled;
     boolean mCheckedForLoaderManager;
     boolean mLoadersStarted;
-    private boolean mResumed;
+    /*package*/ boolean mResumed;
     private boolean mStopped;
     boolean mFinished;
     boolean mStartedActivity;
@@ -4335,9 +4335,8 @@
         
         mLastNonConfigurationInstances = null;
         
-        // First call onResume() -before- setting mResumed, so we don't
-        // send out any status bar / menu notifications the client makes.
         mCalled = false;
+        // mResumed is set by the instrumentation
         mInstrumentation.callActivityOnResume(this);
         if (!mCalled) {
             throw new SuperNotCalledException(
@@ -4346,7 +4345,6 @@
         }
 
         // Now really resume, and install the current status bar and menu.
-        mResumed = true;
         mCalled = false;
         
         mFragments.dispatchResume();
@@ -4370,6 +4368,7 @@
                     "Activity " + mComponent.toShortString() +
                     " did not call through to super.onPause()");
         }
+        mResumed = false;
     }
     
     final void performUserLeaving() {
@@ -4417,7 +4416,6 @@
     
             mStopped = true;
         }
-        mResumed = false;
     }
 
     final void performDestroy() {
@@ -4429,7 +4427,10 @@
         }
     }
     
-    final boolean isResumed() {
+    /**
+     * @hide
+     */
+    public final boolean isResumed() {
         return mResumed;
     }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index db046ef..04d0fdd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -193,6 +193,9 @@
     final HashMap<IBinder, ProviderClientRecord> mLocalProviders
         = new HashMap<IBinder, ProviderClientRecord>();
 
+    final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
+        = new HashMap<Activity, ArrayList<OnActivityPausedListener>>();
+
     final GcIdler mGcIdler = new GcIdler();
     boolean mGcIdlerScheduled = false;
 
@@ -1505,6 +1508,18 @@
         }
     }
 
+    public void registerOnActivityPausedListener(Activity activity,
+            OnActivityPausedListener listener) {
+        synchronized (mOnPauseListeners) {
+            ArrayList<OnActivityPausedListener> list = mOnPauseListeners.get(activity);
+            if (list == null) {
+                list = new ArrayList<OnActivityPausedListener>();
+                mOnPauseListeners.put(activity, list);
+            }
+            list.add(listener);
+        }
+    }
+
     public final ActivityInfo resolveActivityInfo(Intent intent) {
         ActivityInfo aInfo = intent.resolveActivityInfo(
                 mInitialApplication.getPackageManager(), PackageManager.GET_SHARED_LIBRARY_FILES);
@@ -2442,6 +2457,17 @@
             }
         }
         r.paused = true;
+
+        // Notify any outstanding on paused listeners
+        ArrayList<OnActivityPausedListener> listeners;
+        synchronized (mOnPauseListeners) {
+            listeners = mOnPauseListeners.remove(r.activity);
+        }
+        int size = (listeners != null ? listeners.size() : 0);
+        for (int i = 0; i < size; i++) {
+            listeners.get(i).onPaused(r.activity);
+        }
+
         return state;
     }
 
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index ad811d8..cd278be 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1149,6 +1149,7 @@
      * @param activity The activity being resumed.
      */
     public void callActivityOnResume(Activity activity) {
+        activity.mResumed = true;
         activity.onResume();
         
         if (mActivityMonitors != null) {
diff --git a/core/java/android/app/OnActivityPausedListener.java b/core/java/android/app/OnActivityPausedListener.java
new file mode 100644
index 0000000..379f133
--- /dev/null
+++ b/core/java/android/app/OnActivityPausedListener.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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.app;
+
+/**
+ * A listener that is called when an Activity is paused. Since this is tracked client side
+ * it should not be trusted to represent the exact current state, but can be used as a hint
+ * for cleanup.
+ *
+ * @hide
+ */
+public interface OnActivityPausedListener {
+    /**
+     * Called when the given activity is paused.
+     */
+    public void onPaused(Activity activity);
+}
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/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index a663fb8..cb9fc9d 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -16,6 +16,9 @@
 
 package android.nfc;
 
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.IntentFilter;
 import android.nfc.NdefMessage;
 import android.nfc.Tag;
 import android.nfc.ILlcpSocket;
@@ -44,6 +47,9 @@
     NdefMessage localGet();
     void localSet(in NdefMessage message);
     void openTagConnection(in Tag tag);
+    void enableForegroundDispatch(in ComponentName activity, in PendingIntent intent,
+        in IntentFilter[] filters);
+    void disableForegroundDispatch(in ComponentName activity);
 
     // Non-public methods
     // TODO: check and complete
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 758c8a0..62b75d0 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -18,8 +18,12 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Activity;
 import android.app.ActivityThread;
+import android.app.OnActivityPausedListener;
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.IntentFilter;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.os.IBinder;
@@ -43,7 +47,6 @@
      *
      * If any activities respond to this intent neither
      * {@link #ACTION_TECHNOLOGY_DISCOVERED} or {@link #ACTION_TAG_DISCOVERED} will be started.
-     * @hide
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
@@ -57,7 +60,6 @@
      * {@link #ACTION_TAG_DISCOVERED}
      *
      * If any activities respond to this intent {@link #ACTION_TAG_DISCOVERED} will not be started.
-     * @hide
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_TECHNOLOGY_DISCOVERED = "android.nfc.action.TECH_DISCOVERED";
@@ -76,7 +78,6 @@
 
     /**
      * Mandatory Tag extra for the ACTION_TAG intents.
-     * @hide
      */
     public static final String EXTRA_TAG = "android.nfc.extra.TAG";
 
@@ -102,6 +103,20 @@
             "android.nfc.action.TRANSACTION_DETECTED";
 
     /**
+     * Broadcast Action: an RF field ON has been detected.
+     * @hide
+     */
+    public static final String ACTION_RF_FIELD_ON_DETECTED =
+            "android.nfc.action.RF_FIELD_ON_DETECTED";
+
+    /**
+     * Broadcast Action: an RF Field OFF has been detected.
+     * @hide
+     */
+    public static final String ACTION_RF_FIELD_OFF_DETECTED =
+            "android.nfc.action.RF_FIELD_OFF_DETECTED";
+
+    /**
      * Broadcast Action: an adapter's state changed between enabled and disabled.
      *
      * The new value is stored in the extra EXTRA_NEW_BOOLEAN_STATE and just contains
@@ -197,8 +212,7 @@
     // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
     // recovery
     private static INfcAdapter sService;
-
-    private final Context mContext;
+    private static INfcTag sTagService;
 
     /**
      * Helper to check if this device has FEATURE_NFC, but without using
@@ -235,6 +249,12 @@
                 Log.e(TAG, "could not retrieve NFC service");
                 return null;
             }
+            try {
+                sTagService = sService.getNfcTagInterface();
+            } catch (RemoteException e) {
+                Log.e(TAG, "could not retrieve NFC Tag service");
+                return null;
+            }
         }
         return sService;
     }
@@ -289,7 +309,6 @@
         if (setupService() == null) {
             throw new UnsupportedOperationException();
         }
-        mContext = context;
     }
 
     /**
@@ -297,10 +316,20 @@
      * @hide
      */
     public INfcAdapter getService() {
+        isEnabled();  // NOP call to recover sService if it is stale
         return sService;
     }
 
     /**
+     * Returns the binder interface to the tag service.
+     * @hide
+     */
+    public INfcTag getTagService() {
+        isEnabled();  // NOP call to recover sTagService if it is stale
+        return sTagService;
+    }
+
+    /**
      * NFC service dead - attempt best effort recovery
      * @hide
      */
@@ -309,11 +338,21 @@
         INfcAdapter service = getServiceInterface();
         if (service == null) {
             Log.e(TAG, "could not retrieve NFC service during service recovery");
+            // nothing more can be done now, sService is still stale, we'll hit
+            // this recovery path again later
             return;
         }
-        /* assigning to sService is not thread-safe, but this is best-effort code
-         * and on a well-behaved system should never happen */
+        // assigning to sService is not thread-safe, but this is best-effort code
+        // and on a well-behaved system should never happen
         sService = service;
+        try {
+            sTagService = service.getNfcTagInterface();
+        } catch (RemoteException ee) {
+            Log.e(TAG, "could not retrieve NFC tag service during service recovery");
+            // nothing more can be done now, sService is still stale, we'll hit
+            // this recovery path again later
+        }
+
         return;
     }
 
@@ -373,6 +412,66 @@
         }
     }
 
+    class ForegroundDispatchPausedListener implements OnActivityPausedListener {
+        @Override
+        public void onPaused(Activity activity) {
+            disableForegroundDispatchInternal(activity, true);
+        }
+    }
+
+    /**
+     * Enables foreground dispatching to the given Activity. This will force all NFC Intents that
+     * match the given filters to be delivered to the activity bypassing the standard dispatch
+     * mechanism.
+     *
+     * This method must be called from the main thread.
+     *
+     * @param activity the Activity to dispatch to
+     * @param intent the PendingIntent to start for the dispatch
+     * @param filters the IntentFilters to override dispatching for
+     * @throws IllegalStateException
+     */
+    public void enableForegroundDispatch(Activity activity, PendingIntent intent,
+            IntentFilter... filters) {
+        if (activity == null || intent == null || filters == null) {
+            throw new NullPointerException();
+        }
+        if (!activity.isResumed()) {
+            throw new IllegalStateException("Foregorund dispatching can onlly be enabled " +
+                    "when your activity is resumed");
+        }
+        try {
+            ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,
+                    new ForegroundDispatchPausedListener());
+            sService.enableForegroundDispatch(activity.getComponentName(), intent, filters);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
+    }
+
+    /**
+     * Disables foreground activity dispatching setup with
+     * {@link #enableForegroundDispatch}. This must be called before the Activity returns from
+     * it's <code>onPause()</code> or this method will throw an IllegalStateException.
+     *
+     * This method must be called from the main thread.
+     */
+    public void disableForegroundDispatch(Activity activity) {
+        disableForegroundDispatchInternal(activity, false);
+    }
+
+    void disableForegroundDispatchInternal(Activity activity, boolean force) {
+        try {
+            sService.disableForegroundDispatch(activity.getComponentName());
+            if (!force && !activity.isResumed()) {
+                throw new IllegalStateException("You must disable forgeground dispatching " +
+                        "while your activity is still resumed");
+            }
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
+    }
+
     /**
      * Set the NDEF Message that this NFC adapter should appear as to Tag
      * readers.
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index 7404950..6cdd9f1 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -45,7 +45,7 @@
  * in {@link NfcAdapter#ACTION_TAG_DISCOVERED} intents. A {@link Tag} object is immutable
  * and represents the state of the tag at the time of discovery. It can be
  * directly queried for its UID and Type, or used to create a {@link TagTechnology}
- * (with {@link Tag#getTechnology(int)}).
+ * (with {@link Tag#getTechnology}).
  * <p>
  * A {@link Tag} can  be used to create a {@link TagTechnology} only while the tag is in
  * range. If it is removed and then returned to range, then the most recent
@@ -55,7 +55,6 @@
  * time and calls on this class will retrieve those read-only properties, and
  * not cause any further RF activity or block. Note however that arrays passed to and
  * returned by this class are *not* cloned, so be careful not to modify them.
- * @hide
  */
 public class Tag implements Parcelable {
     /*package*/ final byte[] mId;
@@ -249,7 +248,9 @@
         }
     };
 
-    /*
+    /**
+     * For internal use only.
+     *
      * @hide
      */
     public synchronized void setConnectedTechnology(int technology) {
@@ -260,14 +261,18 @@
         }
     }
 
-    /*
+    /**
+     * For internal use only.
+     *
      * @hide
      */
     public int getConnectedTechnology() {
         return mConnectedTechnology;
     }
 
-    /*
+    /**
+     * For internal use only.
+     *
      * @hide
      */
     public void setTechnologyDisconnected() {
diff --git a/core/java/android/nfc/technology/BasicTagTechnology.java b/core/java/android/nfc/technology/BasicTagTechnology.java
index 553f6ec..f529ee5 100644
--- a/core/java/android/nfc/technology/BasicTagTechnology.java
+++ b/core/java/android/nfc/technology/BasicTagTechnology.java
@@ -30,19 +30,14 @@
  * A base class for tag technologies that are built on top of transceive().
  */
 /* package */ abstract class BasicTagTechnology implements TagTechnology {
+    private static final String TAG = "NFC";
 
     /*package*/ final Tag mTag;
     /*package*/ boolean mIsConnected;
     /*package*/ int mSelectedTechnology;
     private final NfcAdapter mAdapter;
-
-    // Following fields are final after construction, except for
-    // during attemptDeadServiceRecovery() when NFC crashes.
-    // Not locked - we accept a best effort attempt when NFC crashes.
-    /*package*/ INfcAdapter mService;
-    /*package*/ INfcTag mTagService;
-
-    private static final String TAG = "NFC";
+    /*package*/ final INfcAdapter mService;
+    /*package*/ final INfcTag mTagService;
 
     /**
      * @hide
@@ -64,11 +59,7 @@
 
         mAdapter = adapter;
         mService = mAdapter.getService();
-        try {
-          mTagService = mService.getNfcTagInterface();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-        }
+        mTagService = mAdapter.getTagService();
         mTag = tag;
         mSelectedTechnology = tech;
     }
@@ -80,19 +71,6 @@
         this(adapter, tag, tag.getTechnologyList()[0]);
     }
 
-    /** NFC service dead - attempt best effort recovery */
-    /*package*/ void attemptDeadServiceRecovery(Exception e) {
-        mAdapter.attemptDeadServiceRecovery(e);
-        /* assigning to mService is not thread-safe, but this is best-effort code
-         * and on a well-behaved system should never happen */
-        mService = mAdapter.getService();
-        try {
-            mTagService = mService.getNfcTagInterface();
-        } catch (RemoteException e2) {
-            Log.e(TAG, "second RemoteException trying to recover from dead NFC service", e2);
-        }
-    }
-
     /**
      * Get the {@link Tag} this connection is associated with.
      * <p>Requires {@link android.Manifest.permission#NFC} permission.
@@ -135,7 +113,7 @@
         try {
             return mTagService.isPresent(mTag.getServiceHandle());
         } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
+            Log.e(TAG, "NFC service dead", e);
             return false;
         }
     }
@@ -163,7 +141,7 @@
                 throw new IOException();
             }
         } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
+            Log.e(TAG, "NFC service dead", e);
             throw new IOException("NFC service died");
         }
     }
@@ -183,21 +161,21 @@
     public void reconnect() throws IOException {
         if (!mIsConnected) {
             throw new IllegalStateException("Technology not connected yet");
-        } else {
-            try {
-                int errorCode = mTagService.reconnect(mTag.getServiceHandle());
+        }
 
-                if (errorCode != ErrorCodes.SUCCESS) {
-                    mIsConnected = false;
-                    mTag.setTechnologyDisconnected();
-                    throw new IOException();
-                }
-            } catch (RemoteException e) {
+        try {
+            int errorCode = mTagService.reconnect(mTag.getServiceHandle());
+
+            if (errorCode != ErrorCodes.SUCCESS) {
                 mIsConnected = false;
                 mTag.setTechnologyDisconnected();
-                attemptDeadServiceRecovery(e);
-                throw new IOException("NFC service died");
+                throw new IOException();
             }
+        } catch (RemoteException e) {
+            mIsConnected = false;
+            mTag.setTechnologyDisconnected();
+            Log.e(TAG, "NFC service dead", e);
+            throw new IOException("NFC service died");
         }
     }
 
@@ -219,13 +197,29 @@
              */
             mTagService.reconnect(mTag.getServiceHandle());
         } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
+            Log.e(TAG, "NFC service dead", e);
         } finally {
             mIsConnected = false;
             mTag.setTechnologyDisconnected();
         }
     }
 
+    /** internal transceive */
+    /*package*/ byte[] transceive(byte[] data, boolean raw) throws IOException {
+        checkConnected();
+
+        try {
+            byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, raw);
+            if (response == null) {
+                throw new IOException("transceive failed");
+            }
+            return response;
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            throw new IOException("NFC service died");
+        }
+    }
+
     /**
      * Send data to a tag and receive the response.
      * <p>
@@ -238,17 +232,6 @@
      * @throws IOException if the target is lost or connection closed
      */
     public byte[] transceive(byte[] data) throws IOException {
-        checkConnected();
-
-        try {
-            byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, true);
-            if (response == null) {
-                throw new IOException("transceive failed");
-            }
-            return response;
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            throw new IOException("NFC service died");
-        }
+        return transceive(data, true);
     }
 }
diff --git a/core/java/android/nfc/technology/IsoDep.java b/core/java/android/nfc/technology/IsoDep.java
index 32a7542..b1f3ed1 100644
--- a/core/java/android/nfc/technology/IsoDep.java
+++ b/core/java/android/nfc/technology/IsoDep.java
@@ -27,7 +27,7 @@
  * A low-level connection to a {@link Tag} using the ISO-DEP technology, also known as
  * ISO1443-4.
  *
- * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}.
+ * <p>You can acquire this kind of connection with {@link Tag#getTechnology}.
  * Use this class to send and receive data with {@link #transceive transceive()}.
  *
  * <p>Applications must implement their own protocol stack on top of
@@ -58,10 +58,14 @@
     /**
      * 3A only
      */
-    public byte[] getHistoricalBytes() { return mHistBytes; }
+    public byte[] getHistoricalBytes() {
+        return mHistBytes;
+    }
 
     /**
      * 3B only
      */
-    public byte[] getHiLayerResponse() { return mHiLayerResponse; }
+    public byte[] getHiLayerResponse() {
+        return mHiLayerResponse;
+    }
 }
diff --git a/core/java/android/nfc/technology/MifareClassic.java b/core/java/android/nfc/technology/MifareClassic.java
index 799f0a78..c5fb361 100644
--- a/core/java/android/nfc/technology/MifareClassic.java
+++ b/core/java/android/nfc/technology/MifareClassic.java
@@ -26,13 +26,13 @@
 /**
  * Concrete class for TagTechnology.MIFARE_CLASSIC
  *
- * Mifare classic has n sectors, with varying sizes, although
- * they are at least the same pattern for any one mifare classic
+ * MIFARE Classic has n sectors, with varying sizes, although
+ * they are at least the same pattern for any one MIFARE Classic
  * product. Each sector has two keys. Authentication with the correct
  * key is needed before access to any sector.
  *
  * Each sector has k blocks.
- * Block size is constant across the whole mifare classic family.
+ * Block size is constant across the whole MIFARE classic family.
  */
 public final class MifareClassic extends BasicTagTechnology {
     /**
@@ -43,12 +43,12 @@
     public static final byte[] KEY_DEFAULT =
             {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF};
     /**
-     * The well-known, default Mifare Application Directory read key.
+     * The well-known, default MIFARE Application Directory read key.
      */
     public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY =
             {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5};
     /**
-     * The well-known, default read key for NDEF data on a Mifare Classic
+     * The well-known, default read key for NDEF data on a MIFARE Classic
      */
     public static final byte[] KEY_NFC_FORUM =
             {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7};
@@ -263,7 +263,7 @@
         System.arraycopy(key, 0, cmd, 6, 6);
 
         try {
-            if ((transceive(cmd) != null)) {
+            if ((transceive(cmd, false) != null)) {
                 return true;
             }
         } catch (IOException e) {
@@ -308,7 +308,7 @@
         byte addr = (byte) block;
         byte[] blockread_cmd = { 0x30, addr };
 
-        return transceive(blockread_cmd);
+        return transceive(blockread_cmd, false);
     }
 
     /**
@@ -324,7 +324,7 @@
         blockwrite_cmd[1] = addr;
         System.arraycopy(data, 0, blockwrite_cmd, 2, data.length);
 
-        transceive(blockwrite_cmd);
+        transceive(blockwrite_cmd, false);
     }
 
     /**
@@ -345,7 +345,7 @@
         byte addr = (byte) block;
         byte[] incr_cmd = { (byte) 0xC1, (byte) block };
 
-        transceive(incr_cmd);
+        transceive(incr_cmd, false);
     }
 
     public void decrement(int block) throws IOException {
@@ -354,7 +354,7 @@
         byte addr = (byte) block;
         byte[] decr_cmd = { (byte) 0xC0, (byte) block };
 
-        transceive(decr_cmd);
+        transceive(decr_cmd, false);
     }
 
     public void transfer(int block) throws IOException {
@@ -363,7 +363,7 @@
         byte addr = (byte) block;
         byte[] trans_cmd = { (byte) 0xB0, (byte) block };
 
-        transceive(trans_cmd);
+        transceive(trans_cmd, false);
     }
 
     public void restore(int block) throws IOException {
@@ -372,33 +372,6 @@
         byte addr = (byte) block;
         byte[] rest_cmd = { (byte) 0xC2, (byte) block };
 
-        transceive(rest_cmd);
-    }
-
-    /**
-     * Send data to a tag and receive the response.
-     * <p>
-     * This method will block until the response is received. It can be canceled
-     * with {@link #close}.
-     * <p>Requires {@link android.Manifest.permission#NFC} permission.
-     *
-     * @param data bytes to send
-     * @return bytes received in response
-     * @throws IOException if the target is lost or connection closed
-     */
-    @Override
-    public byte[] transceive(byte[] data) throws IOException {
-        checkConnected();
-
-        try {
-            byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, false);
-            if (response == null) {
-                throw new IOException("transceive failed");
-            }
-            return response;
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            throw new IOException("NFC service died");
-        }
+        transceive(rest_cmd, false);
     }
 }
diff --git a/core/java/android/nfc/technology/MifareUltralight.java b/core/java/android/nfc/technology/MifareUltralight.java
index 7103b4d..e53061f 100644
--- a/core/java/android/nfc/technology/MifareUltralight.java
+++ b/core/java/android/nfc/technology/MifareUltralight.java
@@ -26,13 +26,13 @@
 /**
  * Concrete class for TagTechnology.MIFARE_ULTRALIGHT
  *
- * Mifare classic has n sectors, with varying sizes, although
- * they are at least the same pattern for any one mifare classic
+ * MIFARE Ultralight has n sectors, with varying sizes, although
+ * they are at least the same pattern for any one MIFARE Ultralight
  * product. Each sector has two keys. Authentication with the correct
  * key is needed before access to any sector.
  *
  * Each sector has k blocks.
- * Block size is constant across the whole mifare classic family.
+ * Block size is constant across the whole MIFARE Ultralight family.
  */
 public final class MifareUltralight extends BasicTagTechnology {
     public static final int TYPE_ULTRALIGHT = 1;
@@ -69,7 +69,7 @@
         checkConnected();
 
         byte[] blockread_cmd = { 0x30, (byte)block }; // phHal_eMifareRead
-        return transceive(blockread_cmd);
+        return transceive(blockread_cmd, false);
     }
 
     /**
@@ -89,7 +89,7 @@
         pagewrite_cmd[1] = (byte) block;
         System.arraycopy(data, 0, pagewrite_cmd, 2, data.length);
 
-        transceive(pagewrite_cmd);
+        transceive(pagewrite_cmd, false);
     }
 
     public void writeBlock(int block, byte[] data) throws IOException {
@@ -100,34 +100,6 @@
         blockwrite_cmd[1] = (byte) block;
         System.arraycopy(data, 0, blockwrite_cmd, 2, data.length);
 
-        transceive(blockwrite_cmd);
+        transceive(blockwrite_cmd, false);
     }
-
-    /**
-     * Send data to a tag and receive the response.
-     * <p>
-     * This method will block until the response is received. It can be canceled
-     * with {@link #close}.
-     * <p>Requires {@link android.Manifest.permission#NFC} permission.
-     *
-     * @param data bytes to send
-     * @return bytes received in response
-     * @throws IOException if the target is lost or connection closed
-     */
-    @Override
-    public byte[] transceive(byte[] data) throws IOException {
-        checkConnected();
-
-        try {
-            byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, false);
-            if (response == null) {
-                throw new IOException("transceive failed");
-            }
-            return response;
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            throw new IOException("NFC service died");
-        }
-    }
-
 }
diff --git a/core/java/android/nfc/technology/Ndef.java b/core/java/android/nfc/technology/Ndef.java
index 05872fe..f033220 100644
--- a/core/java/android/nfc/technology/Ndef.java
+++ b/core/java/android/nfc/technology/Ndef.java
@@ -23,6 +23,7 @@
 import android.nfc.Tag;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.util.Log;
 
 import java.io.IOException;
 
@@ -31,13 +32,15 @@
  * to interact with NDEF data. MiFare Classic cards that present NDEF data may also be used
  * via this class. To determine the exact technology being used call {@link #getTechnologyId()}
  *
- * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}.
+ * <p>You can acquire this kind of connection with {@link Tag#getTechnology}.
  *
  * <p class="note"><strong>Note:</strong>
  * Use of this class requires the {@link android.Manifest.permission#NFC}
  * permission.
  */
 public final class Ndef extends BasicTagTechnology {
+    private static final String TAG = "NFC";
+
     /** @hide */
     public static final int NDEF_MODE_READ_ONLY = 1;
     /** @hide */
@@ -168,7 +171,7 @@
                 return null;
             }
         } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
+            Log.e(TAG, "NFC service dead", e);
             return null;
         }
     }
@@ -200,7 +203,7 @@
                 throw new IOException("Tag is not ndef");
             }
         } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
+            Log.e(TAG, "NFC service dead", e);
         }
     }
 
@@ -221,7 +224,22 @@
     }
 
     /**
-     * Set the CC field to indicate this tag is read-only
+     * Indicates whether a tag can be made read-only with
+     * {@link #makeReadonly()}
+     */
+    public boolean canMakeReadonly() {
+        if (mNdefType == NFC_FORUM_TYPE_1 || mNdefType == NFC_FORUM_TYPE_2) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Sets the CC field to indicate this tag is read-only
+     * and permanently sets the lock bits to prevent any further NDEF
+     * modifications.
+     * This is a one-way process and can not be reverted!
      * @throws IOException
      */
     public boolean makeReadonly() throws IOException {
@@ -241,22 +259,11 @@
                     throw new IOException();
             }
         } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
+            Log.e(TAG, "NFC service dead", e);
             return false;
         }
     }
 
-    /**
-     * Attempt to use tag specific technology to really make
-     * the tag read-only
-     * For NFC Forum Type 1 and 2 only.
-     */
-    public void makeLowLevelReadonly() {
-        checkConnected();
-
-        throw new UnsupportedOperationException();
-    }
-
     @Override
     public byte[] transceive(byte[] data) {
         checkConnected();
diff --git a/core/java/android/nfc/technology/NdefFormatable.java b/core/java/android/nfc/technology/NdefFormatable.java
index 222c558..39bd596 100644
--- a/core/java/android/nfc/technology/NdefFormatable.java
+++ b/core/java/android/nfc/technology/NdefFormatable.java
@@ -23,19 +23,22 @@
 import android.nfc.Tag;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.util.Log;
 
 import java.io.IOException;
 
 /**
  * An interface to a {@link Tag} allowing to format the tag as NDEF.
  *
- * <p>You can acquire this kind of interface with {@link Tag#getTechnology(int)}.
+ * <p>You can acquire this kind of interface with {@link Tag#getTechnology}.
  *
  * <p class="note"><strong>Note:</strong>
  * Use of this class requires the {@link android.Manifest.permission#NFC}
  * permission.
  */
 public final class NdefFormatable extends BasicTagTechnology {
+    private static final String TAG = "NFC";
+
     /**
      * Internal constructor, to be used by NfcAdapter
      * @hide
@@ -85,7 +88,7 @@
                 throw new IOException();
             }
         } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
+            Log.e(TAG, "NFC service dead", e);
         }
     }
 
diff --git a/core/java/android/nfc/technology/NfcA.java b/core/java/android/nfc/technology/NfcA.java
index ef46762..5cb8190 100644
--- a/core/java/android/nfc/technology/NfcA.java
+++ b/core/java/android/nfc/technology/NfcA.java
@@ -25,7 +25,7 @@
  * A low-level connection to a {@link Tag} using the NFC-A technology, also known as
  * ISO1443-3A.
  *
- * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}.
+ * <p>You can acquire this kind of connection with {@link Tag#getTechnology}.
  * Use this class to send and receive data with {@link #transceive transceive()}.
  *
  * <p>Applications must implement their own protocol stack on top of
diff --git a/core/java/android/nfc/technology/NfcB.java b/core/java/android/nfc/technology/NfcB.java
index 267c94d..dc9dd7a 100644
--- a/core/java/android/nfc/technology/NfcB.java
+++ b/core/java/android/nfc/technology/NfcB.java
@@ -25,7 +25,7 @@
  * A low-level connection to a {@link Tag} using the NFC-B technology, also known as
  * ISO1443-3B.
  *
- * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}.
+ * <p>You can acquire this kind of connection with {@link Tag#getTechnology}.
  * Use this class to send and receive data with {@link #transceive transceive()}.
  *
  * <p>Applications must implement their own protocol stack on top of
diff --git a/core/java/android/nfc/technology/NfcF.java b/core/java/android/nfc/technology/NfcF.java
index 6741ac8..dd0e2f9 100644
--- a/core/java/android/nfc/technology/NfcF.java
+++ b/core/java/android/nfc/technology/NfcF.java
@@ -25,7 +25,7 @@
  * A low-level connection to a {@link Tag} using the NFC-F technology, also known as
  * JIS6319-4.
  *
- * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}.
+ * <p>You can acquire this kind of connection with {@link Tag#getTechnology}.
  * Use this class to send and receive data with {@link #transceive transceive()}.
  *
  * <p>Applications must implement their own protocol stack on top of
diff --git a/core/java/android/nfc/technology/NfcV.java b/core/java/android/nfc/technology/NfcV.java
index 460de6a..da73f5d 100644
--- a/core/java/android/nfc/technology/NfcV.java
+++ b/core/java/android/nfc/technology/NfcV.java
@@ -25,7 +25,7 @@
  * A low-level connection to a {@link Tag} using the NFC-V technology, also known as
  * ISO15693.
  *
- * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}.
+ * <p>You can acquire this kind of connection with {@link Tag#getTechnology}.
  * Use this class to send and receive data with {@link #transceive transceive()}.
  *
  * <p>Applications must implement their own protocol stack on top of
diff --git a/core/java/android/nfc/technology/package.html b/core/java/android/nfc/technology/package.html
deleted file mode 100644
index 26b8a32..0000000
--- a/core/java/android/nfc/technology/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<HTML>
-<BODY>
-{@hide}
-</BODY>
-</HTML>
\ No newline at end of file
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 52b0643..a859e3c 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -517,8 +517,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/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index d24af52..6b44f9e 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/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b682947..b3b80f6 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4087,7 +4087,7 @@
                 // we have gone through a significant charge (from a very low
                 // level to a now very high level).
                 if (oldStatus == BatteryManager.BATTERY_STATUS_FULL
-                        || level >= 95
+                        || level >= 90
                         || (mDischargeCurrentLevel < 20 && level >= 80)) {
                     doWrite = true;
                     resetAllStatsLocked();
diff --git a/core/java/com/android/internal/widget/DigitalClock.java b/core/java/com/android/internal/widget/DigitalClock.java
index bc749d8..2bc9dd1 100644
--- a/core/java/com/android/internal/widget/DigitalClock.java
+++ b/core/java/com/android/internal/widget/DigitalClock.java
@@ -34,6 +34,7 @@
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import java.lang.ref.WeakReference;
 import java.text.DateFormatSymbols;
 import java.util.Calendar;
 
@@ -54,26 +55,41 @@
     private TextView mTimeDisplayForeground;
     private AmPm mAmPm;
     private ContentObserver mFormatChangeObserver;
-    private boolean mLive = true;
-    private boolean mAttached;
+    private int mAttached = 0; // for debugging - tells us whether attach/detach is unbalanced
 
     /* called by system on minute ticks */
     private final Handler mHandler = new Handler();
-    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (mLive && intent.getAction().equals(
-                            Intent.ACTION_TIMEZONE_CHANGED)) {
-                    mCalendar = Calendar.getInstance();
-                }
-                // Post a runnable to avoid blocking the broadcast.
-                mHandler.post(new Runnable() {
-                        public void run() {
-                            updateTime();
+    private BroadcastReceiver mIntentReceiver;
+
+    private static class TimeChangedReceiver extends BroadcastReceiver {
+        private WeakReference<DigitalClock> mClock;
+        private Context mContext;
+
+        public TimeChangedReceiver(DigitalClock clock) {
+            mClock = new WeakReference<DigitalClock>(clock);
+            mContext = clock.getContext();
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // Post a runnable to avoid blocking the broadcast.
+            final boolean timezoneChanged =
+                    intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED);
+            final DigitalClock clock = mClock.get();
+            if (clock != null) {
+                clock.mHandler.post(new Runnable() {
+                    public void run() {
+                        if (timezoneChanged) {
+                            clock.mCalendar = Calendar.getInstance();
                         }
+                        clock.updateTime();
+                    }
                 });
+            } else {
+                mContext.unregisterReceiver(this);
             }
-        };
+        }
+    };
 
     static class AmPm {
         private TextView mAmPm;
@@ -99,14 +115,23 @@
         }
     }
 
-    private class FormatChangeObserver extends ContentObserver {
-        public FormatChangeObserver() {
+    private static class FormatChangeObserver extends ContentObserver {
+        private WeakReference<DigitalClock> mClock;
+        private Context mContext;
+        public FormatChangeObserver(DigitalClock clock) {
             super(new Handler());
+            mClock = new WeakReference<DigitalClock>(clock);
+            mContext = clock.getContext();
         }
         @Override
         public void onChange(boolean selfChange) {
-            setDateFormat();
-            updateTime();
+            DigitalClock digitalClock = mClock.get();
+            if (digitalClock != null) {
+                digitalClock.setDateFormat();
+                digitalClock.updateTime();
+            } else {
+                mContext.getContentResolver().unregisterContentObserver(this);
+            }
         }
     }
 
@@ -139,11 +164,11 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
 
-        if (mAttached) return;
-        mAttached = true;
+        mAttached++;
 
-        if (mLive) {
-            /* monitor time ticks, time changed, timezone */
+        /* monitor time ticks, time changed, timezone */
+        if (mIntentReceiver == null) {
+            mIntentReceiver = new TimeChangedReceiver(this);
             IntentFilter filter = new IntentFilter();
             filter.addAction(Intent.ACTION_TIME_TICK);
             filter.addAction(Intent.ACTION_TIME_CHANGED);
@@ -152,9 +177,11 @@
         }
 
         /* monitor 12/24-hour display preference */
-        mFormatChangeObserver = new FormatChangeObserver();
-        mContext.getContentResolver().registerContentObserver(
-                Settings.System.CONTENT_URI, true, mFormatChangeObserver);
+        if (mFormatChangeObserver == null) {
+            mFormatChangeObserver = new FormatChangeObserver(this);
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.System.CONTENT_URI, true, mFormatChangeObserver);
+        }
 
         updateTime();
     }
@@ -163,16 +190,19 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
 
-        if (!mAttached) return;
-        mAttached = false;
+        mAttached--;
 
-        if (mLive) {
+        if (mIntentReceiver != null) {
             mContext.unregisterReceiver(mIntentReceiver);
         }
-        mContext.getContentResolver().unregisterContentObserver(
-                mFormatChangeObserver);
-    }
+        if (mFormatChangeObserver != null) {
+            mContext.getContentResolver().unregisterContentObserver(
+                    mFormatChangeObserver);
+        }
 
+        mFormatChangeObserver = null;
+        mIntentReceiver = null;
+    }
 
     void updateTime(Calendar c) {
         mCalendar = c;
@@ -180,9 +210,7 @@
     }
 
     private void updateTime() {
-        if (mLive) {
-            mCalendar.setTimeInMillis(System.currentTimeMillis());
-        }
+        mCalendar.setTimeInMillis(System.currentTimeMillis());
 
         CharSequence newTime = DateFormat.format(mFormat, mCalendar);
         mTimeDisplayBackground.setText(newTime);
@@ -195,8 +223,4 @@
             ? M24 : M12;
         mAmPm.setShowAmPm(mFormat.equals(M12));
     }
-
-    void setLive(boolean live) {
-        mLive = live;
-    }
 }
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/guide_toc.cs b/docs/html/guide/guide_toc.cs
index a984acd..ca934ec 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -488,12 +488,13 @@
                     </li>
                   </ul>
               </li>
-              <li><a href="/guide/developing/tools/proguard.html">ProGuard</a></li>
-              <li><a href="/guide/developing/tools/adb.html#sqlite">sqlite3</a></li>
-              <li><a href="/guide/developing/tools/traceview.html">Traceview</a></li>
-          <li><a href="<?cs var:toroot ?>guide/developing/tools/zipalign.html">zipalign</a></li>
-        </ul>
-      </li>
+              <li><a href="<?cs var:toroot ?>guide/developing/tools/proguard.html">ProGuard</a>
+<span class="new">new!</span></li>
+              <li><a href="<?cs var:toroot ?>guide/developing/tools/adb.html#sqlite">sqlite3</a></li>
+              <li><a href="<?cs var:toroot ?>guide/developing/tools/traceview.html" >Traceview</a></li>
+              <li><a href="<?cs var:toroot ?>guide/developing/tools/zipalign.html" >zipalign</a></li>
+          </ul>
+        </li>
     </ul>
   </li>
 
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/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index 5c20811..2253eb2 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -25,7 +25,8 @@
     <application>    
         <uses-library android:name="android.test.runner" />
         <activity android:label="@string/app_name"
-                android:name="MediaFrameworkTest">         
+                android:name="MediaFrameworkTest"
+                android:screenOrientation="landscape">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER"/>
diff --git a/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java b/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java
index 840c5e1..c4feefd 100644
--- a/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java
@@ -62,8 +62,8 @@
      */
     private static final int AWAKE_POKE_MILLIS = 30000;
 
-    private final KeyguardScreenCallback mCallback;
-    private final LockPatternUtils mLockPatternUtils;
+    private KeyguardScreenCallback mCallback;
+    private LockPatternUtils mLockPatternUtils;
     private KeyguardUpdateMonitor mUpdateMonitor;
 
     private TextView mTopHeader;
@@ -159,7 +159,10 @@
         if (mCheckingDialog != null) {
             mCheckingDialog.hide();
         }
-        mUpdateMonitor.removeCallback(this);
+        mUpdateMonitor.removeCallback(this); // this must be first
+        mCallback = null;
+        mLockPatternUtils = null;
+        mUpdateMonitor = null;
     }
 
     /** {@inheritDoc} */
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
index 70a4b20..fcd4bf39 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
@@ -231,8 +231,8 @@
                 mKeyguardHost.postDelayed(new Runnable() {
                     public void run() {
                         synchronized (KeyguardViewManager.this) {
-                            mKeyguardHost.removeView(lastView);
                             lastView.cleanUp();
+                            mKeyguardHost.removeView(lastView);
                         }
                     }
                 }, 500);
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index 822be46..886b85f 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -495,8 +495,10 @@
     public void cleanUp() {
         ((KeyguardScreen) mLockScreen).onPause();
         ((KeyguardScreen) mLockScreen).cleanUp();
+        this.removeView(mLockScreen);
         ((KeyguardScreen) mUnlockScreen).onPause();
         ((KeyguardScreen) mUnlockScreen).cleanUp();
+        this.removeView(mUnlockScreen);
     }
 
     private boolean isSecure() {
diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
index 1b810d5..2bc57b5 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/LockScreen.java
@@ -59,9 +59,9 @@
 
     private Status mStatus = Status.Normal;
 
-    private final LockPatternUtils mLockPatternUtils;
-    private final KeyguardUpdateMonitor mUpdateMonitor;
-    private final KeyguardScreenCallback mCallback;
+    private LockPatternUtils mLockPatternUtils;
+    private KeyguardUpdateMonitor mUpdateMonitor;
+    private KeyguardScreenCallback mCallback;
 
     private SlidingTab mSlidingTab;
     private TextView mScreenLocked;
@@ -659,7 +659,10 @@
 
     /** {@inheritDoc} */
     public void cleanUp() {
-        mUpdateMonitor.removeCallback(this);
+        mUpdateMonitor.removeCallback(this); // this must be first
+        mLockPatternUtils = null;
+        mUpdateMonitor = null;
+        mCallback = null;
     }
 
     /** {@inheritDoc} */
diff --git a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
index 6815d50..6c6c2cc8 100644
--- a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
@@ -62,9 +62,9 @@
     private int mTotalFailedPatternAttempts = 0;
     private CountDownTimer mCountdownTimer = null;
 
-    private final LockPatternUtils mLockPatternUtils;
-    private final KeyguardUpdateMonitor mUpdateMonitor;
-    private final KeyguardScreenCallback mCallback;
+    private LockPatternUtils mLockPatternUtils;
+    private KeyguardUpdateMonitor mUpdateMonitor;
+    private KeyguardScreenCallback mCallback;
 
     /**
      * whether there is a fallback option available when the pattern is forgotten.
@@ -362,6 +362,9 @@
     /** {@inheritDoc} */
     public void cleanUp() {
         mUpdateMonitor.removeCallback(this);
+        mLockPatternUtils = null;
+        mUpdateMonitor = null;
+        mCallback = null;
     }
 
     @Override