diff options
65 files changed, 7306 insertions, 257 deletions
@@ -72,6 +72,16 @@ is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 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 6d12990facd7..fff8a0fec619 100644 --- a/api/current.xml +++ b/api/current.xml @@ -118615,6 +118615,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" @@ -118650,6 +118680,17 @@ visibility="public" > </method> +<field name="ACTION_NDEF_DISCOVERED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.nfc.action.NDEF_DISCOVERED"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ACTION_TAG_DISCOVERED" type="java.lang.String" transient="false" @@ -118661,6 +118702,17 @@ visibility="public" > </field> +<field name="ACTION_TECHNOLOGY_DISCOVERED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.nfc.action.TECH_DISCOVERED"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="EXTRA_ID" type="java.lang.String" transient="false" @@ -118683,6 +118735,17 @@ visibility="public" > </field> +<field name="EXTRA_TAG" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.nfc.extra.TAG"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="NfcManager" extends="java.lang.Object" @@ -118704,6 +118767,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 ec2f771cd984..6f0cb45dd466 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -654,7 +654,7 @@ public class Activity extends ContextThemeWrapper boolean mCalled; boolean mCheckedForLoaderManager; boolean mLoadersStarted; - private boolean mResumed; + /*package*/ boolean mResumed; private boolean mStopped; boolean mFinished; boolean mStartedActivity; @@ -4335,9 +4335,8 @@ public class Activity extends ContextThemeWrapper 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 @@ public class Activity extends ContextThemeWrapper } // Now really resume, and install the current status bar and menu. - mResumed = true; mCalled = false; mFragments.dispatchResume(); @@ -4370,6 +4368,7 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onPause()"); } + mResumed = false; } final void performUserLeaving() { @@ -4417,7 +4416,6 @@ public class Activity extends ContextThemeWrapper mStopped = true; } - mResumed = false; } final void performDestroy() { @@ -4429,7 +4427,10 @@ public class Activity extends ContextThemeWrapper } } - 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 db046ef73597..04d0fddbdd79 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -193,6 +193,9 @@ public final class ActivityThread { 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 final class ActivityThread { } } + 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 @@ public final class ActivityThread { } } 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 ad811d893c01..cd278be06a7a 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1149,6 +1149,7 @@ public class Instrumentation { * @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 000000000000..379f133c0755 --- /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 da518c29501d..2d03e7c96147 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -1579,9 +1579,11 @@ public abstract class ContentResolver { @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 a663fb84f82d..cb9fc9d05fbe 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 @@ interface INfcAdapter 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 758c8a0d68c8..62b75d0b2825 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -18,8 +18,12 @@ package android.nfc; 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 @@ public final class NfcAdapter { * * 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 @@ public final class NfcAdapter { * {@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 @@ public final class NfcAdapter { /** * Mandatory Tag extra for the ACTION_TAG intents. - * @hide */ public static final String EXTRA_TAG = "android.nfc.extra.TAG"; @@ -102,6 +103,20 @@ public final class NfcAdapter { "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 @@ public final class NfcAdapter { // 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 @@ public final class NfcAdapter { 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 @@ public final class NfcAdapter { if (setupService() == null) { throw new UnsupportedOperationException(); } - mContext = context; } /** @@ -297,10 +316,20 @@ public final class NfcAdapter { * @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 @@ public final class NfcAdapter { 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 @@ public final class NfcAdapter { } } + 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 740495088724..6cdd9f130497 100644 --- a/core/java/android/nfc/Tag.java +++ b/core/java/android/nfc/Tag.java @@ -45,7 +45,7 @@ import java.util.Arrays; * 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 @@ import java.util.Arrays; * 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 @@ public class Tag implements Parcelable { } }; - /* + /** + * For internal use only. + * * @hide */ public synchronized void setConnectedTechnology(int technology) { @@ -260,14 +261,18 @@ public class Tag implements Parcelable { } } - /* + /** + * 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 553f6eca10d6..f529ee554ef9 100644 --- a/core/java/android/nfc/technology/BasicTagTechnology.java +++ b/core/java/android/nfc/technology/BasicTagTechnology.java @@ -30,19 +30,14 @@ import android.util.Log; * 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 @@ import android.util.Log; 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 @@ import android.util.Log; 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 @@ import android.util.Log; try { return mTagService.isPresent(mTag.getServiceHandle()); } catch (RemoteException e) { - attemptDeadServiceRecovery(e); + Log.e(TAG, "NFC service dead", e); return false; } } @@ -163,7 +141,7 @@ import android.util.Log; 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 @@ import android.util.Log; 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 @@ import android.util.Log; */ 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 @@ import android.util.Log; * @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 32a7542ea77f..b1f3ed15ff3b 100644 --- a/core/java/android/nfc/technology/IsoDep.java +++ b/core/java/android/nfc/technology/IsoDep.java @@ -27,7 +27,7 @@ import java.io.IOException; * 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 @@ public final class IsoDep extends BasicTagTechnology { /** * 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 799f0a7841ec..c5fb361a2ef2 100644 --- a/core/java/android/nfc/technology/MifareClassic.java +++ b/core/java/android/nfc/technology/MifareClassic.java @@ -26,13 +26,13 @@ import java.io.IOException; /** * 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 final class MifareClassic extends BasicTagTechnology { 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 @@ public final class MifareClassic extends BasicTagTechnology { 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 @@ public final class MifareClassic extends BasicTagTechnology { byte addr = (byte) block; byte[] blockread_cmd = { 0x30, addr }; - return transceive(blockread_cmd); + return transceive(blockread_cmd, false); } /** @@ -324,7 +324,7 @@ public final class MifareClassic extends BasicTagTechnology { blockwrite_cmd[1] = addr; System.arraycopy(data, 0, blockwrite_cmd, 2, data.length); - transceive(blockwrite_cmd); + transceive(blockwrite_cmd, false); } /** @@ -345,7 +345,7 @@ public final class MifareClassic extends BasicTagTechnology { 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 @@ public final class MifareClassic extends BasicTagTechnology { 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 @@ public final class MifareClassic extends BasicTagTechnology { 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 @@ public final class MifareClassic extends BasicTagTechnology { 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 7103b4d85e50..e53061faf682 100644 --- a/core/java/android/nfc/technology/MifareUltralight.java +++ b/core/java/android/nfc/technology/MifareUltralight.java @@ -26,13 +26,13 @@ import android.os.RemoteException; /** * 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 @@ public final class MifareUltralight extends BasicTagTechnology { checkConnected(); byte[] blockread_cmd = { 0x30, (byte)block }; // phHal_eMifareRead - return transceive(blockread_cmd); + return transceive(blockread_cmd, false); } /** @@ -89,7 +89,7 @@ public final class MifareUltralight extends BasicTagTechnology { 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 @@ public final class MifareUltralight extends BasicTagTechnology { 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 05872fe78761..f033220105fa 100644 --- a/core/java/android/nfc/technology/Ndef.java +++ b/core/java/android/nfc/technology/Ndef.java @@ -23,6 +23,7 @@ import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; +import android.util.Log; import java.io.IOException; @@ -31,13 +32,15 @@ import java.io.IOException; * 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 @@ public final class Ndef extends BasicTagTechnology { return null; } } catch (RemoteException e) { - attemptDeadServiceRecovery(e); + Log.e(TAG, "NFC service dead", e); return null; } } @@ -200,7 +203,7 @@ public final class Ndef extends BasicTagTechnology { throw new IOException("Tag is not ndef"); } } catch (RemoteException e) { - attemptDeadServiceRecovery(e); + Log.e(TAG, "NFC service dead", e); } } @@ -221,7 +224,22 @@ public final class Ndef extends BasicTagTechnology { } /** - * 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 @@ public final class Ndef extends BasicTagTechnology { 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 222c5584ca76..39bd5961c297 100644 --- a/core/java/android/nfc/technology/NdefFormatable.java +++ b/core/java/android/nfc/technology/NdefFormatable.java @@ -23,19 +23,22 @@ import android.nfc.NfcAdapter; 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 @@ public final class NdefFormatable extends BasicTagTechnology { 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 ef46762fd893..5cb8190b4fde 100644 --- a/core/java/android/nfc/technology/NfcA.java +++ b/core/java/android/nfc/technology/NfcA.java @@ -25,7 +25,7 @@ import android.os.RemoteException; * 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 267c94d7fdf1..dc9dd7a3d84f 100644 --- a/core/java/android/nfc/technology/NfcB.java +++ b/core/java/android/nfc/technology/NfcB.java @@ -25,7 +25,7 @@ import android.os.RemoteException; * 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 6741ac825770..dd0e2f94fda2 100644 --- a/core/java/android/nfc/technology/NfcF.java +++ b/core/java/android/nfc/technology/NfcF.java @@ -25,7 +25,7 @@ import android.os.RemoteException; * 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 460de6afca5d..da73f5d01b17 100644 --- a/core/java/android/nfc/technology/NfcV.java +++ b/core/java/android/nfc/technology/NfcV.java @@ -25,7 +25,7 @@ import android.os.RemoteException; * 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 26b8a328b132..000000000000 --- 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 52b064348e55..a859e3ce3f81 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -517,8 +517,11 @@ public abstract class WallpaperService extends Service { 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 d24af52303d8..6b44f9e1250d 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -38,7 +38,7 @@ import java.util.HashMap; * 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 b68294796f16..b3b80f66838f 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -4087,7 +4087,7 @@ public final class BatteryStatsImpl extends BatteryStats { // 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 bc749d8819e8..2bc9dd1893b0 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.view.View; 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 @@ public class DigitalClock extends RelativeLayout { 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 @@ public class DigitalClock extends RelativeLayout { } } - 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 @@ public class DigitalClock extends RelativeLayout { 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 @@ public class DigitalClock extends RelativeLayout { } /* 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 @@ public class DigitalClock extends RelativeLayout { 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 @@ public class DigitalClock extends RelativeLayout { } 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 @@ public class DigitalClock extends RelativeLayout { ? 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 000000000000..2b6dee8b5da6 --- /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 a984acd18d39..ca934ec3a69e 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 9a6f7877cdee..b6e0c30fd65e 100644 --- a/drm/drmserver/DrmManager.cpp +++ b/drm/drmserver/DrmManager.cpp @@ -117,7 +117,11 @@ status_t DrmManager::unloadPlugIns() { 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 2f54b3393df9..6caf678d8558 100644 --- a/drm/java/android/drm/DrmManagerClient.java +++ b/drm/java/android/drm/DrmManagerClient.java @@ -738,26 +738,34 @@ public class DrmManagerClient { */ 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 == cursor || 0 == cursor.getCount() || !cursor.moveToFirst()) { - throw new IllegalArgumentException("Given Uri could not 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"); } - 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 578e1354bbf4..7b518226305a 100644 --- a/drm/libdrmframework/DrmManagerClient.cpp +++ b/drm/libdrmframework/DrmManagerClient.cpp @@ -31,6 +31,7 @@ DrmManagerClient::DrmManagerClient(): 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 f39131d5277b..d20de92fae07 100644 --- a/drm/libdrmframework/DrmManagerClientImpl.cpp +++ b/drm/libdrmframework/DrmManagerClientImpl.cpp @@ -81,7 +81,8 @@ status_t DrmManagerClientImpl::setOnInfoListener( 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 000000000000..9ee79618c203 --- /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 000000000000..15dda80cf7b9 --- /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 000000000000..4d12a61a6e6e --- /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 000000000000..3dff58c17ef7 --- /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 000000000000..4ee903ef01d2 --- /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 000000000000..9ee79618c203 --- /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 000000000000..d4a6f18dbbe1 --- /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 000000000000..34804cf6160a --- /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 000000000000..da95d6050283 --- /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 000000000000..d430f727236a --- /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 000000000000..9ee79618c203 --- /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 000000000000..6c5d3cf9bcd9 --- /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 000000000000..92bda8ffadb1 --- /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 000000000000..f36f6ea98e5a --- /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 000000000000..00bb788fa2cf --- /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 000000000000..14ea9e909fd2 --- /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 000000000000..e20c0c382c26 --- /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 000000000000..b625edf10a94 --- /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 000000000000..98284e722571 --- /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 000000000000..fc64050eb5ca --- /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 000000000000..8f95cd218a8a --- /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"'> </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"'> +</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"'> +</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"'> +</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"'> </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"'> </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"'> </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"'> </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"'> </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"'> </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"'> </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"'> +</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"'> </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"'> </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"'> </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"'> </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"'> </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"'> </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"'> </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"'> </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"'> </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"'> </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"'> </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"'> +</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 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> ≥ 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><to the end of the + file></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"'> +</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"'> +</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"'> </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"'> </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"'> +</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"'> </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"'> </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"'> +</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"'> +</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 Binary files differnew file mode 100644 index 000000000000..ee94513a0fa7 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif 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 Binary files differnew file mode 100644 index 000000000000..8c12f460a778 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif 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 Binary files differnew file mode 100644 index 000000000000..9e019cab97ac --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif 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 Binary files differnew file mode 100644 index 000000000000..cae1d0156bb9 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif 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 Binary files differnew file mode 100644 index 000000000000..0d87be96ad3b --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif 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 Binary files differnew file mode 100644 index 000000000000..9445b6ba2d9c --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif diff --git a/include/drm/DrmManagerClient.h b/include/drm/DrmManagerClient.h index e6ba3c470ce0..085ebf1b0412 100644 --- a/include/drm/DrmManagerClient.h +++ b/include/drm/DrmManagerClient.h @@ -49,6 +49,9 @@ public: 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 5c20811a048b..2253eb26e438 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 840c5e198e21..c4feefd2e32c 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 @@ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScree */ 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 @@ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScree 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 70a4b20bb61a..fcd4bf397d18 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 @@ public class KeyguardViewManager implements KeyguardWindowController { 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 822be468ac66..886b85fbb54c 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 class LockPatternKeyguardView extends KeyguardViewBase { 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 1b810d5832bb..2bc57b53f54c 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 @@ class LockScreen extends LinearLayout implements KeyguardScreen, 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 @@ class LockScreen extends LinearLayout implements KeyguardScreen, /** {@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 6815d5080c21..6c6c2cc843d7 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 @@ class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient 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 @@ class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient /** {@inheritDoc} */ public void cleanUp() { mUpdateMonitor.removeCallback(this); + mLockPatternUtils = null; + mUpdateMonitor = null; + mCallback = null; } @Override |