am b0976320: Merge "DO NOT MERGE: Backport more USB accessory changes from honeycomb" into gingerbread
* commit 'b09763209980ff9210cc353f2410598220ec0480':
DO NOT MERGE: Backport more USB accessory changes from honeycomb
diff --git a/Android.mk b/Android.mk
index 09e471f..e7271304 100644
--- a/Android.mk
+++ b/Android.mk
@@ -185,6 +185,7 @@
telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
telephony/java/com/android/internal/telephony/IIccPhoneBook.aidl \
telephony/java/com/android/internal/telephony/ISms.aidl \
+ telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
wifi/java/android/net/wifi/IWifiManager.aidl \
telephony/java/com/android/internal/telephony/IExtendedNetworkService.aidl \
vpn/java/android/net/vpn/IVpnService.aidl \
diff --git a/api/current.xml b/api/current.xml
index e10b357..c43075b 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -19598,6 +19598,18 @@
<parameter name="context" type="android.content.Context">
</parameter>
</constructor>
+<constructor name="AlertDialog.Builder"
+ type="android.app.AlertDialog.Builder"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="theme" type="int">
+</parameter>
+</constructor>
<method name="create"
return="android.app.AlertDialog"
abstract="false"
@@ -54220,6 +54232,42 @@
<parameter name="table" type="java.lang.String">
</parameter>
</method>
+<method name="queryNumEntries"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="db" type="android.database.sqlite.SQLiteDatabase">
+</parameter>
+<parameter name="table" type="java.lang.String">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+</method>
+<method name="queryNumEntries"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="db" type="android.database.sqlite.SQLiteDatabase">
+</parameter>
+<parameter name="table" type="java.lang.String">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+</method>
<method name="readExceptionFromParcel"
return="void"
abstract="false"
@@ -56797,6 +56845,29 @@
</parameter>
<parameter name="selection" type="java.lang.String">
</parameter>
+<parameter name="groupBy" type="java.lang.String">
+</parameter>
+<parameter name="having" type="java.lang.String">
+</parameter>
+<parameter name="sortOrder" type="java.lang.String">
+</parameter>
+<parameter name="limit" type="java.lang.String">
+</parameter>
+</method>
+<method name="buildQuery"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="projectionIn" type="java.lang.String[]">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
<parameter name="selectionArgs" type="java.lang.String[]">
</parameter>
<parameter name="groupBy" type="java.lang.String">
@@ -56874,6 +56945,33 @@
</parameter>
<parameter name="selection" type="java.lang.String">
</parameter>
+<parameter name="groupBy" type="java.lang.String">
+</parameter>
+<parameter name="having" type="java.lang.String">
+</parameter>
+</method>
+<method name="buildUnionSubQuery"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="typeDiscriminatorColumn" type="java.lang.String">
+</parameter>
+<parameter name="unionColumns" type="java.lang.String[]">
+</parameter>
+<parameter name="columnsPresentInTable" type="java.util.Set<java.lang.String>">
+</parameter>
+<parameter name="computedColumnsOffset" type="int">
+</parameter>
+<parameter name="typeDiscriminatorValue" type="java.lang.String">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
<parameter name="selectionArgs" type="java.lang.String[]">
</parameter>
<parameter name="groupBy" type="java.lang.String">
@@ -57092,6 +57190,1928 @@
</method>
</interface>
</package>
+<package name="android.drm"
+>
+<class name="DrmConvertedStatus"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmConvertedStatus"
+ type="android.drm.DrmConvertedStatus"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="_statusCode" type="int">
+</parameter>
+<parameter name="_convertedData" type="byte[]">
+</parameter>
+<parameter name="_offset" type="int">
+</parameter>
+</constructor>
+<field name="STATUS_ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_INPUTDATA_ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_OK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="convertedData"
+ type="byte[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="offset"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="statusCode"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DrmErrorEvent"
+ extends="android.drm.DrmEvent"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmErrorEvent"
+ type="android.drm.DrmErrorEvent"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uniqueId" type="int">
+</parameter>
+<parameter name="type" type="int">
+</parameter>
+<parameter name="message" type="java.lang.String">
+</parameter>
+</constructor>
+<field name="TYPE_NOT_SUPPORTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2003"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_NO_INTERNET_CONNECTION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2005"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_OUT_OF_MEMORY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2004"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_PROCESS_DRM_INFO_FAILED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2006"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_REMOVE_ALL_RIGHTS_FAILED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2007"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_RIGHTS_NOT_INSTALLED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2001"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_RIGHTS_RENEWAL_NOT_ALLOWED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2002"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DrmEvent"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmEvent"
+ type="android.drm.DrmEvent"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="uniqueId" type="int">
+</parameter>
+<parameter name="type" type="int">
+</parameter>
+<parameter name="message" type="java.lang.String">
+</parameter>
+</constructor>
+<method name="getMessage"
+ return="java.lang.String"
+ 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="getUniqueId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<field name="DRM_INFO_STATUS_OBJECT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""drm_info_status_object""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_ALL_RIGHTS_REMOVED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1001"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_DRM_INFO_PROCESSED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1002"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DrmInfo"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmInfo"
+ type="android.drm.DrmInfo"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="infoType" type="int">
+</parameter>
+<parameter name="data" type="byte[]">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="DrmInfo"
+ type="android.drm.DrmInfo"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="infoType" type="int">
+</parameter>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</constructor>
+<method name="get"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+</method>
+<method name="getData"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInfoType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMimeType"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="iterator"
+ return="java.util.Iterator<java.lang.Object>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="keyIterator"
+ return="java.util.Iterator<java.lang.String>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="put"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="value" type="java.lang.Object">
+</parameter>
+</method>
+</class>
+<class name="DrmInfoEvent"
+ extends="android.drm.DrmEvent"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmInfoEvent"
+ type="android.drm.DrmInfoEvent"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uniqueId" type="int">
+</parameter>
+<parameter name="type" type="int">
+</parameter>
+<parameter name="message" type="java.lang.String">
+</parameter>
+</constructor>
+<field name="TYPE_ACCOUNT_ALREADY_REGISTERED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_REMOVE_RIGHTS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_RIGHTS_INSTALLED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_WAIT_FOR_RIGHTS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DrmInfoRequest"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmInfoRequest"
+ type="android.drm.DrmInfoRequest"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="infoType" type="int">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</constructor>
+<method name="get"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+</method>
+<method name="getInfoType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMimeType"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="iterator"
+ return="java.util.Iterator<java.lang.Object>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="keyIterator"
+ return="java.util.Iterator<java.lang.String>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="put"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="value" type="java.lang.Object">
+</parameter>
+</method>
+<field name="ACCOUNT_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""account_id""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SUBSCRIPTION_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""subscription_id""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_REGISTRATION_INFO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_RIGHTS_ACQUISITION_INFO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_UNREGISTRATION_INFO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DrmInfoStatus"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmInfoStatus"
+ type="android.drm.DrmInfoStatus"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="_statusCode" type="int">
+</parameter>
+<parameter name="_infoType" type="int">
+</parameter>
+<parameter name="_data" type="android.drm.ProcessedData">
+</parameter>
+<parameter name="_mimeType" type="java.lang.String">
+</parameter>
+</constructor>
+<field name="STATUS_ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_OK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="data"
+ type="android.drm.ProcessedData"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="infoType"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="mimeType"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="statusCode"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DrmManagerClient"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmManagerClient"
+ type="android.drm.DrmManagerClient"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<method name="acquireDrmInfo"
+ return="android.drm.DrmInfo"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="drmInfoRequest" type="android.drm.DrmInfoRequest">
+</parameter>
+</method>
+<method name="acquireRights"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="drmInfoRequest" type="android.drm.DrmInfoRequest">
+</parameter>
+</method>
+<method name="canHandle"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</method>
+<method name="canHandle"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</method>
+<method name="checkRightsStatus"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+</method>
+<method name="checkRightsStatus"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="checkRightsStatus"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="action" type="int">
+</parameter>
+</method>
+<method name="checkRightsStatus"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="action" type="int">
+</parameter>
+</method>
+<method name="closeConvertSession"
+ return="android.drm.DrmConvertedStatus"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="convertId" type="int">
+</parameter>
+</method>
+<method name="convertData"
+ return="android.drm.DrmConvertedStatus"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="convertId" type="int">
+</parameter>
+<parameter name="inputData" type="byte[]">
+</parameter>
+</method>
+<method name="getAvailableDrmEngines"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getConstraints"
+ return="android.content.ContentValues"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="action" type="int">
+</parameter>
+</method>
+<method name="getConstraints"
+ return="android.content.ContentValues"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="action" type="int">
+</parameter>
+</method>
+<method name="getDrmObjectType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</method>
+<method name="getDrmObjectType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</method>
+<method name="getMetadata"
+ return="android.content.ContentValues"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+</method>
+<method name="getMetadata"
+ return="android.content.ContentValues"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="getOriginalMimeType"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+</method>
+<method name="getOriginalMimeType"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="openConvertSession"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</method>
+<method name="processDrmInfo"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="drmInfo" type="android.drm.DrmInfo">
+</parameter>
+</method>
+<method name="removeAllRights"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="removeRights"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+</method>
+<method name="removeRights"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="saveRights"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="drmRights" type="android.drm.DrmRights">
+</parameter>
+<parameter name="rightsPath" type="java.lang.String">
+</parameter>
+<parameter name="contentPath" type="java.lang.String">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="setOnErrorListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="errorListener" type="android.drm.DrmManagerClient.OnErrorListener">
+</parameter>
+</method>
+<method name="setOnEventListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="eventListener" type="android.drm.DrmManagerClient.OnEventListener">
+</parameter>
+</method>
+<method name="setOnInfoListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="infoListener" type="android.drm.DrmManagerClient.OnInfoListener">
+</parameter>
+</method>
+<field name="ERROR_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_UNKNOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-2000"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="DrmManagerClient.OnErrorListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onError"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="client" type="android.drm.DrmManagerClient">
+</parameter>
+<parameter name="event" type="android.drm.DrmErrorEvent">
+</parameter>
+</method>
+</interface>
+<interface name="DrmManagerClient.OnEventListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onEvent"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="client" type="android.drm.DrmManagerClient">
+</parameter>
+<parameter name="event" type="android.drm.DrmEvent">
+</parameter>
+<parameter name="attributes" type="java.util.HashMap<java.lang.String, java.lang.Object>">
+</parameter>
+</method>
+</interface>
+<interface name="DrmManagerClient.OnInfoListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onInfo"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="client" type="android.drm.DrmManagerClient">
+</parameter>
+<parameter name="event" type="android.drm.DrmInfoEvent">
+</parameter>
+</method>
+</interface>
+<class name="DrmRights"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmRights"
+ type="android.drm.DrmRights"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rightsFilePath" type="java.lang.String">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="DrmRights"
+ type="android.drm.DrmRights"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rightsFilePath" type="java.lang.String">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+<parameter name="accountId" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="DrmRights"
+ type="android.drm.DrmRights"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rightsFilePath" type="java.lang.String">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+<parameter name="accountId" type="java.lang.String">
+</parameter>
+<parameter name="subscriptionId" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="DrmRights"
+ type="android.drm.DrmRights"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rightsFile" type="java.io.File">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="DrmRights"
+ type="android.drm.DrmRights"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="android.drm.ProcessedData">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</constructor>
+<method name="getAccountId"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getData"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMimeType"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSubscriptionId"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="DrmStore"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmStore"
+ type="android.drm.DrmStore"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+</class>
+<class name="DrmStore.Action"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmStore.Action"
+ type="android.drm.DrmStore.Action"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="DEFAULT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISPLAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXECUTE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OUTPUT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PLAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PREVIEW"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RINGTONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TRANSFER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="DrmStore.ConstraintsColumns"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="EXTENDED_METADATA"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""extended_metadata""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="LICENSE_AVAILABLE_TIME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""license_available_time""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="LICENSE_EXPIRY_TIME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""license_expiry_time""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="LICENSE_START_TIME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""license_start_time""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MAX_REPEAT_COUNT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""max_repeat_count""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="REMAINING_REPEAT_COUNT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""remaining_repeat_count""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
+<class name="DrmStore.DrmObjectType"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmStore.DrmObjectType"
+ type="android.drm.DrmStore.DrmObjectType"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="CONTENT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RIGHTS_OBJECT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TRIGGER_OBJECT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="UNKNOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DrmStore.Playback"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmStore.Playback"
+ type="android.drm.DrmStore.Playback"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="PAUSE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RESUME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="START"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STOP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DrmStore.RightsStatus"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmStore.RightsStatus"
+ type="android.drm.DrmStore.RightsStatus"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="RIGHTS_EXPIRED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RIGHTS_INVALID"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RIGHTS_NOT_ACQUIRED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RIGHTS_VALID"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DrmSupportInfo"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmSupportInfo"
+ type="android.drm.DrmSupportInfo"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="addFileSuffix"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fileSuffix" type="java.lang.String">
+</parameter>
+</method>
+<method name="addMimeType"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</method>
+<method name="getDescriprition"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFileSuffixIterator"
+ return="java.util.Iterator<java.lang.String>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMimeTypeIterator"
+ return="java.util.Iterator<java.lang.String>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setDescription"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="description" type="java.lang.String">
+</parameter>
+</method>
+</class>
+<class name="DrmUtils"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DrmUtils"
+ type="android.drm.DrmUtils"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getExtendedMetadataParser"
+ return="android.drm.DrmUtils.ExtendedMetadataParser"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="extendedMetadata" type="byte[]">
+</parameter>
+</method>
+</class>
+<class name="DrmUtils.ExtendedMetadataParser"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="get"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+</method>
+<method name="iterator"
+ return="java.util.Iterator<java.lang.String>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="keyIterator"
+ return="java.util.Iterator<java.lang.String>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="ProcessedData"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getAccountId"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getData"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSubscriptionId"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+</package>
<package name="android.gesture"
>
<class name="Gesture"
@@ -121323,6 +123343,17 @@
visibility="public"
>
</constructor>
+<field name="BATTERY_HEALTH_COLD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="BATTERY_HEALTH_DEAD"
type="int"
transient="false"
@@ -124717,6 +126748,39 @@
<parameter name="tag" type="java.lang.String">
</parameter>
</method>
+<field name="ACTION_DROPBOX_ENTRY_ADDED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.intent.action.DROPBOX_ENTRY_ADDED""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_TAG"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""tag""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_TIME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""time""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="IS_EMPTY"
type="int"
transient="false"
@@ -210084,6 +212148,32 @@
<parameter name="object" type="T">
</parameter>
</method>
+<method name="addAll"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="collection" type="java.util.Collection<? extends T>">
+</parameter>
+</method>
+<method name="addAll"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="items" type="T...">
+</parameter>
+</method>
<method name="clear"
return="void"
abstract="false"
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 7a7f8ed..6650a71 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -49,6 +49,9 @@
#include "BootAnimation.h"
+#define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip"
+#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
+
namespace android {
// ---------------------------------------------------------------------------
@@ -244,14 +247,12 @@
mFlingerSurfaceControl = control;
mFlingerSurface = s;
- mAndroidAnimation = false;
- status_t err = mZip.open("/data/local/bootanimation.zip");
- if (err != NO_ERROR) {
- err = mZip.open("/system/media/bootanimation.zip");
- if (err != NO_ERROR) {
- mAndroidAnimation = true;
- }
- }
+ mAndroidAnimation = true;
+ if ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) &&
+ (mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR) ||
+ (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
+ (mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))
+ mAndroidAnimation = false;
return NO_ERROR;
}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 326e2c8..d764aa9 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -403,9 +403,11 @@
if (nonLocalized != null) {
return nonLocalized.toString();
}
- Resources r = getResources(pii);
- if (r != null) {
- return r.getString(res);
+ if (res != 0) {
+ Resources r = getResources(pii);
+ if (r != null) {
+ return r.getString(res);
+ }
}
return null;
}
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index a5a1f01..14536bd 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -34,6 +34,8 @@
{ AID_MEDIA, "media.player" },
{ AID_MEDIA, "media.camera" },
{ AID_MEDIA, "media.audio_policy" },
+ { AID_DRMIO, "drm.drmIOService" },
+ { AID_DRM, "drm.drmManager" },
{ AID_NFC, "nfc" },
{ AID_RADIO, "radio.phone" },
{ AID_RADIO, "radio.sms" },
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index b459320..9712b2b 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -265,12 +265,22 @@
public static class Builder {
private final AlertController.AlertParams P;
+ private int mTheme;
/**
* Constructor using a context for this builder and the {@link AlertDialog} it creates.
*/
public Builder(Context context) {
+ this(context, com.android.internal.R.style.Theme_Dialog_Alert);
+ }
+
+ /**
+ * Constructor using a context and theme for this builder and
+ * the {@link AlertDialog} it creates.
+ */
+ public Builder(Context context, int theme) {
P = new AlertController.AlertParams(context);
+ mTheme = theme;
}
/**
@@ -783,7 +793,7 @@
* to do and want this to be created and displayed.
*/
public AlertDialog create() {
- final AlertDialog dialog = new AlertDialog(P.mContext);
+ final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
dialog.setOnCancelListener(P.mOnCancelListener);
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 8f940d5..2382596 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -188,7 +188,7 @@
/**
* Return activity in receiverPackage that handles ACTION_APP_ERROR.
*
- * @param pm PackageManager isntance
+ * @param pm PackageManager instance
* @param errorPackage package which caused the error
* @param receiverPackage candidate package to receive the error
* @return activity component within receiverPackage which handles
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 46ad64c..0e473c9 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -53,7 +53,6 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.PackageParser.Package;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -88,11 +87,9 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.StatFs;
import android.os.Vibrator;
import android.os.FileUtils.FileStatus;
import android.os.storage.StorageManager;
-import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.ClipboardManager;
import android.util.AndroidRuntimeException;
@@ -267,18 +264,18 @@
public Looper getMainLooper() {
return mMainThread.getLooper();
}
-
+
@Override
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
}
-
+
@Override
public void setTheme(int resid) {
mThemeResource = resid;
}
-
+
@Override
public Resources.Theme getTheme() {
if (mTheme == null) {
@@ -444,7 +441,7 @@
}
if (!mFilesDir.exists()) {
if(!mFilesDir.mkdirs()) {
- Log.w(TAG, "Unable to create files directory");
+ Log.w(TAG, "Unable to create files directory " + mFilesDir.getPath());
return null;
}
FileUtils.setPermissions(
@@ -455,7 +452,7 @@
return mFilesDir;
}
}
-
+
@Override
public File getExternalFilesDir(String type) {
synchronized (mSync) {
@@ -487,7 +484,7 @@
return dir;
}
}
-
+
@Override
public File getCacheDir() {
synchronized (mSync) {
@@ -507,7 +504,7 @@
}
return mCacheDir;
}
-
+
@Override
public File getExternalCacheDir() {
synchronized (mSync) {
@@ -529,7 +526,7 @@
return mExternalCacheDir;
}
}
-
+
@Override
public File getFileStreamPath(String name) {
return makeFilename(getFilesDir(), name);
@@ -570,7 +567,7 @@
return (list != null) ? list : EMPTY_FILE_LIST;
}
-
+
private File getDatabasesDir() {
synchronized (mSync) {
if (mDatabasesDir == null) {
@@ -582,7 +579,7 @@
return mDatabasesDir;
}
}
-
+
@Override
public Drawable getWallpaper() {
return getWallpaperManager().getDrawable();
@@ -650,7 +647,7 @@
} catch (RemoteException e) {
}
}
-
+
@Override
public void sendBroadcast(Intent intent) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
@@ -1587,15 +1584,15 @@
final void setActivityToken(IBinder token) {
mActivityToken = token;
}
-
+
final void setOuterContext(Context context) {
mOuterContext = context;
}
-
+
final Context getOuterContext() {
return mOuterContext;
}
-
+
final IBinder getActivityToken() {
return mActivityToken;
}
@@ -1673,7 +1670,7 @@
public boolean releaseProvider(IContentProvider provider) {
return mMainThread.releaseProvider(provider);
}
-
+
private final ActivityThread mMainThread;
}
@@ -1706,7 +1703,7 @@
throw new RuntimeException("Package manager has died", e);
}
}
-
+
@Override
public String[] canonicalToCurrentPackageNames(String[] names) {
try {
@@ -1715,7 +1712,7 @@
throw new RuntimeException("Package manager has died", e);
}
}
-
+
@Override
public Intent getLaunchIntentForPackage(String packageName) {
// First see if the package has an INFO activity; the existence of
@@ -1737,8 +1734,9 @@
if (resolveInfo == null) {
return null;
}
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClassName(packageName, resolveInfo.activityInfo.name);
+ Intent intent = new Intent(intentToResolve);
+ intent.setClassName(resolveInfo.activityInfo.applicationInfo.packageName,
+ resolveInfo.activityInfo.name);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
@@ -1904,7 +1902,7 @@
throw new RuntimeException("Package manager has died", e);
}
}
-
+
@Override
public boolean hasSystemFeature(String name) {
try {
@@ -1913,7 +1911,7 @@
throw new RuntimeException("Package manager has died", e);
}
}
-
+
@Override
public int checkPermission(String permName, String pkgName) {
try {
@@ -1985,9 +1983,9 @@
throw new RuntimeException("Package manager has died", e);
}
}
-
+
@Override
- public int getUidForSharedUser(String sharedUserName)
+ public int getUidForSharedUser(String sharedUserName)
throws NameNotFoundException {
try {
int uid = mPM.getUidForSharedUser(sharedUserName);
@@ -2391,7 +2389,7 @@
}
}
}
-
+
private static final class ResourceName {
final String packageName;
final int iconId;
@@ -2563,7 +2561,7 @@
}
}
@Override
- public void clearApplicationUserData(String packageName,
+ public void clearApplicationUserData(String packageName,
IPackageDataObserver observer) {
try {
mPM.clearApplicationUserData(packageName, observer);
@@ -2572,7 +2570,7 @@
}
}
@Override
- public void deleteApplicationCacheFiles(String packageName,
+ public void deleteApplicationCacheFiles(String packageName,
IPackageDataObserver observer) {
try {
mPM.deleteApplicationCacheFiles(packageName, observer);
@@ -2597,9 +2595,9 @@
// Should never happen!
}
}
-
+
@Override
- public void getPackageSizeInfo(String packageName,
+ public void getPackageSizeInfo(String packageName,
IPackageStatsObserver observer) {
try {
mPM.getPackageSizeInfo(packageName, observer);
@@ -2644,7 +2642,7 @@
// Should never happen!
}
}
-
+
@Override
public void replacePreferredActivity(IntentFilter filter,
int match, ComponentName[] set, ComponentName activity) {
@@ -2663,7 +2661,7 @@
// Should never happen!
}
}
-
+
@Override
public int getPreferredActivities(List<IntentFilter> outFilters,
List<ComponentName> outActivities, String packageName) {
@@ -2674,7 +2672,7 @@
}
return 0;
}
-
+
@Override
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags) {
@@ -2704,7 +2702,7 @@
// Should never happen!
}
}
-
+
@Override
public int getApplicationEnabledSetting(String packageName) {
try {
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 009671f..8ba480d 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -21,7 +21,6 @@
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.text.TextUtils.TruncateAt;
-import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.DatePicker;
@@ -39,13 +38,13 @@
* <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
* tutorial</a>.</p>
*/
-public class DatePickerDialog extends AlertDialog implements OnClickListener,
+public class DatePickerDialog extends AlertDialog implements OnClickListener,
OnDateChangedListener {
private static final String YEAR = "year";
private static final String MONTH = "month";
private static final String DAY = "day";
-
+
private final DatePicker mDatePicker;
private final OnDateSetListener mCallBack;
private final Calendar mCalendar;
@@ -83,7 +82,7 @@
int year,
int monthOfYear,
int dayOfMonth) {
- this(context, com.android.internal.R.style.Theme_Dialog_Alert,
+ this(context, com.android.internal.R.style.Theme_Dialog_Alert,
callBack, year, monthOfYear, dayOfMonth);
}
@@ -109,17 +108,17 @@
mInitialDay = dayOfMonth;
DateFormatSymbols symbols = new DateFormatSymbols();
mWeekDays = symbols.getShortWeekdays();
-
+
mTitleDateFormat = java.text.DateFormat.
getDateInstance(java.text.DateFormat.FULL);
mCalendar = Calendar.getInstance();
updateTitle(mInitialYear, mInitialMonth, mInitialDay);
-
- setButton(context.getText(R.string.date_time_set), this);
- setButton2(context.getText(R.string.cancel), (OnClickListener) null);
+
+ setButton(BUTTON_POSITIVE, context.getText(R.string.date_time_set), this);
+ setButton(BUTTON_NEGATIVE, context.getText(R.string.cancel), (OnClickListener) null);
setIcon(R.drawable.ic_dialog_time);
-
- LayoutInflater inflater =
+
+ LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.date_picker_dialog, null);
setView(view);
@@ -139,20 +138,20 @@
title.setSingleLine();
title.setEllipsize(TruncateAt.END);
}
-
+
public void onClick(DialogInterface dialog, int which) {
if (mCallBack != null) {
mDatePicker.clearFocus();
- mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
+ mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
}
}
-
+
public void onDateChanged(DatePicker view, int year,
int month, int day) {
updateTitle(year, month, day);
}
-
+
public void updateDate(int year, int monthOfYear, int dayOfMonth) {
mInitialYear = year;
mInitialMonth = monthOfYear;
@@ -166,7 +165,7 @@
mCalendar.set(Calendar.DAY_OF_MONTH, day);
setTitle(mTitleDateFormat.format(mCalendar.getTime()));
}
-
+
@Override
public Bundle onSaveInstanceState() {
Bundle state = super.onSaveInstanceState();
@@ -175,7 +174,7 @@
state.putInt(DAY, mDatePicker.getDayOfMonth());
return state;
}
-
+
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index a24fcae..7845e03 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -380,7 +380,7 @@
if (r != null) {
win = performDestroy(r, finish);
if (finish) {
- mActivities.remove(r);
+ mActivities.remove(id);
}
}
return win;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 856943d..414a2a1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -374,7 +374,7 @@
if (this.contentView != null) {
that.contentView = this.contentView.clone();
}
- that.iconLevel = that.iconLevel;
+ that.iconLevel = this.iconLevel;
that.sound = this.sound; // android.net.Uri is immutable
that.audioStreamType = this.audioStreamType;
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 1fae516..e3242c1 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -39,13 +39,17 @@
* </ul>
*
* <p>
- * Each of the notify methods takes an int id parameter. This id identifies
- * this notification from your app to the system, so that id should be unique
- * within your app. If you call one of the notify methods with an id that is
- * currently active and a new set of notification parameters, it will be
- * updated. For example, if you pass a new status bar icon, the old icon in
- * the status bar will be replaced with the new one. This is also the same
- * id you pass to the {@link #cancel} method to clear this notification.
+ * Each of the notify methods takes an int id parameter and optionally a
+ * {@link String} tag parameter, which may be {@code null}. These parameters
+ * are used to form a pair (tag, id), or ({@code null}, id) if tag is
+ * unspecified. This pair identifies this notification from your app to the
+ * system, so that pair should be unique within your app. If you call one
+ * of the notify methods with a (tag, id) pair that is currently active and
+ * a new set of notification parameters, it will be updated. For example,
+ * if you pass a new status bar icon, the old icon in the status bar will
+ * be replaced with the new one. This is also the same tag and id you pass
+ * to the {@link #cancel(int)} or {@link #cancel(String, int)} method to clear
+ * this notification.
*
* <p>
* You do not instantiate this class directly; instead, retrieve it through
@@ -94,12 +98,11 @@
/**
* Persistent notification on the status bar,
*
- * @param tag An string identifier for this notification unique within your
- * application.
+ * @param tag A string identifier for this notification. May be {@code null}.
+ * @param id An identifier for this notification. The pair (tag, id) must be unique
+ * within your application.
* @param notification A {@link Notification} object describing how to
* notify the user, other than the view you're providing. Must not be null.
- * @return the id of the notification that is associated with the string identifier that
- * can be used to cancel the notification
*/
public void notify(String tag, int id, Notification notification)
{
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index cd22fa1..4eb89ae 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -1109,6 +1109,9 @@
* @return true if a successful launch, false if could not (e.g. bad position).
*/
protected boolean launchSuggestion(int position, int actionKey, String actionMsg) {
+ if (mSuggestionsAdapter == null) {
+ return false;
+ }
Cursor c = mSuggestionsAdapter.getCursor();
if ((c != null) && c.moveToPosition(position)) {
@@ -1133,7 +1136,7 @@
try {
// If the intent was created from a suggestion, it will always have an explicit
// component here.
- Log.i(LOG_TAG, "Starting (as ourselves) " + intent.toURI());
+ Log.i(LOG_TAG, "Starting (as ourselves) " + intent.toUri(0));
getContext().startActivity(intent);
// If the search switches to a different activity,
// SearchDialogWrapper#performActivityResuming
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 62d3f7d..521d41c 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -36,7 +36,7 @@
* <p>See the <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Time Picker
* tutorial</a>.</p>
*/
-public class TimePickerDialog extends AlertDialog implements OnClickListener,
+public class TimePickerDialog extends AlertDialog implements OnClickListener,
OnTimeChangedListener {
/**
@@ -56,12 +56,12 @@
private static final String HOUR = "hour";
private static final String MINUTE = "minute";
private static final String IS_24_HOUR = "is24hour";
-
+
private final TimePicker mTimePicker;
private final OnTimeSetListener mCallback;
private final Calendar mCalendar;
private final java.text.DateFormat mDateFormat;
-
+
int mInitialHourOfDay;
int mInitialMinute;
boolean mIs24HourView;
@@ -101,12 +101,13 @@
mDateFormat = DateFormat.getTimeFormat(context);
mCalendar = Calendar.getInstance();
updateTitle(mInitialHourOfDay, mInitialMinute);
-
- setButton(context.getText(R.string.date_time_set), this);
- setButton2(context.getText(R.string.cancel), (OnClickListener) null);
+
+ setButton(BUTTON_POSITIVE, context.getText(R.string.date_time_set), this);
+ setButton(BUTTON_NEGATIVE, context.getText(R.string.cancel),
+ (OnClickListener) null);
setIcon(R.drawable.ic_dialog_time);
-
- LayoutInflater inflater =
+
+ LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.time_picker_dialog, null);
setView(view);
@@ -118,11 +119,11 @@
mTimePicker.setIs24HourView(mIs24HourView);
mTimePicker.setOnTimeChangedListener(this);
}
-
+
public void onClick(DialogInterface dialog, int which) {
if (mCallback != null) {
mTimePicker.clearFocus();
- mCallback.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
+ mCallback.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
mTimePicker.getCurrentMinute());
}
}
@@ -130,7 +131,7 @@
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
updateTitle(hourOfDay, minute);
}
-
+
public void updateTime(int hourOfDay, int minutOfHour) {
mTimePicker.setCurrentHour(hourOfDay);
mTimePicker.setCurrentMinute(minutOfHour);
@@ -141,7 +142,7 @@
mCalendar.set(Calendar.MINUTE, minute);
setTitle(mDateFormat.format(mCalendar.getTime()));
}
-
+
@Override
public Bundle onSaveInstanceState() {
Bundle state = super.onSaveInstanceState();
@@ -150,7 +151,7 @@
state.putBoolean(IS_24_HOUR, mTimePicker.is24HourView());
return state;
}
-
+
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index e455a59..92b7cf5 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -235,8 +235,13 @@
if (width <= 0 || height <= 0) {
// Degenerate case: no size requested, just load
// bitmap as-is.
- Bitmap bm = BitmapFactory.decodeFileDescriptor(
- fd.getFileDescriptor(), null, null);
+ Bitmap bm = null;
+ try {
+ bm = BitmapFactory.decodeFileDescriptor(
+ fd.getFileDescriptor(), null, null);
+ } catch (OutOfMemoryError e) {
+ Log.w(TAG, "Can't decode file", e);
+ }
try {
fd.close();
} catch (IOException e) {
@@ -277,7 +282,12 @@
if (width <= 0 || height <= 0) {
// Degenerate case: no size requested, just load
// bitmap as-is.
- Bitmap bm = BitmapFactory.decodeStream(is, null, null);
+ Bitmap bm = null;
+ try {
+ bm = BitmapFactory.decodeStream(is, null, null);
+ } catch (OutOfMemoryError e) {
+ Log.w(TAG, "Can't decode stream", e);
+ }
try {
is.close();
} catch (IOException e) {
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index d4ce6a1..d2ab85e 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -22,7 +22,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.util.TypedValue;
import android.widget.RemoteViews;
@@ -149,7 +148,7 @@
* instances as possible.</td>
* </tr>
* </table>
- *
+ *
* @see AppWidgetProvider#onUpdate AppWidgetProvider.onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
*/
public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
@@ -163,7 +162,7 @@
/**
* Sent when an instance of an AppWidget is removed from the last host.
- *
+ *
* @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
*/
public static final String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
@@ -172,7 +171,7 @@
* Sent when an instance of an AppWidget is added to a host for the first time.
* This broadcast is sent at boot time if there is a AppWidgetHost installed with
* an instance for this provider.
- *
+ *
* @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
*/
public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
@@ -183,20 +182,21 @@
* @see AppWidgetProviderInfo
*/
public static final String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
-
+
/**
* Field for the manifest meta-data tag used to indicate any previous name for the
* app widget receiver.
*
* @see AppWidgetProviderInfo
- *
+ *
* @hide Pending API approval
*/
public static final String META_DATA_APPWIDGET_OLD_NAME = "android.appwidget.oldName";
- static WeakHashMap<Context, WeakReference<AppWidgetManager>> sManagerCache = new WeakHashMap();
+ static WeakHashMap<Context, WeakReference<AppWidgetManager>> sManagerCache =
+ new WeakHashMap<Context, WeakReference<AppWidgetManager>>();
static IAppWidgetService sService;
-
+
Context mContext;
private DisplayMetrics mDisplayMetrics;
@@ -219,7 +219,7 @@
}
if (result == null) {
result = new AppWidgetManager(context);
- sManagerCache.put(context, new WeakReference(result));
+ sManagerCache.put(context, new WeakReference<AppWidgetManager>(result));
}
return result;
}
@@ -310,7 +310,7 @@
AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId);
if (info != null) {
// Converting complex to dp.
- info.minWidth =
+ info.minWidth =
TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
info.minHeight =
TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
@@ -344,7 +344,7 @@
/**
* Get the list of appWidgetIds that have been bound to the given AppWidget
* provider.
- *
+ *
* @param provider The {@link android.content.BroadcastReceiver} that is the
* AppWidget provider to find appWidgetIds for.
*/
diff --git a/core/java/android/bluetooth/AtCommandHandler.java b/core/java/android/bluetooth/AtCommandHandler.java
index 8de2133..6deab34 100644
--- a/core/java/android/bluetooth/AtCommandHandler.java
+++ b/core/java/android/bluetooth/AtCommandHandler.java
@@ -73,7 +73,7 @@
* least one element in this array.
* @return The result of this command.
*/
- // Typically used to set this paramter
+ // Typically used to set this parameter
public AtCommandResult handleSetCommand(Object[] args) {
return new AtCommandResult(AtCommandResult.ERROR);
}
@@ -83,11 +83,12 @@
* Test commands are part of the Extended command syntax, and are typically
* used to request an indication of the range of legal values that "FOO"
* can take.<p>
- * By defualt we return an OK result, to indicate that this command is at
+ * By default we return an OK result, to indicate that this command is at
* least recognized.<p>
* @return The result of this command.
*/
public AtCommandResult handleTestCommand() {
return new AtCommandResult(AtCommandResult.OK);
}
+
}
diff --git a/core/java/android/bluetooth/BluetoothAssignedNumbers.java b/core/java/android/bluetooth/BluetoothAssignedNumbers.java
new file mode 100644
index 0000000..55bc814
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothAssignedNumbers.java
@@ -0,0 +1,523 @@
+/*
+ * 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.bluetooth;
+
+/**
+ * Bluetooth Assigned Numbers.
+ * <p>
+ * For now we only include Company ID values.
+ * @see <a href="https://www.bluetooth.org/technical/assignednumbers/identifiers.htm">
+ * The Official Bluetooth SIG Member Website | Company Identifiers</a>
+ *
+ * @hide
+ */
+public class BluetoothAssignedNumbers {
+
+ //// Bluetooth SIG Company ID values
+
+ /*
+ * Ericsson Technology Licensing.
+ */
+ public static final int ERICSSON_TECHNOLOGY = 0x0000;
+
+ /*
+ * Nokia Mobile Phones.
+ */
+ public static final int NOKIA_MOBILE_PHONES = 0x0001;
+
+ /*
+ * Intel Corp.
+ */
+ public static final int INTEL = 0x0002;
+
+ /*
+ * IBM Corp.
+ */
+ public static final int IBM = 0x0003;
+
+ /*
+ * Toshiba Corp.
+ */
+ public static final int TOSHIBA = 0x0004;
+
+ /*
+ * 3Com.
+ */
+ public static final int THREECOM = 0x0005;
+
+ /*
+ * Microsoft.
+ */
+ public static final int MICROSOFT = 0x0006;
+
+ /*
+ * Lucent.
+ */
+ public static final int LUCENT = 0x0007;
+
+ /*
+ * Motorola.
+ */
+ public static final int MOTOROLA = 0x0008;
+
+ /*
+ * Infineon Technologies AG.
+ */
+ public static final int INFINEON_TECHNOLOGIES = 0x0009;
+
+ /*
+ * Cambridge Silicon Radio.
+ */
+ public static final int CAMBRIDGE_SILICON_RADIO = 0x000A;
+
+ /*
+ * Silicon Wave.
+ */
+ public static final int SILICON_WAVE = 0x000B;
+
+ /*
+ * Digianswer A/S.
+ */
+ public static final int DIGIANSWER = 0x000C;
+
+ /*
+ * Texas Instruments Inc.
+ */
+ public static final int TEXAS_INSTRUMENTS = 0x000D;
+
+ /*
+ * Parthus Technologies Inc.
+ */
+ public static final int PARTHUS_TECHNOLOGIES = 0x000E;
+
+ /*
+ * Broadcom Corporation.
+ */
+ public static final int BROADCOM = 0x000F;
+
+ /*
+ * Mitel Semiconductor.
+ */
+ public static final int MITEL_SEMICONDUCTOR = 0x0010;
+
+ /*
+ * Widcomm, Inc.
+ */
+ public static final int WIDCOMM = 0x0011;
+
+ /*
+ * Zeevo, Inc.
+ */
+ public static final int ZEEVO = 0x0012;
+
+ /*
+ * Atmel Corporation.
+ */
+ public static final int ATMEL = 0x0013;
+
+ /*
+ * Mitsubishi Electric Corporation.
+ */
+ public static final int MITSUBISHI_ELECTRIC = 0x0014;
+
+ /*
+ * RTX Telecom A/S.
+ */
+ public static final int RTX_TELECOM = 0x0015;
+
+ /*
+ * KC Technology Inc.
+ */
+ public static final int KC_TECHNOLOGY = 0x0016;
+
+ /*
+ * Newlogic.
+ */
+ public static final int NEWLOGIC = 0x0017;
+
+ /*
+ * Transilica, Inc.
+ */
+ public static final int TRANSILICA = 0x0018;
+
+ /*
+ * Rohde & Schwarz GmbH & Co. KG.
+ */
+ public static final int ROHDE_AND_SCHWARZ = 0x0019;
+
+ /*
+ * TTPCom Limited.
+ */
+ public static final int TTPCOM = 0x001A;
+
+ /*
+ * Signia Technologies, Inc.
+ */
+ public static final int SIGNIA_TECHNOLOGIES = 0x001B;
+
+ /*
+ * Conexant Systems Inc.
+ */
+ public static final int CONEXANT_SYSTEMS = 0x001C;
+
+ /*
+ * Qualcomm.
+ */
+ public static final int QUALCOMM = 0x001D;
+
+ /*
+ * Inventel.
+ */
+ public static final int INVENTEL = 0x001E;
+
+ /*
+ * AVM Berlin.
+ */
+ public static final int AVM_BERLIN = 0x001F;
+
+ /*
+ * BandSpeed, Inc.
+ */
+ public static final int BANDSPEED = 0x0020;
+
+ /*
+ * Mansella Ltd.
+ */
+ public static final int MANSELLA = 0x0021;
+
+ /*
+ * NEC Corporation.
+ */
+ public static final int NEC = 0x0022;
+
+ /*
+ * WavePlus Technology Co., Ltd.
+ */
+ public static final int WAVEPLUS_TECHNOLOGY = 0x0023;
+
+ /*
+ * Alcatel.
+ */
+ public static final int ALCATEL = 0x0024;
+
+ /*
+ * Philips Semiconductors.
+ */
+ public static final int PHILIPS_SEMICONDUCTORS = 0x0025;
+
+ /*
+ * C Technologies.
+ */
+ public static final int C_TECHNOLOGIES = 0x0026;
+
+ /*
+ * Open Interface.
+ */
+ public static final int OPEN_INTERFACE = 0x0027;
+
+ /*
+ * R F Micro Devices.
+ */
+ public static final int RF_MICRO_DEVICES = 0x0028;
+
+ /*
+ * Hitachi Ltd.
+ */
+ public static final int HITACHI = 0x0029;
+
+ /*
+ * Symbol Technologies, Inc.
+ */
+ public static final int SYMBOL_TECHNOLOGIES = 0x002A;
+
+ /*
+ * Tenovis.
+ */
+ public static final int TENOVIS = 0x002B;
+
+ /*
+ * Macronix International Co. Ltd.
+ */
+ public static final int MACRONIX = 0x002C;
+
+ /*
+ * GCT Semiconductor.
+ */
+ public static final int GCT_SEMICONDUCTOR = 0x002D;
+
+ /*
+ * Norwood Systems.
+ */
+ public static final int NORWOOD_SYSTEMS = 0x002E;
+
+ /*
+ * MewTel Technology Inc.
+ */
+ public static final int MEWTEL_TECHNOLOGY = 0x002F;
+
+ /*
+ * ST Microelectronics.
+ */
+ public static final int ST_MICROELECTRONICS = 0x0030;
+
+ /*
+ * Synopsys.
+ */
+ public static final int SYNOPSYS = 0x0031;
+
+ /*
+ * Red-M (Communications) Ltd.
+ */
+ public static final int RED_M = 0x0032;
+
+ /*
+ * Commil Ltd.
+ */
+ public static final int COMMIL = 0x0033;
+
+ /*
+ * Computer Access Technology Corporation (CATC).
+ */
+ public static final int CATC = 0x0034;
+
+ /*
+ * Eclipse (HQ Espana) S.L.
+ */
+ public static final int ECLIPSE = 0x0035;
+
+ /*
+ * Renesas Technology Corp.
+ */
+ public static final int RENESAS_TECHNOLOGY = 0x0036;
+
+ /*
+ * Mobilian Corporation.
+ */
+ public static final int MOBILIAN_CORPORATION = 0x0037;
+
+ /*
+ * Terax.
+ */
+ public static final int TERAX = 0x0038;
+
+ /*
+ * Integrated System Solution Corp.
+ */
+ public static final int INTEGRATED_SYSTEM_SOLUTION = 0x0039;
+
+ /*
+ * Matsushita Electric Industrial Co., Ltd.
+ */
+ public static final int MATSUSHITA_ELECTRIC = 0x003A;
+
+ /*
+ * Gennum Corporation.
+ */
+ public static final int GENNUM = 0x003B;
+
+ /*
+ * Research In Motion.
+ */
+ public static final int RESEARCH_IN_MOTION = 0x003C;
+
+ /*
+ * IPextreme, Inc.
+ */
+ public static final int IPEXTREME = 0x003D;
+
+ /*
+ * Systems and Chips, Inc.
+ */
+ public static final int SYSTEMS_AND_CHIPS = 0x003E;
+
+ /*
+ * Bluetooth SIG, Inc.
+ */
+ public static final int BLUETOOTH_SIG = 0x003F;
+
+ /*
+ * Seiko Epson Corporation.
+ */
+ public static final int SEIKO_EPSON = 0x0040;
+
+ /*
+ * Integrated Silicon Solution Taiwan, Inc.
+ */
+ public static final int INTEGRATED_SILICON_SOLUTION = 0x0041;
+
+ /*
+ * CONWISE Technology Corporation Ltd.
+ */
+ public static final int CONWISE_TECHNOLOGY = 0x0042;
+
+ /*
+ * PARROT SA.
+ */
+ public static final int PARROT = 0x0043;
+
+ /*
+ * Socket Mobile.
+ */
+ public static final int SOCKET_MOBILE = 0x0044;
+
+ /*
+ * Atheros Communications, Inc.
+ */
+ public static final int ATHEROS_COMMUNICATIONS = 0x0045;
+
+ /*
+ * MediaTek, Inc.
+ */
+ public static final int MEDIATEK = 0x0046;
+
+ /*
+ * Bluegiga.
+ */
+ public static final int BLUEGIGA = 0x0047;
+
+ /*
+ * Marvell Technology Group Ltd.
+ */
+ public static final int MARVELL = 0x0048;
+
+ /*
+ * 3DSP Corporation.
+ */
+ public static final int THREE_DSP = 0x0049;
+
+ /*
+ * Accel Semiconductor Ltd.
+ */
+ public static final int ACCEL_SEMICONDUCTOR = 0x004A;
+
+ /*
+ * Continental Automotive Systems.
+ */
+ public static final int CONTINENTAL_AUTOMOTIVE = 0x004B;
+
+ /*
+ * Apple, Inc.
+ */
+ public static final int APPLE = 0x004C;
+
+ /*
+ * Staccato Communications, Inc.
+ */
+ public static final int STACCATO_COMMUNICATIONS = 0x004D;
+
+ /*
+ * Avago Technologies.
+ */
+ public static final int AVAGO = 0x004E;
+
+ /*
+ * APT Licensing Ltd.
+ */
+ public static final int APT_LICENSING = 0x004F;
+
+ /*
+ * SiRF Technology, Inc.
+ */
+ public static final int SIRF_TECHNOLOGY = 0x0050;
+
+ /*
+ * Tzero Technologies, Inc.
+ */
+ public static final int TZERO_TECHNOLOGIES = 0x0051;
+
+ /*
+ * J&M Corporation.
+ */
+ public static final int J_AND_M = 0x0052;
+
+ /*
+ * Free2move AB.
+ */
+ public static final int FREE2MOVE = 0x0053;
+
+ /*
+ * 3DiJoy Corporation.
+ */
+ public static final int THREE_DIJOY = 0x0054;
+
+ /*
+ * Plantronics, Inc.
+ */
+ public static final int PLANTRONICS = 0x0055;
+
+ /*
+ * Sony Ericsson Mobile Communications.
+ */
+ public static final int SONY_ERICSSON = 0x0056;
+
+ /*
+ * Harman International Industries, Inc.
+ */
+ public static final int HARMAN_INTERNATIONAL = 0x0057;
+
+ /*
+ * Vizio, Inc.
+ */
+ public static final int VIZIO = 0x0058;
+
+ /*
+ * Nordic Semiconductor ASA.
+ */
+ public static final int NORDIC_SEMICONDUCTOR = 0x0059;
+
+ /*
+ * EM Microelectronic-Marin SA.
+ */
+ public static final int EM_MICROELECTRONIC_MARIN = 0x005A;
+
+ /*
+ * Ralink Technology Corporation.
+ */
+ public static final int RALINK_TECHNOLOGY = 0x005B;
+
+ /*
+ * Belkin International, Inc.
+ */
+ public static final int BELKIN_INTERNATIONAL = 0x005C;
+
+ /*
+ * Realtek Semiconductor Corporation.
+ */
+ public static final int REALTEK_SEMICONDUCTOR = 0x005D;
+
+ /*
+ * Stonestreet One, LLC.
+ */
+ public static final int STONESTREET_ONE = 0x005E;
+
+ /*
+ * Wicentric, Inc.
+ */
+ public static final int WICENTRIC = 0x005F;
+
+ /*
+ * RivieraWaves S.A.S.
+ */
+ public static final int RIVIERAWAVES = 0x0060;
+
+ /*
+ * You can't instantiate one of these.
+ */
+ private BluetoothAssignedNumbers() {
+ }
+
+}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 197b022..da1aa45 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -85,6 +85,43 @@
"android.bluetooth.headset.extra.DISCONNECT_INITIATOR";
/**
+ * Broadcast Action: Indicates a headset has posted a vendor-specific event.
+ * <p>Always contains the extra fields {@link #EXTRA_DEVICE},
+ * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD}, and
+ * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
+ "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
+
+ /**
+ * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
+ * intents that contains the name of the vendor-specific command.
+ */
+ public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD =
+ "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD";
+
+ /**
+ * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
+ * intents that contains the Company ID of the vendor defining the vendor-specific
+ * command.
+ * @see <a href="https://www.bluetooth.org/Technical/AssignedNumbers/identifiers.htm">
+ * Bluetooth SIG Assigned Numbers - Company Identifiers</a>
+ */
+ public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID =
+ "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID";
+
+ /**
+ * A Parcelable String array extra field in
+ * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains
+ * the arguments to the vendor-specific command.
+ */
+ public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
+ "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
+
+
+ /**
* TODO(API release): Consider incorporating as new state in
* HEADSET_STATE_CHANGED
*/
diff --git a/core/java/android/bluetooth/HeadsetBase.java b/core/java/android/bluetooth/HeadsetBase.java
index 22495a7..9ef2eb5 100644
--- a/core/java/android/bluetooth/HeadsetBase.java
+++ b/core/java/android/bluetooth/HeadsetBase.java
@@ -74,8 +74,8 @@
private native void cleanupNativeDataNative();
- public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device,
- int rfcommChannel) {
+ public HeadsetBase(PowerManager pm, BluetoothAdapter adapter,
+ BluetoothDevice device, int rfcommChannel) {
mDirection = DIRECTION_OUTGOING;
mConnectTimestamp = System.currentTimeMillis();
mAdapter = adapter;
@@ -89,9 +89,10 @@
initializeNativeDataNative(-1);
}
- /* Create from an already existing rfcomm connection */
- public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device,
- int socketFd, int rfcommChannel, Handler handler) {
+ /* Create from an existing rfcomm connection */
+ public HeadsetBase(PowerManager pm, BluetoothAdapter adapter,
+ BluetoothDevice device,
+ int socketFd, int rfcommChannel, Handler handler) {
mDirection = DIRECTION_INCOMING;
mConnectTimestamp = System.currentTimeMillis();
mAdapter = adapter;
@@ -142,8 +143,9 @@
*/
protected void initializeAtParser() {
mAtParser = new AtParser();
- //TODO(): Get rid of this as there are no parsers registered. But because of dependencies,
- //it needs to be done as part of refactoring HeadsetBase and BluetoothHandsfree
+
+ //TODO(): Get rid of this as there are no parsers registered. But because of dependencies
+ // it needs to be done as part of refactoring HeadsetBase and BluetoothHandsfree
}
public AtParser getAtParser() {
@@ -159,8 +161,7 @@
String input = readNative(500);
if (input != null) {
handleInput(input);
- }
- else {
+ } else {
last_read_error = getLastReadStatusNative();
if (last_read_error != 0) {
Log.i(TAG, "headset read error " + last_read_error);
@@ -179,8 +180,6 @@
mEventThread.start();
}
-
-
private native String readNative(int timeout_ms);
private native int getLastReadStatusNative();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 81ff414..d16b3d8 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -201,6 +201,7 @@
} catch (RemoteException e) {
return null;
} catch (java.lang.Exception e) {
+ Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
return null;
} finally {
releaseProvider(provider);
@@ -216,6 +217,9 @@
return type;
} catch (RemoteException e) {
return null;
+ } catch (java.lang.Exception e) {
+ Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
+ return null;
}
}
@@ -1394,9 +1398,11 @@
@Override
protected void finalize() throws Throwable {
+ // TODO: integrate CloseGuard support.
try {
if(!mCloseFlag) {
- ContentResolver.this.releaseProvider(mContentProvider);
+ Log.w(TAG, "Cursor finalized without prior close()");
+ close();
}
} finally {
super.finalize();
diff --git a/core/java/android/content/UriMatcher.java b/core/java/android/content/UriMatcher.java
index 72ec469..841c8f4 100644
--- a/core/java/android/content/UriMatcher.java
+++ b/core/java/android/content/UriMatcher.java
@@ -25,8 +25,8 @@
/**
Utility class to aid in matching URIs in content providers.
-<p>To use this class, build up a tree of UriMatcher objects.
-Typically, it looks something like this:
+<p>To use this class, build up a tree of <code>UriMatcher</code> objects.
+For example:
<pre>
private static final int PEOPLE = 1;
private static final int PEOPLE_ID = 2;
@@ -48,36 +48,35 @@
private static final int CALLS_ID = 12;
private static final int CALLS_FILTER = 15;
- private static final UriMatcher sURIMatcher = new UriMatcher();
+ private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static
{
- sURIMatcher.addURI("contacts", "/people", PEOPLE);
- sURIMatcher.addURI("contacts", "/people/#", PEOPLE_ID);
- sURIMatcher.addURI("contacts", "/people/#/phones", PEOPLE_PHONES);
- sURIMatcher.addURI("contacts", "/people/#/phones/#", PEOPLE_PHONES_ID);
- sURIMatcher.addURI("contacts", "/people/#/contact_methods", PEOPLE_CONTACTMETHODS);
- sURIMatcher.addURI("contacts", "/people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID);
- sURIMatcher.addURI("contacts", "/deleted_people", DELETED_PEOPLE);
- sURIMatcher.addURI("contacts", "/phones", PHONES);
- sURIMatcher.addURI("contacts", "/phones/filter/*", PHONES_FILTER);
- sURIMatcher.addURI("contacts", "/phones/#", PHONES_ID);
- sURIMatcher.addURI("contacts", "/contact_methods", CONTACTMETHODS);
- sURIMatcher.addURI("contacts", "/contact_methods/#", CONTACTMETHODS_ID);
- sURIMatcher.addURI("call_log", "/calls", CALLS);
- sURIMatcher.addURI("call_log", "/calls/filter/*", CALLS_FILTER);
- sURIMatcher.addURI("call_log", "/calls/#", CALLS_ID);
+ sURIMatcher.addURI("contacts", "people", PEOPLE);
+ sURIMatcher.addURI("contacts", "people/#", PEOPLE_ID);
+ sURIMatcher.addURI("contacts", "people/#/phones", PEOPLE_PHONES);
+ sURIMatcher.addURI("contacts", "people/#/phones/#", PEOPLE_PHONES_ID);
+ sURIMatcher.addURI("contacts", "people/#/contact_methods", PEOPLE_CONTACTMETHODS);
+ sURIMatcher.addURI("contacts", "people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID);
+ sURIMatcher.addURI("contacts", "deleted_people", DELETED_PEOPLE);
+ sURIMatcher.addURI("contacts", "phones", PHONES);
+ sURIMatcher.addURI("contacts", "phones/filter/*", PHONES_FILTER);
+ sURIMatcher.addURI("contacts", "phones/#", PHONES_ID);
+ sURIMatcher.addURI("contacts", "contact_methods", CONTACTMETHODS);
+ sURIMatcher.addURI("contacts", "contact_methods/#", CONTACTMETHODS_ID);
+ sURIMatcher.addURI("call_log", "calls", CALLS);
+ sURIMatcher.addURI("call_log", "calls/filter/*", CALLS_FILTER);
+ sURIMatcher.addURI("call_log", "calls/#", CALLS_ID);
}
</pre>
-<p>Then when you need to match match against a URI, call {@link #match}, providing
-the tokenized url you've been given, and the value you want if there isn't
-a match. You can use the result to build a query, return a type, insert or
-delete a row, or whatever you need, without duplicating all of the if-else
-logic you'd otherwise need. Like this:
+<p>Then when you need to match against a URI, call {@link #match}, providing
+the URL that you have been given. You can use the result to build a query,
+return a type, insert or delete a row, or whatever you need, without duplicating
+all of the if-else logic that you would otherwise need. For example:
<pre>
- public String getType(String[] url)
+ public String getType(Uri url)
{
- int match = sURIMatcher.match(url, NO_MATCH);
+ int match = sURIMatcher.match(url);
switch (match)
{
case PEOPLE:
@@ -93,19 +92,20 @@
}
}
</pre>
-instead of
+instead of:
<pre>
- public String getType(String[] url)
+ public String getType(Uri url)
{
- if (url.length >= 2) {
- if (url[1].equals("people")) {
- if (url.length == 2) {
+ List<String> pathSegments = url.getPathSegments();
+ if (pathSegments.size() >= 2) {
+ if ("people".equals(pathSegments.get(1))) {
+ if (pathSegments.size() == 2) {
return "vnd.android.cursor.dir/person";
- } else if (url.length == 3) {
+ } else if (pathSegments.size() == 3) {
return "vnd.android.cursor.item/person";
... snip ...
return "vnd.android.cursor.dir/snail-mail";
- } else if (url.length == 3) {
+ } else if (pathSegments.size() == 3) {
return "vnd.android.cursor.item/snail-mail";
}
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
old mode 100644
new mode 100755
index 9bb3b75..e9a2929
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -25,6 +25,7 @@
import android.graphics.Movie;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable.ConstantState;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemProperties;
@@ -66,6 +67,8 @@
= new LongSparseArray<Drawable.ConstantState>();
private static final SparseArray<ColorStateList> mPreloadedColorStateLists
= new SparseArray<ColorStateList>();
+ private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables
+ = new LongSparseArray<Drawable.ConstantState>();
private static boolean mPreloaded;
/*package*/ final TypedValue mTmpValue = new TypedValue();
@@ -75,6 +78,8 @@
= new LongSparseArray<WeakReference<Drawable.ConstantState> >();
private final SparseArray<WeakReference<ColorStateList> > mColorStateListCache
= new SparseArray<WeakReference<ColorStateList> >();
+ private final LongSparseArray<WeakReference<Drawable.ConstantState> > mColorDrawableCache
+ = new LongSparseArray<WeakReference<Drawable.ConstantState> >();
private boolean mPreloading;
/*package*/ TypedArray mCachedStyledAttributes = null;
@@ -1299,37 +1304,13 @@
(int)(mMetrics.density*160), mConfiguration.keyboard,
keyboardHidden, mConfiguration.navigation, width, height,
mConfiguration.screenLayout, mConfiguration.uiMode, sSdkVersion);
- int N = mDrawableCache.size();
- if (DEBUG_CONFIG) {
- Log.d(TAG, "Cleaning up drawables config changes: 0x"
- + Integer.toHexString(configChanges));
- }
- for (int i=0; i<N; i++) {
- WeakReference<Drawable.ConstantState> ref = mDrawableCache.valueAt(i);
- if (ref != null) {
- Drawable.ConstantState cs = ref.get();
- if (cs != null) {
- if (Configuration.needNewResources(
- configChanges, cs.getChangingConfigurations())) {
- if (DEBUG_CONFIG) {
- Log.d(TAG, "FLUSHING #0x"
- + Long.toHexString(mDrawableCache.keyAt(i))
- + " / " + cs + " with changes: 0x"
- + Integer.toHexString(cs.getChangingConfigurations()));
- }
- mDrawableCache.setValueAt(i, null);
- } else if (DEBUG_CONFIG) {
- Log.d(TAG, "(Keeping #0x"
- + Long.toHexString(mDrawableCache.keyAt(i))
- + " / " + cs + " with changes: 0x"
- + Integer.toHexString(cs.getChangingConfigurations())
- + ")");
- }
- }
- }
- }
- mDrawableCache.clear();
+
+ clearDrawableCache(mDrawableCache, configChanges);
+ clearDrawableCache(mColorDrawableCache, configChanges);
+
mColorStateListCache.clear();
+
+
flushLayoutCache();
}
synchronized (mSync) {
@@ -1339,6 +1320,40 @@
}
}
+ private void clearDrawableCache(
+ LongSparseArray<WeakReference<ConstantState>> cache,
+ int configChanges) {
+ int N = cache.size();
+ if (DEBUG_CONFIG) {
+ Log.d(TAG, "Cleaning up drawables config changes: 0x"
+ + Integer.toHexString(configChanges));
+ }
+ for (int i=0; i<N; i++) {
+ WeakReference<Drawable.ConstantState> ref = cache.valueAt(i);
+ if (ref != null) {
+ Drawable.ConstantState cs = ref.get();
+ if (cs != null) {
+ if (Configuration.needNewResources(
+ configChanges, cs.getChangingConfigurations())) {
+ if (DEBUG_CONFIG) {
+ Log.d(TAG, "FLUSHING #0x"
+ + Long.toHexString(mDrawableCache.keyAt(i))
+ + " / " + cs + " with changes: 0x"
+ + Integer.toHexString(cs.getChangingConfigurations()));
+ }
+ cache.setValueAt(i, null);
+ } else if (DEBUG_CONFIG) {
+ Log.d(TAG, "(Keeping #0x"
+ + Long.toHexString(cache.keyAt(i))
+ + " / " + cs + " with changes: 0x"
+ + Integer.toHexString(cs.getChangingConfigurations())
+ + ")");
+ }
+ }
+ }
+ }
+ }
+
/**
* Update the system resources configuration if they have previously
* been initialized.
@@ -1661,13 +1676,18 @@
}
final long key = (((long) value.assetCookie) << 32) | value.data;
- Drawable dr = getCachedDrawable(key);
+ boolean isColorDrawable = false;
+ if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
+ value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+ isColorDrawable = true;
+ }
+ Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);
if (dr != null) {
return dr;
}
- Drawable.ConstantState cs = sPreloadedDrawables.get(key);
+ Drawable.ConstantState cs = isColorDrawable ? sPreloadedColorDrawables.get(key) : sPreloadedDrawables.get(key);
if (cs != null) {
dr = cs.newDrawable(this);
} else {
@@ -1726,13 +1746,21 @@
cs = dr.getConstantState();
if (cs != null) {
if (mPreloading) {
- sPreloadedDrawables.put(key, cs);
+ if (isColorDrawable) {
+ sPreloadedColorDrawables.put(key, cs);
+ } else {
+ sPreloadedDrawables.put(key, cs);
+ }
} else {
synchronized (mTmpValue) {
//Log.i(TAG, "Saving cached drawable @ #" +
// Integer.toHexString(key.intValue())
// + " in " + this + ": " + cs);
- mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
+ if (isColorDrawable) {
+ mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
+ } else {
+ mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
+ }
}
}
}
@@ -1741,9 +1769,11 @@
return dr;
}
- private Drawable getCachedDrawable(long key) {
+ private Drawable getCachedDrawable(
+ LongSparseArray<WeakReference<ConstantState>> drawableCache,
+ long key) {
synchronized (mTmpValue) {
- WeakReference<Drawable.ConstantState> wr = mDrawableCache.get(key);
+ WeakReference<Drawable.ConstantState> wr = drawableCache.get(key);
if (wr != null) { // we have the key
Drawable.ConstantState entry = wr.get();
if (entry != null) {
@@ -1753,7 +1783,7 @@
return entry.newDrawable(this);
}
else { // our entry has been purged
- mDrawableCache.delete(key);
+ drawableCache.delete(key);
}
}
}
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
index 5e90b91..23a6f97 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -87,21 +87,48 @@
if (style != null) {
if (mStyleIDs == null) {
mStyleIDs = new StyleIDs();
- mStyleIDs.boldId = nativeIndexOfString(mNative, "b");
- mStyleIDs.italicId = nativeIndexOfString(mNative, "i");
- mStyleIDs.underlineId = nativeIndexOfString(mNative, "u");
- mStyleIDs.ttId = nativeIndexOfString(mNative, "tt");
- mStyleIDs.bigId = nativeIndexOfString(mNative, "big");
- mStyleIDs.smallId = nativeIndexOfString(mNative, "small");
- mStyleIDs.supId = nativeIndexOfString(mNative, "sup");
- mStyleIDs.subId = nativeIndexOfString(mNative, "sub");
- mStyleIDs.strikeId = nativeIndexOfString(mNative, "strike");
- mStyleIDs.listItemId = nativeIndexOfString(mNative, "li");
- mStyleIDs.marqueeId = nativeIndexOfString(mNative, "marquee");
+ }
- if (localLOGV) Log.v(TAG, "BoldId=" + mStyleIDs.boldId
- + ", ItalicId=" + mStyleIDs.italicId
- + ", UnderlineId=" + mStyleIDs.underlineId);
+ // the style array is a flat array of <type, start, end> hence
+ // the magic constant 3.
+ for (int styleIndex = 0; styleIndex < style.length; styleIndex += 3) {
+ int styleId = style[styleIndex];
+
+ if (styleId == mStyleIDs.boldId || styleId == mStyleIDs.italicId
+ || styleId == mStyleIDs.underlineId || styleId == mStyleIDs.ttId
+ || styleId == mStyleIDs.bigId || styleId == mStyleIDs.smallId
+ || styleId == mStyleIDs.subId || styleId == mStyleIDs.supId
+ || styleId == mStyleIDs.strikeId || styleId == mStyleIDs.listItemId
+ || styleId == mStyleIDs.marqueeId) {
+ // id already found skip to next style
+ continue;
+ }
+
+ String styleTag = nativeGetString(mNative, styleId);
+
+ if (styleTag.equals("b")) {
+ mStyleIDs.boldId = styleId;
+ } else if (styleTag.equals("i")) {
+ mStyleIDs.italicId = styleId;
+ } else if (styleTag.equals("u")) {
+ mStyleIDs.underlineId = styleId;
+ } else if (styleTag.equals("tt")) {
+ mStyleIDs.ttId = styleId;
+ } else if (styleTag.equals("big")) {
+ mStyleIDs.bigId = styleId;
+ } else if (styleTag.equals("small")) {
+ mStyleIDs.smallId = styleId;
+ } else if (styleTag.equals("sup")) {
+ mStyleIDs.supId = styleId;
+ } else if (styleTag.equals("sub")) {
+ mStyleIDs.subId = styleId;
+ } else if (styleTag.equals("strike")) {
+ mStyleIDs.strikeId = styleId;
+ } else if (styleTag.equals("li")) {
+ mStyleIDs.listItemId = styleId;
+ } else if (styleTag.equals("marquee")) {
+ mStyleIDs.marqueeId = styleId;
+ }
}
res = applyStyles(str, style, mStyleIDs);
@@ -119,17 +146,17 @@
}
static final class StyleIDs {
- private int boldId;
- private int italicId;
- private int underlineId;
- private int ttId;
- private int bigId;
- private int smallId;
- private int subId;
- private int supId;
- private int strikeId;
- private int listItemId;
- private int marqueeId;
+ private int boldId = -1;
+ private int italicId = -1;
+ private int underlineId = -1;
+ private int ttId = -1;
+ private int bigId = -1;
+ private int smallId = -1;
+ private int subId = -1;
+ private int supId = -1;
+ private int strikeId = -1;
+ private int listItemId = -1;
+ private int marqueeId = -1;
}
private CharSequence applyStyles(String str, int[] style, StyleIDs ids) {
@@ -403,6 +430,5 @@
private static final native int nativeGetSize(int obj);
private static final native String nativeGetString(int obj, int idx);
private static final native int[] nativeGetStyle(int obj, int idx);
- private static final native int nativeIndexOfString(int obj, String str);
private static final native void nativeDestroy(int obj);
}
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index 038eedf..a5e5e46 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -204,7 +204,7 @@
* @param window
*/
public void fillWindow(int position, CursorWindow window) {
- if (position < 0 || position > getCount()) {
+ if (position < 0 || position >= getCount()) {
return;
}
window.acquireReference();
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index 6539156..e339a63 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -211,7 +211,9 @@
/**
* Returns the value of the requested column as a byte array.
*
- * <p>If the native content of that column is not blob exception may throw
+ * <p>The result and whether this method throws an exception when the
+ * column value is null or the column type is not a blob type is
+ * implementation-defined.
*
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a byte array.
@@ -221,8 +223,9 @@
/**
* Returns the value of the requested column as a String.
*
- * <p>If the native content of that column is not text the result will be
- * the result of passing the column value to String.valueOf(x).
+ * <p>The result and whether this method throws an exception when the
+ * column value is null or the column type is not a string type is
+ * implementation-defined.
*
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a String.
@@ -242,8 +245,10 @@
/**
* Returns the value of the requested column as a short.
*
- * <p>If the native content of that column is not numeric the result will be
- * the result of passing the column value to Short.valueOf(x).
+ * <p>The result and whether this method throws an exception when the
+ * column value is null, the column type is not an integral type, or the
+ * integer value is outside the range [<code>Short.MIN_VALUE</code>,
+ * <code>Short.MAX_VALUE</code>] is implementation-defined.
*
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a short.
@@ -253,8 +258,10 @@
/**
* Returns the value of the requested column as an int.
*
- * <p>If the native content of that column is not numeric the result will be
- * the result of passing the column value to Integer.valueOf(x).
+ * <p>The result and whether this method throws an exception when the
+ * column value is null, the column type is not an integral type, or the
+ * integer value is outside the range [<code>Integer.MIN_VALUE</code>,
+ * <code>Integer.MAX_VALUE</code>] is implementation-defined.
*
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as an int.
@@ -264,8 +271,10 @@
/**
* Returns the value of the requested column as a long.
*
- * <p>If the native content of that column is not numeric the result will be
- * the result of passing the column value to Long.valueOf(x).
+ * <p>The result and whether this method throws an exception when the
+ * column value is null, the column type is not an integral type, or the
+ * integer value is outside the range [<code>Long.MIN_VALUE</code>,
+ * <code>Long.MAX_VALUE</code>] is implementation-defined.
*
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a long.
@@ -275,8 +284,10 @@
/**
* Returns the value of the requested column as a float.
*
- * <p>If the native content of that column is not numeric the result will be
- * the result of passing the column value to Float.valueOf(x).
+ * <p>The result and whether this method throws an exception when the
+ * column value is null, the column type is not a floating-point type, or the
+ * floating-point value is not representable as a <code>float</code> value is
+ * implementation-defined.
*
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a float.
@@ -286,8 +297,10 @@
/**
* Returns the value of the requested column as a double.
*
- * <p>If the native content of that column is not numeric the result will be
- * the result of passing the column value to Double.valueOf(x).
+ * <p>The result and whether this method throws an exception when the
+ * column value is null, the column type is not a floating-point type, or the
+ * floating-point value is not representable as a <code>double</code> value is
+ * implementation-defined.
*
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a double.
@@ -573,7 +586,8 @@
* that are required to fetch data for the cursor.
*
* <p>These values may only change when requery is called.
- * @return cursor-defined values, or Bundle.EMTPY if there are no values. Never null.
+ * @return cursor-defined values, or {@link android.os.Bundle#EMPTY Bundle.EMPTY} if there
+ * are no values. Never <code>null</code>.
*/
Bundle getExtras();
@@ -583,8 +597,10 @@
*
* <p>One use of this is to tell a cursor that it should retry its network request after it
* reported an error.
- * @param extras extra values, or Bundle.EMTPY. Never null.
- * @return extra values, or Bundle.EMTPY. Never null.
+ * @param extras extra values, or {@link android.os.Bundle#EMPTY Bundle.EMPTY}.
+ * Never <code>null</code>.
+ * @return extra values, or {@link android.os.Bundle#EMPTY Bundle.EMPTY}.
+ * Never <code>null</code>.
*/
Bundle respond(Bundle extras);
}
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index c756825..76b91f5 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -245,6 +245,15 @@
}
}
+ /**
+ * Returns the value at (<code>row</code>, <code>col</code>) as a <code>byte</code> array.
+ *
+ * <p>If the value is null, then <code>null</code> is returned. If the
+ * type of column <code>col</code> is a string type, then the result
+ * is the array of bytes that make up the internal representation of the
+ * string value. If the type of column <code>col</code> is integral or floating-point,
+ * then an {@link SQLiteException} is thrown.
+ */
private native byte[] getBlob_native(int row, int col);
/**
@@ -332,6 +341,19 @@
}
}
+ /**
+ * Returns the value at (<code>row</code>, <code>col</code>) as a <code>String</code>.
+ *
+ * <p>If the value is null, then <code>null</code> is returned. If the
+ * type of column <code>col</code> is integral, then the result is the string
+ * that is obtained by formatting the integer value with the <code>printf</code>
+ * family of functions using format specifier <code>%lld</code>. If the
+ * type of column <code>col</code> is floating-point, then the result is the string
+ * that is obtained by formatting the floating-point value with the
+ * <code>printf</code> family of functions using format specifier <code>%g</code>.
+ * If the type of column <code>col</code> is a blob type, then an
+ * {@link SQLiteException} is thrown.
+ */
private native String getString_native(int row, int col);
/**
@@ -383,6 +405,17 @@
}
}
+ /**
+ * Returns the value at (<code>row</code>, <code>col</code>) as a <code>long</code>.
+ *
+ * <p>If the value is null, then <code>0L</code> is returned. If the
+ * type of column <code>col</code> is a string type, then the result
+ * is the <code>long</code> that is obtained by parsing the string value with
+ * <code>strtoll</code>. If the type of column <code>col</code> is
+ * floating-point, then the result is the floating-point value casted to a <code>long</code>.
+ * If the type of column <code>col</code> is a blob type, then an
+ * {@link SQLiteException} is thrown.
+ */
private native long getLong_native(int row, int col);
/**
@@ -402,6 +435,17 @@
}
}
+ /**
+ * Returns the value at (<code>row</code>, <code>col</code>) as a <code>double</code>.
+ *
+ * <p>If the value is null, then <code>0.0</code> is returned. If the
+ * type of column <code>col</code> is a string type, then the result
+ * is the <code>double</code> that is obtained by parsing the string value with
+ * <code>strtod</code>. If the type of column <code>col</code> is
+ * integral, then the result is the integer value casted to a <code>double</code>.
+ * If the type of column <code>col</code> is a blob type, then an
+ * {@link SQLiteException} is thrown.
+ */
private native double getDouble_native(int row, int col);
/**
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 66406ca..95fc1fd 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -50,8 +50,6 @@
private static final boolean DEBUG = false;
private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
- private static final String[] countProjection = new String[]{"count(*)"};
-
/**
* Special function for writing an exception result at the header of
* a parcel, to be used when returning an exception from a transaction.
@@ -604,14 +602,40 @@
* @return the number of rows in the table
*/
public static long queryNumEntries(SQLiteDatabase db, String table) {
- Cursor cursor = db.query(table, countProjection,
- null, null, null, null, null);
- try {
- cursor.moveToFirst();
- return cursor.getLong(0);
- } finally {
- cursor.close();
- }
+ return queryNumEntries(db, table, null, null);
+ }
+
+ /**
+ * Query the table for the number of rows in the table.
+ * @param db the database the table is in
+ * @param table the name of the table to query
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE itself).
+ * Passing null will count all rows for the given table
+ * @return the number of rows in the table filtered by the selection
+ */
+ public static long queryNumEntries(SQLiteDatabase db, String table, String selection) {
+ return queryNumEntries(db, table, selection, null);
+ }
+
+ /**
+ * Query the table for the number of rows in the table.
+ * @param db the database the table is in
+ * @param table the name of the table to query
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE itself).
+ * Passing null will count all rows for the given table
+ * @param selectionArgs You may include ?s in selection,
+ * which will be replaced by the values from selectionArgs,
+ * in order that they appear in the selection.
+ * The values will be bound as Strings.
+ * @return the number of rows in the table filtered by the selection
+ */
+ public static long queryNumEntries(SQLiteDatabase db, String table, String selection,
+ String[] selectionArgs) {
+ String s = (!TextUtils.isEmpty(selection)) ? " where " + selection : "";
+ return longForQuery(db, "select count(*) from " + table + s,
+ selectionArgs);
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index d8dcaf7..c592134 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -132,11 +132,11 @@
// the cursor's state doesn't change
while (true) {
mLock.lock();
- if (mCursorState != mThreadState) {
- mLock.unlock();
- break;
- }
try {
+ if (mCursorState != mThreadState) {
+ break;
+ }
+
int count = mQuery.fillWindow(cw, mMaxRead, mCount);
// return -1 means not finished
if (count != 0) {
@@ -218,9 +218,8 @@
mColumnNameMap = null;
mQuery = query;
+ db.lock();
try {
- db.lock();
-
// Setup the list of columns
int columnCount = mQuery.columnCountLocked();
mColumns = new String[columnCount];
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 610bf70..b6aca2b 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -321,7 +321,7 @@
}
String sql = buildQuery(
- projectionIn, selection, selectionArgs, groupBy, having,
+ projectionIn, selection, groupBy, having,
sortOrder, limit);
if (Log.isLoggable(TAG, Log.DEBUG)) {
@@ -345,10 +345,6 @@
* formatted as an SQL WHERE clause (excluding the WHERE
* itself). Passing null will return all rows for the given
* URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
* @param groupBy A filter declaring how to group rows, formatted
* as an SQL GROUP BY clause (excluding the GROUP BY itself).
* Passing null will cause the rows to not be grouped.
@@ -365,8 +361,8 @@
* @return the resulting SQL SELECT statement
*/
public String buildQuery(
- String[] projectionIn, String selection, String[] selectionArgs,
- String groupBy, String having, String sortOrder, String limit) {
+ String[] projectionIn, String selection, String groupBy,
+ String having, String sortOrder, String limit) {
String[] projection = computeProjection(projectionIn);
StringBuilder where = new StringBuilder();
@@ -394,6 +390,19 @@
}
/**
+ * @deprecated This method's signature is misleading since no SQL parameter
+ * substitution is carried out. The selection arguments parameter does not get
+ * used at all. To avoid confusion, call
+ * {@link #buildQuery(String[], String, String, String, String, String)} instead.
+ */
+ @Deprecated
+ public String buildQuery(
+ String[] projectionIn, String selection, String[] selectionArgs,
+ String groupBy, String having, String sortOrder, String limit) {
+ return buildQuery(projectionIn, selection, groupBy, having, sortOrder, limit);
+ }
+
+ /**
* Construct a SELECT statement suitable for use in a group of
* SELECT statements that will be joined through UNION operators
* in buildUnionQuery.
@@ -422,10 +431,6 @@
* formatted as an SQL WHERE clause (excluding the WHERE
* itself). Passing null will return all rows for the given
* URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
* @param groupBy A filter declaring how to group rows, formatted
* as an SQL GROUP BY clause (excluding the GROUP BY itself).
* Passing null will cause the rows to not be grouped.
@@ -443,7 +448,6 @@
int computedColumnsOffset,
String typeDiscriminatorValue,
String selection,
- String[] selectionArgs,
String groupBy,
String having) {
int unionColumnsCount = unionColumns.length;
@@ -463,12 +467,36 @@
}
}
return buildQuery(
- projectionIn, selection, selectionArgs, groupBy, having,
+ projectionIn, selection, groupBy, having,
null /* sortOrder */,
null /* limit */);
}
/**
+ * @deprecated This method's signature is misleading since no SQL parameter
+ * substitution is carried out. The selection arguments parameter does not get
+ * used at all. To avoid confusion, call
+ * {@link #buildUnionSubQuery}
+ * instead.
+ */
+ @Deprecated
+ public String buildUnionSubQuery(
+ String typeDiscriminatorColumn,
+ String[] unionColumns,
+ Set<String> columnsPresentInTable,
+ int computedColumnsOffset,
+ String typeDiscriminatorValue,
+ String selection,
+ String[] selectionArgs,
+ String groupBy,
+ String having) {
+ return buildUnionSubQuery(
+ typeDiscriminatorColumn, unionColumns, columnsPresentInTable,
+ computedColumnsOffset, typeDiscriminatorValue, selection,
+ groupBy, having);
+ }
+
+ /**
* Given a set of subqueries, all of which are SELECT statements,
* construct a query that returns the union of what those
* subqueries return.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index cd5ceeb..494e922 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -21,6 +21,9 @@
import android.os.Binder;
import android.os.RemoteException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
/**
* Class that answers queries about the state of network connectivity. It also
* notifies applications when network connectivity changes. Get an instance
@@ -337,8 +340,29 @@
* @return {@code true} on success, {@code false} on failure
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
+ InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress);
+
+ if (inetAddress == null) {
+ return false;
+ }
+
+ return requestRouteToHostAddress(networkType, inetAddress);
+ }
+
+ /**
+ * Ensure that a network route exists to deliver traffic to the specified
+ * host via the specified network interface. An attempt to add a route that
+ * already exists is ignored, but treated as successful.
+ * @param networkType the type of the network over which traffic to the specified
+ * host is to be routed
+ * @param hostAddress the IP address of the host to which the route is desired
+ * @return {@code true} on success, {@code false} on failure
+ * @hide
+ */
+ public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
+ byte[] address = hostAddress.getAddress();
try {
- return mService.requestRouteToHost(networkType, hostAddress);
+ return mService.requestRouteToHostAddress(networkType, address);
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index b734ac7..afccdd9 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -47,6 +47,8 @@
boolean requestRouteToHost(int networkType, int hostAddress);
+ boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
+
boolean getBackgroundDataSetting();
void setBackgroundDataSetting(boolean allowBackgroundData);
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index b5596013..32c2d64 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -16,6 +16,8 @@
package android.net;
+import java.net.InetAddress;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -23,7 +25,6 @@
import android.os.RemoteException;
import android.os.Handler;
import android.os.ServiceManager;
-import android.os.SystemProperties;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.TelephonyIntents;
@@ -491,17 +492,16 @@
* Ensure that a network route exists to deliver traffic to the specified
* host via the mobile data network.
* @param hostAddress the IP address of the host to which the route is desired,
- * in network byte order.
* @return {@code true} on success, {@code false} on failure
*/
@Override
- public boolean requestRouteToHost(int hostAddress) {
+ public boolean requestRouteToHost(InetAddress hostAddress) {
if (DBG) {
- Log.d(TAG, "Requested host route to " + Integer.toHexString(hostAddress) +
+ Log.d(TAG, "Requested host route to " + hostAddress.getHostAddress() +
" for " + mApnType + "(" + mInterfaceName + ")");
}
- if (mInterfaceName != null && hostAddress != -1) {
- return NetworkUtils.addHostRoute(mInterfaceName, hostAddress) == 0;
+ if (mInterfaceName != null) {
+ return NetworkUtils.addHostRoute(mInterfaceName, hostAddress, null);
} else {
return false;
}
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 039dfff..9140dc9 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -18,13 +18,14 @@
import java.io.FileWriter;
import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
import android.content.Context;
import android.text.TextUtils;
-import android.util.Config;
import android.util.Log;
@@ -133,13 +134,18 @@
}
if (mInterfaceName != null && !mPrivateDnsRouteSet) {
for (String addrString : getNameServers()) {
- int addr = NetworkUtils.lookupHost(addrString);
- if (addr != -1 && addr != 0) {
- if (DBG) Log.d(TAG, " adding "+addrString+" ("+addr+")");
- NetworkUtils.addHostRoute(mInterfaceName, addr);
+ if (addrString != null) {
+ try {
+ InetAddress inetAddress = InetAddress.getByName(addrString);
+ if (DBG) Log.d(TAG, " adding " + addrString);
+ if (NetworkUtils.addHostRoute(mInterfaceName, inetAddress, null)) {
+ mPrivateDnsRouteSet = true;
+ }
+ } catch (UnknownHostException e) {
+ if (DBG) Log.d(TAG, " DNS address " + addrString + " : Exception " + e);
+ }
}
}
- mPrivateDnsRouteSet = true;
}
}
@@ -162,7 +168,14 @@
Log.d(TAG, "addDefaultRoute for " + mNetworkInfo.getTypeName() +
" (" + mInterfaceName + "), GatewayAddr=" + mDefaultGatewayAddr);
}
- NetworkUtils.setDefaultRoute(mInterfaceName, mDefaultGatewayAddr);
+ InetAddress inetAddress = NetworkUtils.intToInetAddress(mDefaultGatewayAddr);
+ if (inetAddress == null) {
+ if (DBG) Log.d(TAG, " Unable to add default route. mDefaultGatewayAddr Error");
+ } else {
+ if (!NetworkUtils.addDefaultRoute(mInterfaceName, inetAddress) && DBG) {
+ Log.d(TAG, " Unable to add default route.");
+ }
+ }
}
}
@@ -399,7 +412,7 @@
* @param hostAddress the IP address of the host to which the route is desired
* @return {@code true} on success, {@code false} on failure
*/
- public boolean requestRouteToHost(int hostAddress) {
+ public boolean requestRouteToHost(InetAddress hostAddress) {
return false;
}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 81362c9..8bdfdf9 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -17,25 +17,39 @@
package android.net;
import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.UnknownHostException;
+import android.util.Log;
+
/**
* Native methods for managing network interfaces.
*
* {@hide}
*/
public class NetworkUtils {
+
+ private static final String TAG = "NetworkUtils";
+
/** Bring the named network interface up. */
public native static int enableInterface(String interfaceName);
/** Bring the named network interface down. */
public native static int disableInterface(String interfaceName);
- /** Add a route to the specified host via the named interface. */
- public native static int addHostRoute(String interfaceName, int hostaddr);
-
- /** Add a default route for the named interface. */
- public native static int setDefaultRoute(String interfaceName, int gwayAddr);
+ /**
+ * Add a route to the routing table.
+ *
+ * @param interfaceName the interface to route through.
+ * @param dst the network or host to route to. May be IPv4 or IPv6, e.g.
+ * "0.0.0.0" or "2001:4860::".
+ * @param prefixLength the prefix length of the route.
+ * @param gw the gateway to use, e.g., "192.168.251.1". If null,
+ * indicates a directly-connected route.
+ */
+ public native static int addRoute(String interfaceName, String dst,
+ int prefixLength, String gw);
/** Return the gateway address for the default route for the named interface. */
public native static int getDefaultRoute(String interfaceName);
@@ -106,27 +120,79 @@
String interfaceName, int ipAddress, int netmask, int gateway, int dns1, int dns2);
/**
- * Look up a host name and return the result as an int. Works if the argument
- * is an IP address in dot notation. Obviously, this can only be used for IPv4
- * addresses.
- * @param hostname the name of the host (or the IP address)
- * @return the IP address as an {@code int} in network byte order
+ * Convert a IPv4 address from an integer to an InetAddress.
+ * @param hostAddr is an Int corresponding to the IPv4 address in network byte order
+ * @return the IP address as an {@code InetAddress}, returns null if
+ * unable to convert or if the int is an invalid address.
*/
- public static int lookupHost(String hostname) {
+ public static InetAddress intToInetAddress(int hostAddress) {
InetAddress inetAddress;
+ byte[] addressBytes = { (byte)(0xff & hostAddress),
+ (byte)(0xff & (hostAddress >> 8)),
+ (byte)(0xff & (hostAddress >> 16)),
+ (byte)(0xff & (hostAddress >> 24)) };
+
try {
- inetAddress = InetAddress.getByName(hostname);
- } catch (UnknownHostException e) {
- return -1;
+ inetAddress = InetAddress.getByAddress(addressBytes);
+ } catch(UnknownHostException e) {
+ return null;
}
- byte[] addrBytes;
- int addr;
- addrBytes = inetAddress.getAddress();
- addr = ((addrBytes[3] & 0xff) << 24)
- | ((addrBytes[2] & 0xff) << 16)
- | ((addrBytes[1] & 0xff) << 8)
- | (addrBytes[0] & 0xff);
- return addr;
+
+ return inetAddress;
+ }
+
+ /**
+ * Add a default route through the specified gateway.
+ * @param interfaceName interface on which the route should be added
+ * @param gw the IP address of the gateway to which the route is desired,
+ * @return {@code true} on success, {@code false} on failure
+ */
+ public static boolean addDefaultRoute(String interfaceName, InetAddress gw) {
+ String dstStr;
+ String gwStr = gw.getHostAddress();
+
+ if (gw instanceof Inet4Address) {
+ dstStr = "0.0.0.0";
+ } else if (gw instanceof Inet6Address) {
+ dstStr = "::";
+ } else {
+ Log.w(TAG, "addDefaultRoute failure: address is neither IPv4 nor IPv6" +
+ "(" + gwStr + ")");
+ return false;
+ }
+ return addRoute(interfaceName, dstStr, 0, gwStr) == 0;
+ }
+
+ /**
+ * Add a host route.
+ * @param interfaceName interface on which the route should be added
+ * @param dst the IP address of the host to which the route is desired,
+ * this should not be null.
+ * @param gw the IP address of the gateway to which the route is desired,
+ * if null, indicates a directly-connected route.
+ * @return {@code true} on success, {@code false} on failure
+ */
+ public static boolean addHostRoute(String interfaceName, InetAddress dst,
+ InetAddress gw) {
+ if (dst == null) {
+ Log.w(TAG, "addHostRoute: dst should not be null");
+ return false;
+ }
+
+ int prefixLength;
+ String dstStr = dst.getHostAddress();
+ String gwStr = (gw != null) ? gw.getHostAddress() : null;
+
+ if (dst instanceof Inet4Address) {
+ prefixLength = 32;
+ } else if (dst instanceof Inet6Address) {
+ prefixLength = 128;
+ } else {
+ Log.w(TAG, "addHostRoute failure: address is neither IPv4 nor IPv6" +
+ "(" + dst + ")");
+ return false;
+ }
+ return addRoute(interfaceName, dstStr, prefixLength, gwStr) == 0;
}
public static int v4StringToInt(String str) {
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 66eefb2..c1fa5b4 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -16,12 +16,16 @@
package android.net;
+import org.apache.http.HttpHost;
+
import android.content.ContentResolver;
import android.content.Context;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Log;
+import java.net.URI;
+
import junit.framework.Assert;
/**
@@ -120,4 +124,73 @@
}
}
+ /**
+ * Returns the preferred proxy to be used by clients. This is a wrapper
+ * around {@link android.net.Proxy#getHost()}. Currently no proxy will
+ * be returned for localhost or if the active network is Wi-Fi.
+ *
+ * @param context the context which will be passed to
+ * {@link android.net.Proxy#getHost()}
+ * @param url the target URL for the request
+ * @note Calling this method requires permission
+ * android.permission.ACCESS_NETWORK_STATE
+ * @return The preferred proxy to be used by clients, or null if there
+ * is no proxy.
+ *
+ * {@hide}
+ */
+ static final public HttpHost getPreferredHttpHost(Context context,
+ String url) {
+ if (!isLocalHost(url) && !isNetworkWifi(context)) {
+ final String proxyHost = Proxy.getHost(context);
+ if (proxyHost != null) {
+ return new HttpHost(proxyHost, Proxy.getPort(context), "http");
+ }
+ }
+
+ return null;
+ }
+
+ static final private boolean isLocalHost(String url) {
+ if (url == null) {
+ return false;
+ }
+
+ try {
+ final URI uri = URI.create(url);
+ final String host = uri.getHost();
+ if (host != null) {
+ // TODO: InetAddress.isLoopbackAddress should be used to check
+ // for localhost. However no public factory methods exist which
+ // can be used without triggering DNS lookup if host is not localhost.
+ if (host.equalsIgnoreCase("localhost") ||
+ host.equals("127.0.0.1") ||
+ host.equals("[::1]")) {
+ return true;
+ }
+ }
+ } catch (IllegalArgumentException iex) {
+ // Ignore (URI.create)
+ }
+
+ return false;
+ }
+
+ static final private boolean isNetworkWifi(Context context) {
+ if (context == null) {
+ return false;
+ }
+
+ final ConnectivityManager connectivity = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (connectivity != null) {
+ final NetworkInfo info = connectivity.getActiveNetworkInfo();
+ if (info != null &&
+ info.getType() == ConnectivityManager.TYPE_WIFI) {
+ return true;
+ }
+ }
+
+ return false;
+ }
};
diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java
index 0757293..8dc2a862 100644
--- a/core/java/android/net/http/AndroidHttpClient.java
+++ b/core/java/android/net/http/AndroidHttpClient.java
@@ -79,6 +79,9 @@
// Gzip of data shorter than this probably won't be worthwhile
public static long DEFAULT_SYNC_MIN_GZIP_BYTES = 256;
+ // Default connection and socket timeout of 60 seconds. Tweak to taste.
+ private static final int SOCKET_OPERATION_TIMEOUT = 60 * 1000;
+
private static final String TAG = "AndroidHttpClient";
@@ -107,9 +110,8 @@
// and it's not worth it to pay the penalty of checking every time.
HttpConnectionParams.setStaleCheckingEnabled(params, false);
- // Default connection and socket timeout of 20 seconds. Tweak to taste.
- HttpConnectionParams.setConnectionTimeout(params, 60 * 1000);
- HttpConnectionParams.setSoTimeout(params, 60 * 1000);
+ HttpConnectionParams.setConnectionTimeout(params, SOCKET_OPERATION_TIMEOUT);
+ HttpConnectionParams.setSoTimeout(params, SOCKET_OPERATION_TIMEOUT);
HttpConnectionParams.setSocketBufferSize(params, 8192);
// Don't handle redirects -- return them to the caller. Our code
@@ -125,7 +127,8 @@
schemeRegistry.register(new Scheme("http",
PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https",
- SSLCertificateSocketFactory.getHttpSocketFactory(30 * 1000, sessionCache), 443));
+ SSLCertificateSocketFactory.getHttpSocketFactory(
+ SOCKET_OPERATION_TIMEOUT, sessionCache), 443));
ClientConnectionManager manager =
new ThreadSafeClientConnManager(params, schemeRegistry);
diff --git a/core/java/android/net/http/CertificateValidatorCache.java b/core/java/android/net/http/CertificateValidatorCache.java
index 47661d5..a89f75c 100644
--- a/core/java/android/net/http/CertificateValidatorCache.java
+++ b/core/java/android/net/http/CertificateValidatorCache.java
@@ -137,8 +137,8 @@
if (domain != null && domain.length() != 0) {
if (secureHash != null && secureHash.length != 0) {
- CacheEntry cacheEntry = (CacheEntry)mCacheMap.get(
- new Integer(mBigScrew ^ domain.hashCode()));
+ final Integer key = new Integer(mBigScrew ^ domain.hashCode());
+ CacheEntry cacheEntry = mCacheMap.get(key);
if (cacheEntry != null) {
if (!cacheEntry.expired()) {
rval = cacheEntry.has(domain, secureHash);
@@ -148,7 +148,7 @@
}
// TODO: debug only!
} else {
- mCacheMap.remove(cacheEntry);
+ mCacheMap.remove(key);
}
}
}
diff --git a/core/java/android/net/http/Headers.java b/core/java/android/net/http/Headers.java
index 09f6f4f..74c0de8 100644
--- a/core/java/android/net/http/Headers.java
+++ b/core/java/android/net/http/Headers.java
@@ -262,7 +262,14 @@
break;
case HASH_CACHE_CONTROL:
if (name.equals(CACHE_CONTROL)) {
- mHeaders[IDX_CACHE_CONTROL] = val;
+ // In case where we receive more than one header, create a ',' separated list.
+ // This should be ok, according to RFC 2616 chapter 4.2
+ if (mHeaders[IDX_CACHE_CONTROL] != null &&
+ mHeaders[IDX_CACHE_CONTROL].length() > 0) {
+ mHeaders[IDX_CACHE_CONTROL] += (',' + val);
+ } else {
+ mHeaders[IDX_CACHE_CONTROL] = val;
+ }
}
break;
case HASH_LAST_MODIFIED:
diff --git a/core/java/android/net/http/HttpsConnection.java b/core/java/android/net/http/HttpsConnection.java
index b361dca..d77e9d9 100644
--- a/core/java/android/net/http/HttpsConnection.java
+++ b/core/java/android/net/http/HttpsConnection.java
@@ -205,10 +205,13 @@
BasicHttpRequest proxyReq = new BasicHttpRequest
("CONNECT", mHost.toHostString());
- // add all 'proxy' headers from the original request
+ // add all 'proxy' headers from the original request, we also need
+ // to add 'host' header unless we want proxy to answer us with a
+ // 400 Bad Request
for (Header h : req.mHttpRequest.getAllHeaders()) {
String headerName = h.getName().toLowerCase();
- if (headerName.startsWith("proxy") || headerName.equals("keep-alive")) {
+ if (headerName.startsWith("proxy") || headerName.equals("keep-alive")
+ || headerName.equals("host")) {
proxyReq.addHeader(h);
}
}
diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java
index 103fd94..2c48a04 100644
--- a/core/java/android/net/http/RequestHandle.java
+++ b/core/java/android/net/http/RequestHandle.java
@@ -308,7 +308,7 @@
String A2 = mMethod + ":" + mUrl;
// because we do not preemptively send authorization headers, nc is always 1
- String nc = "000001";
+ String nc = "00000001";
String cnonce = computeCnonce();
String digest = computeDigest(A1, A2, nonce, QOP, nc, cnonce);
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 44b73c5..bd57b33 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -99,6 +99,7 @@
public static final int BATTERY_HEALTH_DEAD = 4;
public static final int BATTERY_HEALTH_OVER_VOLTAGE = 5;
public static final int BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6;
+ public static final int BATTERY_HEALTH_COLD = 7;
// values of the "plugged" field in the ACTION_BATTERY_CHANGED intent.
// These must be powers of 2.
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index f8260ca..a402c91 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -324,6 +324,10 @@
} catch (RuntimeException e) {
reply.writeException(e);
res = true;
+ } catch (OutOfMemoryError e) {
+ RuntimeException re = new RuntimeException("Out of memory", e);
+ reply.writeException(re);
+ res = true;
}
reply.recycle();
data.recycle();
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 2e14667..86f9a6b 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -94,7 +94,8 @@
/**
* Default trace file path and file
*/
- private static final String DEFAULT_TRACE_PATH_PREFIX = "/sdcard/";
+ private static final String DEFAULT_TRACE_PATH_PREFIX =
+ Environment.getExternalStorageDirectory().getPath() + "/";
private static final String DEFAULT_TRACE_BODY = "dmtrace";
private static final String DEFAULT_TRACE_EXTENSION = ".trace";
private static final String DEFAULT_TRACE_FILE_PATH =
@@ -127,7 +128,7 @@
public int otherPrivateDirty;
/** The shared dirty pages used by everything else. */
public int otherSharedDirty;
-
+
public MemoryInfo() {
}
@@ -137,21 +138,21 @@
public int getTotalPss() {
return dalvikPss + nativePss + otherPss;
}
-
+
/**
* Return total private dirty memory usage in kB.
*/
public int getTotalPrivateDirty() {
return dalvikPrivateDirty + nativePrivateDirty + otherPrivateDirty;
}
-
+
/**
* Return total shared dirty memory usage in kB.
*/
public int getTotalSharedDirty() {
return dalvikSharedDirty + nativeSharedDirty + otherSharedDirty;
}
-
+
public int describeContents() {
return 0;
}
@@ -179,7 +180,7 @@
otherPrivateDirty = source.readInt();
otherSharedDirty = source.readInt();
}
-
+
public static final Creator<MemoryInfo> CREATOR = new Creator<MemoryInfo>() {
public MemoryInfo createFromParcel(Parcel source) {
return new MemoryInfo(source);
@@ -460,7 +461,7 @@
* Like startMethodTracing(String, int, int), but taking an already-opened
* FileDescriptor in which the trace is written. The file name is also
* supplied simply for logging. Makes a dup of the file descriptor.
- *
+ *
* Not exposed in the SDK unless we are really comfortable with supporting
* this and find it would be useful.
* @hide
@@ -1070,7 +1071,7 @@
* static {
* // Sets all the fields
* Debug.setFieldsOn(MyDebugVars.class);
- *
+ *
* // Sets only the fields annotated with @Debug.DebugProperty
* // Debug.setFieldsOn(MyDebugVars.class, true);
* }
diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java
index a47c66a..e1c1678 100644
--- a/core/java/android/os/DropBoxManager.java
+++ b/core/java/android/os/DropBoxManager.java
@@ -58,6 +58,30 @@
private static final int HAS_BYTE_ARRAY = 8;
/**
+ * Broadcast Action: This is broadcast when a new entry is added in the dropbox.
+ * You must hold the {@link android.Manifest.permission#READ_LOGS} permission
+ * in order to receive this broadcast.
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system.
+ */
+ public static final String ACTION_DROPBOX_ENTRY_ADDED =
+ "android.intent.action.DROPBOX_ENTRY_ADDED";
+
+ /**
+ * Extra for {@link android.os.DropBoxManager#ACTION_DROPBOX_ENTRY_ADDED}:
+ * string containing the dropbox tag.
+ */
+ public static final String EXTRA_TAG = "tag";
+
+ /**
+ * Extra for {@link android.os.DropBoxManager#ACTION_DROPBOX_ENTRY_ADDED}:
+ * long integer value containing time (in milliseconds since January 1, 1970 00:00:00 UTC)
+ * when the entry was created.
+ */
+ public static final String EXTRA_TIME = "time";
+
+ /**
* A single entry retrieved from the drop box.
* This may include a reference to a stream, so you must call
* {@link #close()} when you are done using it.
@@ -169,7 +193,12 @@
is = getInputStream();
if (is == null) return null;
byte[] buf = new byte[maxBytes];
- return new String(buf, 0, Math.max(0, is.read(buf)));
+ int readBytes = 0;
+ int n = 0;
+ while (n >= 0 && (readBytes += n) < maxBytes) {
+ n = is.read(buf, readBytes, maxBytes - readBytes);
+ }
+ return new String(buf, 0, readBytes);
} catch (IOException e) {
return null;
} finally {
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 49b72fe..7d128ec 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -85,9 +85,9 @@
// sometimes we store linked lists of these things
/*package*/ Message next;
- private static Object mPoolSync = new Object();
- private static Message mPool;
- private static int mPoolSize = 0;
+ private static final Object sPoolSync = new Object();
+ private static Message sPool;
+ private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 10;
@@ -96,11 +96,12 @@
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
- synchronized (mPoolSync) {
- if (mPool != null) {
- Message m = mPool;
- mPool = m.next;
+ synchronized (sPoolSync) {
+ if (sPool != null) {
+ Message m = sPool;
+ sPool = m.next;
m.next = null;
+ sPoolSize--;
return m;
}
}
@@ -237,12 +238,12 @@
* freed.
*/
public void recycle() {
- synchronized (mPoolSync) {
- if (mPoolSize < MAX_POOL_SIZE) {
+ synchronized (sPoolSync) {
+ if (sPoolSize < MAX_POOL_SIZE) {
clearForRecycle();
-
- next = mPool;
- mPool = this;
+ next = sPool;
+ sPool = this;
+ sPoolSize++;
}
}
}
@@ -453,4 +454,3 @@
replyTo = Messenger.readMessengerOrNullFromParcel(source);
}
}
-
diff --git a/core/java/android/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java
index bbad2b6..45c8174 100644
--- a/core/java/android/preference/DialogPreference.java
+++ b/core/java/android/preference/DialogPreference.java
@@ -322,7 +322,7 @@
private void requestInputMethod(Dialog dialog) {
Window window = dialog.getWindow();
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE |
- WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
+ WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
}
/**
diff --git a/core/java/android/preference/ListPreference.java b/core/java/android/preference/ListPreference.java
index f842d75..f44cbe4 100644
--- a/core/java/android/preference/ListPreference.java
+++ b/core/java/android/preference/ListPreference.java
@@ -39,6 +39,7 @@
private CharSequence[] mEntries;
private CharSequence[] mEntryValues;
private String mValue;
+ private String mSummary;
private int mClickedDialogEntryIndex;
public ListPreference(Context context, AttributeSet attrs) {
@@ -49,8 +50,16 @@
mEntries = a.getTextArray(com.android.internal.R.styleable.ListPreference_entries);
mEntryValues = a.getTextArray(com.android.internal.R.styleable.ListPreference_entryValues);
a.recycle();
+
+ /* Retrieve the Preference summary attribute since it's private
+ * in the Preference class.
+ */
+ a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.Preference, 0, 0);
+ mSummary = a.getString(com.android.internal.R.styleable.Preference_summary);
+ a.recycle();
}
-
+
public ListPreference(Context context) {
this(context, null);
}
@@ -127,6 +136,43 @@
}
/**
+ * Returns the summary of this ListPreference. If the summary
+ * has a {@linkplain java.lang.String#format String formatting}
+ * marker in it (i.e. "%s" or "%1$s"), then the current entry
+ * value will be substituted in its place.
+ *
+ * @return the summary with appropriate string substitution
+ */
+ @Override
+ public CharSequence getSummary() {
+ final CharSequence entry = getEntry();
+ if (mSummary == null || entry == null) {
+ return super.getSummary();
+ } else {
+ return String.format(mSummary, entry);
+ }
+ }
+
+ /**
+ * Sets the summary for this Preference with a CharSequence.
+ * If the summary has a
+ * {@linkplain java.lang.String#format String formatting}
+ * marker in it (i.e. "%s" or "%1$s"), then the current entry
+ * value will be substituted in its place when it's retrieved.
+ *
+ * @param summary The summary for the preference.
+ */
+ @Override
+ public void setSummary(CharSequence summary) {
+ super.setSummary(summary);
+ if (summary == null && mSummary != null) {
+ mSummary = null;
+ } else if (summary != null && !summary.equals(mSummary)) {
+ mSummary = summary.toString();
+ }
+ }
+
+ /**
* Sets the value to the given index from the entry values.
*
* @param index The index of the value to set.
diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java
index 95e54324..fae5f1a 100644
--- a/core/java/android/preference/PreferenceScreen.java
+++ b/core/java/android/preference/PreferenceScreen.java
@@ -80,6 +80,8 @@
private ListAdapter mRootAdapter;
private Dialog mDialog;
+
+ private ListView mListView;
/**
* Do NOT use this constructor, use {@link PreferenceManager#createPreferenceScreen(Context)}.
@@ -145,15 +147,18 @@
private void showDialog(Bundle state) {
Context context = getContext();
- ListView listView = new ListView(context);
- bind(listView);
+ if (mListView != null) {
+ mListView.setAdapter(null);
+ }
+ mListView = new ListView(context);
+ bind(mListView);
// Set the title bar if title is available, else no title bar
final CharSequence title = getTitle();
Dialog dialog = mDialog = new Dialog(context, TextUtils.isEmpty(title)
? com.android.internal.R.style.Theme_NoTitleBar
: com.android.internal.R.style.Theme);
- dialog.setContentView(listView);
+ dialog.setContentView(mListView);
if (!TextUtils.isEmpty(title)) {
dialog.setTitle(title);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0ff1976..38c5dc4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3640,7 +3640,7 @@
while (intent == null && c.moveToNext()) {
try {
String intentURI = c.getString(c.getColumnIndexOrThrow(INTENT));
- intent = Intent.getIntent(intentURI);
+ intent = Intent.parseUri(intentURI, 0);
} catch (java.net.URISyntaxException e) {
// The stored URL is bad... ignore it.
} catch (IllegalArgumentException e) {
@@ -3698,7 +3698,7 @@
ContentValues values = new ContentValues();
if (title != null) values.put(TITLE, title);
if (folder != null) values.put(FOLDER, folder);
- values.put(INTENT, intent.toURI());
+ values.put(INTENT, intent.toUri(0));
if (shortcut != 0) values.put(SHORTCUT, (int) shortcut);
values.put(ORDERING, ordering);
return cr.insert(CONTENT_URI, values);
@@ -3750,7 +3750,7 @@
Intent intent;
try {
- intent = Intent.getIntent(intentUri);
+ intent = Intent.parseUri(intentUri, 0);
} catch (URISyntaxException e) {
return "";
}
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index f0f2ecb..62f66b6 100755
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -25,6 +25,7 @@
import android.database.Cursor;
import android.database.sqlite.SqliteWrapper;
import android.net.Uri;
+import android.os.Environment;
import android.telephony.SmsMessage;
import android.text.TextUtils;
import android.util.Config;
@@ -561,15 +562,24 @@
* values:</p>
*
* <ul>
- * <li><em>transactionId (Integer)</em> - The WAP transaction
- * ID</li>
+ * <li><em>transactionId (Integer)</em> - The WAP transaction ID</li>
* <li><em>pduType (Integer)</em> - The WAP PDU type</li>
* <li><em>header (byte[])</em> - The header of the message</li>
* <li><em>data (byte[])</em> - The data payload of the message</li>
+ * <li><em>contentTypeParameters (HashMap<String,String>)</em>
+ * - Any parameters associated with the content type
+ * (decoded from the WSP Content-Type header)</li>
* </ul>
*
* <p>If a BroadcastReceiver encounters an error while processing
* this intent it should set the result code appropriately.</p>
+ *
+ * <p>The contentTypeParameters extra value is map of content parameters keyed by
+ * their names.</p>
+ *
+ * <p>If any unassigned well-known parameters are encountered, the key of the map will
+ * be 'unassigned/0x...', where '...' is the hex value of the unassigned parameter. If
+ * a parameter has No-Value the value in the map will be null.</p>
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String WAP_PUSH_RECEIVED_ACTION =
@@ -582,7 +592,7 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String SIM_FULL_ACTION =
- "android.provider.Telephony.SIM_FULL";
+ "android.provider.Telephony.SIM_FULL";
/**
* Broadcast Action: An incoming SMS has been rejected by the
@@ -1525,7 +1535,8 @@
* which streams the captured image to the uri. Internally we write the media content
* to this file. It's named '.temp.jpg' so Gallery won't pick it up.
*/
- public static final String SCRAP_FILE_PATH = "/sdcard/mms/scrapSpace/.temp.jpg";
+ public static final String SCRAP_FILE_PATH =
+ Environment.getExternalStorageDirectory().getPath() + "/mms/scrapSpace/.temp.jpg";
}
public static final class Intents {
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index c877c5c..568d465 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -30,6 +30,8 @@
import java.util.HashMap;
import java.util.Set;
+import android.os.PowerManager;
+
/**
* TODO: Move this to
@@ -51,6 +53,9 @@
private final BluetoothService mBluetoothService;
private final BluetoothAdapter mAdapter;
private final Context mContext;
+ // The WakeLock is used for bringing up the LCD during a pairing request
+ // from remote device when Android is in Suspend state.
+ private PowerManager.WakeLock mWakeLock;
private static final int EVENT_RESTART_BLUETOOTH = 1;
private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 2;
@@ -105,6 +110,11 @@
mContext = context;
mPasskeyAgentRequestData = new HashMap();
mAdapter = adapter;
+ //WakeLock instantiation in BluetoothEventLoop class
+ PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP
+ | PowerManager.ON_AFTER_RELEASE, TAG);
+ mWakeLock.setReferenceCounted(false);
initializeNativeDataNative();
}
@@ -398,37 +408,46 @@
mHandler.sendMessageDelayed(message, 1500);
return;
}
-
+ // Acquire wakelock during PIN code request to bring up LCD display
+ mWakeLock.acquire();
Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
BluetoothDevice.PAIRING_VARIANT_CONSENT);
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ // Release wakelock to allow the LCD to go off after the PIN popup notifcation.
+ mWakeLock.release();
return;
}
private void onRequestPasskeyConfirmation(String objectPath, int passkey, int nativeData) {
String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
if (address == null) return;
-
+ // Acquire wakelock during PIN code request to bring up LCD display
+ mWakeLock.acquire();
Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey);
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION);
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ // Release wakelock to allow the LCD to go off after the PIN popup notifcation.
+ mWakeLock.release();
return;
}
private void onRequestPasskey(String objectPath, int nativeData) {
String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
if (address == null) return;
-
+ // Acquire wakelock during PIN code request to bring up LCD display
+ mWakeLock.acquire();
Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
BluetoothDevice.PAIRING_VARIANT_PASSKEY);
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ // Release wakelock to allow the LCD to go off after the PIN popup notifcation.
+ mWakeLock.release();
return;
}
@@ -461,10 +480,14 @@
if (mBluetoothService.attemptAutoPair(address)) return;
}
}
+ // Acquire wakelock during PIN code request to bring up LCD display
+ mWakeLock.acquire();
Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PIN);
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ // Release wakelock to allow the LCD to go off after the PIN popup notifcation.
+ mWakeLock.release();
return;
}
@@ -472,12 +495,16 @@
String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
if (address == null) return;
+ // Acquire wakelock during PIN code request to bring up LCD display
+ mWakeLock.acquire();
Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey);
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY);
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ //Release wakelock to allow the LCD to go off after the PIN popup notifcation.
+ mWakeLock.release();
}
private void onRequestOobData(String objectPath , int nativeData) {
@@ -566,6 +593,8 @@
private void onDiscoverServicesResult(String deviceObjectPath, boolean result) {
String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+ if (address == null) return;
+
// We don't parse the xml here, instead just query Bluez for the properties.
if (result) {
mBluetoothService.updateRemoteDevicePropertiesCache(address);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 26346d2..9f362d3 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -514,8 +514,11 @@
mLayout.windowAnimations =
com.android.internal.R.style.Animation_Wallpaper;
mInputChannel = new InputChannel();
- mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets,
- mInputChannel);
+ if (mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets,
+ mInputChannel) < 0) {
+ Log.w(TAG, "Failed to add window while updating wallpaper surface.");
+ return;
+ }
mCreated = true;
InputQueue.registerInputChannel(mInputChannel, mInputHandler,
diff --git a/core/java/android/text/InputFilter.java b/core/java/android/text/InputFilter.java
index 2f55677..8d4b08e 100644
--- a/core/java/android/text/InputFilter.java
+++ b/core/java/android/text/InputFilter.java
@@ -88,7 +88,14 @@
} else if (keep >= end - start) {
return null; // keep original
} else {
- return source.subSequence(start, start + keep);
+ keep += start;
+ if (Character.isHighSurrogate(source.charAt(keep - 1))) {
+ --keep;
+ if (keep == start) {
+ return "";
+ }
+ }
+ return source.subSequence(start, keep);
}
}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
old mode 100644
new mode 100755
index 38ac9b7..4e197cd
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -749,6 +749,9 @@
if (line == getLineCount() - 1)
max++;
+ if (line != getLineCount() - 1)
+ max = TextUtils.getOffsetBefore(mText, getLineEnd(line));
+
int best = min;
float bestdist = Math.abs(getPrimaryHorizontal(best) - horiz);
@@ -893,7 +896,7 @@
Directions dirs = getLineDirections(line);
if (line != getLineCount() - 1)
- end--;
+ end = TextUtils.getOffsetBefore(mText, end);
float horiz = getPrimaryHorizontal(offset);
@@ -993,7 +996,7 @@
Directions dirs = getLineDirections(line);
if (line != getLineCount() - 1)
- end--;
+ end = TextUtils.getOffsetBefore(mText, end);
float horiz = getPrimaryHorizontal(offset);
@@ -1564,7 +1567,8 @@
h = dir * nextTab(text, start, end, h * dir, tabs);
}
- if (bm != null) {
+ if (j != there && bm != null) {
+ if (offset == start + j) return h;
workPaint.set(paint);
Styled.measureText(paint, workPaint, text,
j, j + 2, null);
@@ -1958,4 +1962,3 @@
new Directions(new short[] { 0, 32767 });
}
-
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
old mode 100644
new mode 100755
index f02ad2a..d0d2482
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -313,7 +313,9 @@
class);
if (spanned == null) {
- paint.getTextWidths(sub, i, next, widths);
+ final int actualNum = paint.getTextWidths(sub, i, next, widths);
+ if (next - i > actualNum)
+ adjustTextWidths(widths, sub, i, next, actualNum);
System.arraycopy(widths, 0, widths,
end - start + (i - start), next - i);
@@ -321,9 +323,11 @@
} else {
mWorkPaint.baselineShift = 0;
- Styled.getTextWidths(paint, mWorkPaint,
- spanned, i, next,
- widths, fm);
+ final int actualNum = Styled.getTextWidths(paint, mWorkPaint,
+ spanned, i, next,
+ widths, fm);
+ if (next - i > actualNum)
+ adjustTextWidths(widths, spanned, i, next, actualNum);
System.arraycopy(widths, 0, widths,
end - start + (i - start), next - i);
@@ -966,6 +970,22 @@
return low;
}
+ private static void adjustTextWidths(float[] widths, CharSequence text,
+ int curPos, int nextPos, int actualNum) {
+ try {
+ int dstIndex = nextPos - curPos - 1;
+ for (int srcIndex = actualNum - 1; srcIndex >= 0; srcIndex--) {
+ final char c = text.charAt(dstIndex + curPos);
+ if (c >= 0xD800 && c <= 0xDFFF) {
+ widths[dstIndex--] = 0.0f;
+ }
+ widths[dstIndex--] = widths[srcIndex];
+ }
+ } catch (IndexOutOfBoundsException e) {
+ Log.e("text", "adjust text widths failed");
+ }
+ }
+
private int out(CharSequence text, int start, int end,
int above, int below, int top, int bottom, int v,
float spacingmult, float spacingadd,
diff --git a/core/java/android/text/Styled.java b/core/java/android/text/Styled.java
old mode 100644
new mode 100755
index 513b2cd..13cc42c
--- a/core/java/android/text/Styled.java
+++ b/core/java/android/text/Styled.java
@@ -203,9 +203,10 @@
}
}
+ int result;
if (replacement == null) {
workPaint.getFontMetricsInt(fmi);
- workPaint.getTextWidths(text, start, end, widths);
+ result = workPaint.getTextWidths(text, start, end, widths);
} else {
int wid = replacement.getSize(workPaint, text, start, end, fmi);
@@ -214,8 +215,9 @@
for (int i = start + 1; i < end; i++)
widths[i - start] = 0;
}
+ result = end - start;
}
- return end - start;
+ return result;
}
/**
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 4e2c3c3..353b628 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -502,7 +502,7 @@
}
}
} else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) {
- count = duration / DAY_IN_MILLIS;
+ count = getNumberOfDaysPassed(time, now);
if (past) {
if (abbrevRelative) {
resId = com.android.internal.R.plurals.abbrev_num_days_ago;
@@ -527,6 +527,24 @@
}
/**
+ * Returns the number of days passed between two dates.
+ *
+ * @param date1 first date
+ * @param date2 second date
+ * @return number of days passed between to dates.
+ */
+ private synchronized static long getNumberOfDaysPassed(long date1, long date2) {
+ if (sThenTime == null) {
+ sThenTime = new Time();
+ }
+ sThenTime.set(date1);
+ int day1 = Time.getJulianDay(date1, sThenTime.gmtoff);
+ sThenTime.set(date2);
+ int day2 = Time.getJulianDay(date2, sThenTime.gmtoff);
+ return Math.abs(day2 - day1);
+ }
+
+ /**
* Return string describing the elapsed time since startTime formatted like
* "[relative time/date], [time]".
* <p>
@@ -624,14 +642,18 @@
private static void initFormatStrings() {
synchronized (sLock) {
- Resources r = Resources.getSystem();
- Configuration cfg = r.getConfiguration();
- if (sLastConfig == null || !sLastConfig.equals(cfg)) {
- sLastConfig = cfg;
- sStatusTimeFormat = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT);
- sElapsedFormatMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_mm_ss);
- sElapsedFormatHMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_h_mm_ss);
- }
+ initFormatStringsLocked();
+ }
+ }
+
+ private static void initFormatStringsLocked() {
+ Resources r = Resources.getSystem();
+ Configuration cfg = r.getConfiguration();
+ if (sLastConfig == null || !sLastConfig.equals(cfg)) {
+ sLastConfig = cfg;
+ sStatusTimeFormat = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT);
+ sElapsedFormatMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_mm_ss);
+ sElapsedFormatHMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_h_mm_ss);
}
}
@@ -641,8 +663,10 @@
* @hide
*/
public static final CharSequence timeString(long millis) {
- initFormatStrings();
- return sStatusTimeFormat.format(millis);
+ synchronized (sLock) {
+ initFormatStringsLocked();
+ return sStatusTimeFormat.format(millis);
+ }
}
/**
@@ -1595,40 +1619,45 @@
public static CharSequence getRelativeTimeSpanString(Context c, long millis,
boolean withPreposition) {
+ String result;
long now = System.currentTimeMillis();
long span = now - millis;
- if (sNowTime == null) {
- sNowTime = new Time();
- sThenTime = new Time();
- }
+ synchronized (DateUtils.class) {
+ if (sNowTime == null) {
+ sNowTime = new Time();
+ }
- sNowTime.set(now);
- sThenTime.set(millis);
+ if (sThenTime == null) {
+ sThenTime = new Time();
+ }
- String result;
- int prepositionId;
- if (span < DAY_IN_MILLIS && sNowTime.weekDay == sThenTime.weekDay) {
- // Same day
- int flags = FORMAT_SHOW_TIME;
- result = formatDateRange(c, millis, millis, flags);
- prepositionId = R.string.preposition_for_time;
- } else if (sNowTime.year != sThenTime.year) {
- // Different years
- int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;
- result = formatDateRange(c, millis, millis, flags);
+ sNowTime.set(now);
+ sThenTime.set(millis);
- // This is a date (like "10/31/2008" so use the date preposition)
- prepositionId = R.string.preposition_for_date;
- } else {
- // Default
- int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
- result = formatDateRange(c, millis, millis, flags);
- prepositionId = R.string.preposition_for_date;
- }
- if (withPreposition) {
- Resources res = c.getResources();
- result = res.getString(prepositionId, result);
+ int prepositionId;
+ if (span < DAY_IN_MILLIS && sNowTime.weekDay == sThenTime.weekDay) {
+ // Same day
+ int flags = FORMAT_SHOW_TIME;
+ result = formatDateRange(c, millis, millis, flags);
+ prepositionId = R.string.preposition_for_time;
+ } else if (sNowTime.year != sThenTime.year) {
+ // Different years
+ int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;
+ result = formatDateRange(c, millis, millis, flags);
+
+ // This is a date (like "10/31/2008" so use the date preposition)
+ prepositionId = R.string.preposition_for_date;
+ } else {
+ // Default
+ int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
+ result = formatDateRange(c, millis, millis, flags);
+ prepositionId = R.string.preposition_for_date;
+ }
+ if (withPreposition) {
+ Resources res = c.getResources();
+ result = res.getString(prepositionId, result);
+ }
}
return result;
}
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 8eae111..c05a8fe 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -32,7 +32,7 @@
private static final String Y_M_D_T_H_M_S_000 = "%Y-%m-%dT%H:%M:%S.000";
private static final String Y_M_D_T_H_M_S_000_Z = "%Y-%m-%dT%H:%M:%S.000Z";
private static final String Y_M_D = "%Y-%m-%d";
-
+
public static final String TIMEZONE_UTC = "UTC";
/**
@@ -170,11 +170,11 @@
public Time() {
this(TimeZone.getDefault().getID());
}
-
+
/**
* A copy constructor. Construct a Time object by copying the given
* Time object. No normalization occurs.
- *
+ *
* @param other
*/
public Time(Time other) {
@@ -185,17 +185,17 @@
* Ensures the values in each field are in range. For example if the
* current value of this calendar is March 32, normalize() will convert it
* to April 1. It also fills in weekDay, yearDay, isDst and gmtoff.
- *
+ *
* <p>
* If "ignoreDst" is true, then this method sets the "isDst" field to -1
* (the "unknown" value) before normalizing. It then computes the
* correct value for "isDst".
- *
+ *
* <p>
* See {@link #toMillis(boolean)} for more information about when to
* use <tt>true</tt> or <tt>false</tt> for "ignoreDst".
- *
- * @return the UTC milliseconds since the epoch
+ *
+ * @return the UTC milliseconds since the epoch
*/
native public long normalize(boolean ignoreDst);
@@ -379,13 +379,13 @@
* Parses a date-time string in either the RFC 2445 format or an abbreviated
* format that does not include the "time" field. For example, all of the
* following strings are valid:
- *
+ *
* <ul>
* <li>"20081013T160000Z"</li>
* <li>"20081013T160000"</li>
* <li>"20081013"</li>
* </ul>
- *
+ *
* Returns whether or not the time is in UTC (ends with Z). If the string
* ends with "Z" then the timezone is set to UTC. If the date-time string
* included only a date and no time field, then the <code>allDay</code>
@@ -396,10 +396,10 @@
* <code>yearDay</code>, and <code>gmtoff</code> are always set to zero,
* and the field <code>isDst</code> is set to -1 (unknown). To set those
* fields, call {@link #normalize(boolean)} after parsing.
- *
+ *
* To parse a date-time string and convert it to UTC milliseconds, do
* something like this:
- *
+ *
* <pre>
* Time time = new Time();
* String date = "20081013T160000Z";
@@ -428,25 +428,25 @@
* Parse a time in RFC 3339 format. This method also parses simple dates
* (that is, strings that contain no time or time offset). For example,
* all of the following strings are valid:
- *
+ *
* <ul>
* <li>"2008-10-13T16:00:00.000Z"</li>
* <li>"2008-10-13T16:00:00.000+07:00"</li>
* <li>"2008-10-13T16:00:00.000-07:00"</li>
* <li>"2008-10-13"</li>
* </ul>
- *
+ *
* <p>
* If the string contains a time and time offset, then the time offset will
* be used to convert the time value to UTC.
* </p>
- *
+ *
* <p>
* If the given string contains just a date (with no time field), then
* the {@link #allDay} field is set to true and the {@link #hour},
* {@link #minute}, and {@link #second} fields are set to zero.
* </p>
- *
+ *
* <p>
* Returns true if the resulting time value is in UTC time.
* </p>
@@ -462,7 +462,7 @@
}
return false;
}
-
+
native private boolean nativeParse3339(String s);
/**
@@ -484,13 +484,13 @@
* <em>not</em> change any of the fields in this Time object. If you want
* to normalize the fields in this Time object and also get the milliseconds
* then use {@link #normalize(boolean)}.
- *
+ *
* <p>
* If "ignoreDst" is false, then this method uses the current setting of the
* "isDst" field and will adjust the returned time if the "isDst" field is
* wrong for the given time. See the sample code below for an example of
* this.
- *
+ *
* <p>
* If "ignoreDst" is true, then this method ignores the current setting of
* the "isDst" field in this Time object and will instead figure out the
@@ -499,27 +499,27 @@
* correct value of the "isDst" field is when the time is inherently
* ambiguous because it falls in the hour that is repeated when switching
* from Daylight-Saving Time to Standard Time.
- *
+ *
* <p>
* Here is an example where <tt>toMillis(true)</tt> adjusts the time,
* assuming that DST changes at 2am on Sunday, Nov 4, 2007.
- *
+ *
* <pre>
* Time time = new Time();
- * time.set(2007, 10, 4); // set the date to Nov 4, 2007, 12am
+ * time.set(4, 10, 2007); // set the date to Nov 4, 2007, 12am
* time.normalize(); // this sets isDst = 1
* time.monthDay += 1; // changes the date to Nov 5, 2007, 12am
* millis = time.toMillis(false); // millis is Nov 4, 2007, 11pm
* millis = time.toMillis(true); // millis is Nov 5, 2007, 12am
* </pre>
- *
+ *
* <p>
* To avoid this problem, use <tt>toMillis(true)</tt>
* after adding or subtracting days or explicitly setting the "monthDay"
* field. On the other hand, if you are adding
* or subtracting hours or minutes, then you should use
* <tt>toMillis(false)</tt>.
- *
+ *
* <p>
* You should also use <tt>toMillis(false)</tt> if you want
* to read back the same milliseconds that you set with {@link #set(long)}
@@ -531,14 +531,14 @@
* Sets the fields in this Time object given the UTC milliseconds. After
* this method returns, all the fields are normalized.
* This also sets the "isDst" field to the correct value.
- *
+ *
* @param millis the time in UTC milliseconds since the epoch.
*/
native public void set(long millis);
/**
* Format according to RFC 2445 DATETIME type.
- *
+ *
* <p>
* The same as format("%Y%m%dT%H%M%S").
*/
@@ -584,7 +584,7 @@
* Sets the date from the given fields. Also sets allDay to true.
* Sets weekDay, yearDay and gmtoff to 0, and isDst to -1.
* Call {@link #normalize(boolean)} if you need those.
- *
+ *
* @param monthDay the day of the month (in the range [1,31])
* @param month the zero-based month number (in the range [0,11])
* @param year the year
@@ -606,7 +606,7 @@
/**
* Returns true if the time represented by this Time object occurs before
* the given time.
- *
+ *
* @param that a given Time object to compare against
* @return true if this time is less than the given time
*/
@@ -618,7 +618,7 @@
/**
* Returns true if the time represented by this Time object occurs after
* the given time.
- *
+ *
* @param that a given Time object to compare against
* @return true if this time is greater than the given time
*/
@@ -632,12 +632,12 @@
* closest Thursday yearDay.
*/
private static final int[] sThursdayOffset = { -3, 3, 2, 1, 0, -1, -2 };
-
+
/**
* Computes the week number according to ISO 8601. The current Time
* object must already be normalized because this method uses the
* yearDay and weekDay fields.
- *
+ *
* <p>
* In IS0 8601, weeks start on Monday.
* The first week of the year (week 1) is defined by ISO 8601 as the
@@ -645,12 +645,12 @@
* Or equivalently, the week containing January 4. Or equivalently,
* the week with the year's first Thursday in it.
* </p>
- *
+ *
* <p>
* The week number can be calculated by counting Thursdays. Week N
* contains the Nth Thursday of the year.
* </p>
- *
+ *
* @return the ISO week number.
*/
public int getWeekNumber() {
@@ -661,7 +661,7 @@
if (closestThursday >= 0 && closestThursday <= 364) {
return closestThursday / 7 + 1;
}
-
+
// The week crosses a year boundary.
Time temp = new Time(this);
temp.monthDay += sThursdayOffset[weekDay];
@@ -670,7 +670,7 @@
}
/**
- * Return a string in the RFC 3339 format.
+ * Return a string in the RFC 3339 format.
* <p>
* If allDay is true, expresses the time as Y-M-D</p>
* <p>
@@ -691,13 +691,13 @@
int offset = (int)Math.abs(gmtoff);
int minutes = (offset % 3600) / 60;
int hours = offset / 3600;
-
+
return String.format("%s%s%02d:%02d", base, sign, hours, minutes);
}
}
-
+
/**
- * Returns true if the day of the given time is the epoch on the Julian Calendar
+ * Returns true if the day of the given time is the epoch on the Julian Calendar
* (January 1, 1970 on the Gregorian calendar).
*
* @param time the time to test
@@ -707,7 +707,7 @@
long millis = time.toMillis(true);
return getJulianDay(millis, 0) == EPOCH_JULIAN_DAY;
}
-
+
/**
* Computes the Julian day number, given the UTC milliseconds
* and the offset (in seconds) from UTC. The Julian day for a given
@@ -716,10 +716,10 @@
* what timezone is being used. The Julian day is useful for testing
* if two events occur on the same day and for determining the relative
* time of an event from the present ("yesterday", "3 days ago", etc.).
- *
+ *
* <p>
* Use {@link #toMillis(boolean)} to get the milliseconds.
- *
+ *
* @param millis the time in UTC milliseconds
* @param gmtoff the offset from UTC in seconds
* @return the Julian day
@@ -729,7 +729,7 @@
long julianDay = (millis + offsetMillis) / DateUtils.DAY_IN_MILLIS;
return (int) julianDay + EPOCH_JULIAN_DAY;
}
-
+
/**
* <p>Sets the time from the given Julian day number, which must be based on
* the same timezone that is set in this Time object. The "gmtoff" field
@@ -738,7 +738,7 @@
* After this method returns all the fields will be normalized and the time
* will be set to 12am at the beginning of the given Julian day.
* </p>
- *
+ *
* <p>
* The only exception to this is if 12am does not exist for that day because
* of daylight saving time. For example, Cairo, Eqypt moves time ahead one
@@ -746,7 +746,7 @@
* also change daylight saving time at 12am. In those cases, the time
* will be set to 1am.
* </p>
- *
+ *
* @param julianDay the Julian day in the timezone for this Time object
* @return the UTC milliseconds for the beginning of the Julian day
*/
@@ -756,13 +756,13 @@
// the day.
long millis = (julianDay - EPOCH_JULIAN_DAY) * DateUtils.DAY_IN_MILLIS;
set(millis);
-
+
// Figure out how close we are to the requested Julian day.
// We can't be off by more than a day.
int approximateDay = getJulianDay(millis, gmtoff);
int diff = julianDay - approximateDay;
monthDay += diff;
-
+
// Set the time to 12am and re-normalize.
hour = 0;
minute = 0;
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index 9860588..e2293e4 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -498,10 +498,6 @@
return 0;
}
-
- public final boolean equals(Object o) {
- return false;
- }
};
Collections.sort(links, c);
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 85ce5e1..5c281cf 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -190,7 +190,7 @@
int pos = 0;
fieldLen -= 1;
while (pos < fieldLen) {
- formatStr[pos] = ' ';
+ formatStr[pos++] = ' ';
}
formatStr[pos] = '0';
return pos+1;
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 15fb839..8ad9a62 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -266,7 +266,7 @@
/**
- * Do the "beams" w.r.t the given direcition's axos of rect1 and rect2 overlap?
+ * Do the "beams" w.r.t the given direcition's axis of rect1 and rect2 overlap?
* @param direction the direction (up, down, left, right)
* @param rect1 The first rectangle
* @param rect2 The second rectangle
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 194c013..d5e411a 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -38,7 +38,7 @@
* for the device you are running on. For example:
*
* <pre>LayoutInflater inflater = (LayoutInflater)context.getSystemService
- * Context.LAYOUT_INFLATER_SERVICE);</pre>
+ * (Context.LAYOUT_INFLATER_SERVICE);</pre>
*
* <p>
* To create a new LayoutInflater with an additional {@link Factory} for your
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index c2fec96..952face 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -620,7 +620,7 @@
ev.mEdgeFlags = o.mEdgeFlags;
ev.mDownTimeNano = o.mDownTimeNano;
ev.mAction = o.mAction;
- o.mFlags = o.mFlags;
+ ev.mFlags = o.mFlags;
ev.mMetaState = o.mMetaState;
ev.mXOffset = o.mXOffset;
ev.mYOffset = o.mYOffset;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1766345..232dd6a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3944,6 +3944,7 @@
imm.focusOut(this);
}
removeLongPressCallback();
+ removeTapCallback();
onFocusLost();
} else if (imm != null && (mPrivateFlags & FOCUSED) != 0) {
imm.focusIn(this);
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index c58207e..ccaef40 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1065,10 +1065,11 @@
}
}
mSurfaceHolder.mSurfaceLock.lock();
- // Make surface invalid.
- //mSurfaceHolder.mSurface.copyFrom(mSurface);
- mSurfaceHolder.mSurface = new Surface();
- mSurfaceHolder.mSurfaceLock.unlock();
+ try {
+ mSurfaceHolder.mSurface = new Surface();
+ } finally {
+ mSurfaceHolder.mSurfaceLock.unlock();
+ }
}
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 091844e..d6dcd4c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1050,14 +1050,6 @@
gravity = o.gravity;
changes |= LAYOUT_CHANGED;
}
- if (horizontalMargin != o.horizontalMargin) {
- horizontalMargin = o.horizontalMargin;
- changes |= LAYOUT_CHANGED;
- }
- if (verticalMargin != o.verticalMargin) {
- verticalMargin = o.verticalMargin;
- changes |= LAYOUT_CHANGED;
- }
if (format != o.format) {
format = o.format;
changes |= FORMAT_CHANGED;
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 7fd993a..021b53c 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -18,6 +18,7 @@
import android.net.http.EventHandler;
import android.net.http.RequestHandle;
+import android.os.Build;
import android.util.Log;
import android.webkit.CacheManager.CacheResult;
@@ -35,6 +36,7 @@
private int mCacheMode;
private String mReferrer;
private String mContentType;
+ private final String mUaprofHeader;
private static final int URI_PROTOCOL = 0x100;
@@ -57,6 +59,8 @@
mMethod = method;
mCacheMode = WebSettings.LOAD_NORMAL;
mSettings = settings;
+ mUaprofHeader = mListener.getContext().getResources().getString(
+ com.android.internal.R.string.config_useragentprofile_url, Build.MODEL);
}
public void setReferrer(String ref) {
@@ -356,6 +360,11 @@
}
mHeaders.put("User-Agent", mSettings.getUserAgentString());
+
+ // Set the x-wap-profile header
+ if (mUaprofHeader != null && mUaprofHeader.length() > 0) {
+ mHeaders.put("x-wap-profile", mUaprofHeader);
+ }
}
/**
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index ca9ad53..c1ac180 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -369,10 +369,13 @@
sMimeTypeMap.loadEntry("application/x-xfig", "fig");
sMimeTypeMap.loadEntry("application/xhtml+xml", "xhtml");
sMimeTypeMap.loadEntry("audio/3gpp", "3gpp");
+ sMimeTypeMap.loadEntry("audio/amr", "amr");
sMimeTypeMap.loadEntry("audio/basic", "snd");
sMimeTypeMap.loadEntry("audio/midi", "mid");
sMimeTypeMap.loadEntry("audio/midi", "midi");
sMimeTypeMap.loadEntry("audio/midi", "kar");
+ sMimeTypeMap.loadEntry("audio/midi", "xmf");
+ sMimeTypeMap.loadEntry("audio/mobile-xmf", "mxmf");
sMimeTypeMap.loadEntry("audio/mpeg", "mpga");
sMimeTypeMap.loadEntry("audio/mpeg", "mpega");
sMimeTypeMap.loadEntry("audio/mpeg", "mp2");
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1915425..7f7a25e 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -313,6 +313,10 @@
// true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
private boolean mAutoRedraw;
+ // Reference to the AlertDialog displayed by InvokeListBox.
+ // It's used to dismiss the dialog in destroy if not done before.
+ private AlertDialog mListBoxDialog = null;
+
static final String LOGTAG = "webview";
private static class ExtendedZoomControls extends FrameLayout {
@@ -1301,6 +1305,10 @@
*/
public void destroy() {
clearHelpers();
+ if (mListBoxDialog != null) {
+ mListBoxDialog.dismiss();
+ mListBoxDialog = null;
+ }
if (mWebViewCore != null) {
// Set the handlers to null before destroying WebViewCore so no
// more messages will be posted.
@@ -4173,6 +4181,16 @@
}
}
+ if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
+ pageUp(false);
+ return true;
+ }
+
+ if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
+ pageDown(false);
+ return true;
+ }
+
if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
&& keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
switchOutDrawHistory();
@@ -7540,7 +7558,7 @@
EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
}});
}
- final AlertDialog dialog = b.create();
+ mListBoxDialog = b.create();
listView.setAdapter(adapter);
listView.setFocusableInTouchMode(true);
// There is a bug (1250103) where the checks in a ListView with
@@ -7562,7 +7580,8 @@
int position, long id) {
mWebViewCore.sendMessage(
EventHub.SINGLE_LISTBOX_CHOICE, (int)id, 0);
- dialog.dismiss();
+ mListBoxDialog.dismiss();
+ mListBoxDialog = null;
}
});
if (mSelection != -1) {
@@ -7574,13 +7593,14 @@
adapter.registerDataSetObserver(observer);
}
}
- dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ mListBoxDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
mWebViewCore.sendMessage(
EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
+ mListBoxDialog = null;
}
});
- dialog.show();
+ mListBoxDialog.show();
}
}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index f54b207..e36602f 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -58,7 +58,11 @@
// Load libwebcore during static initialization. This happens in the
// zygote process so it will be shared read-only across all app
// processes.
- System.loadLibrary("webcore");
+ try {
+ System.loadLibrary("webcore");
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(LOGTAG, "Unable to load webcore library");
+ }
}
/*
diff --git a/core/java/android/widget/AbsoluteLayout.java b/core/java/android/widget/AbsoluteLayout.java
index b829655..970cbe3 100644
--- a/core/java/android/widget/AbsoluteLayout.java
+++ b/core/java/android/widget/AbsoluteLayout.java
@@ -187,7 +187,7 @@
* </ul>
*
* @param c the application environment
- * @param attrs the set of attributes fom which to extract the layout
+ * @param attrs the set of attributes from which to extract the layout
* parameters values
*/
public LayoutParams(Context c, AttributeSet attrs) {
diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java
index 32e5504..03ada94 100644
--- a/core/java/android/widget/ArrayAdapter.java
+++ b/core/java/android/widget/ArrayAdapter.java
@@ -24,6 +24,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import java.util.Comparator;
import java.util.Collections;
@@ -83,7 +84,7 @@
*/
private boolean mNotifyOnChange = true;
- private Context mContext;
+ private Context mContext;
private ArrayList<T> mOriginalValues;
private ArrayFilter mFilter;
@@ -181,6 +182,44 @@
}
/**
+ * Adds the specified Collection at the end of the array.
+ *
+ * @param collection The Collection to add at the end of the array.
+ */
+ public void addAll(Collection<? extends T> collection) {
+ if (mOriginalValues != null) {
+ synchronized (mLock) {
+ mOriginalValues.addAll(collection);
+ if (mNotifyOnChange) notifyDataSetChanged();
+ }
+ } else {
+ mObjects.addAll(collection);
+ if (mNotifyOnChange) notifyDataSetChanged();
+ }
+ }
+
+ /**
+ * Adds the specified items at the end of the array.
+ *
+ * @param items The items to add at the end of the array.
+ */
+ public void addAll(T ... items) {
+ if (mOriginalValues != null) {
+ synchronized (mLock) {
+ for (T item : items) {
+ mOriginalValues.add(item);
+ }
+ if (mNotifyOnChange) notifyDataSetChanged();
+ }
+ } else {
+ for (T item : items) {
+ mObjects.add(item);
+ }
+ if (mNotifyOnChange) notifyDataSetChanged();
+ }
+ }
+
+ /**
* Inserts the specified object at the specified index in the array.
*
* @param object The object to insert into the array.
@@ -236,7 +275,7 @@
*/
public void sort(Comparator<? super T> comparator) {
Collections.sort(mObjects, comparator);
- if (mNotifyOnChange) notifyDataSetChanged();
+ if (mNotifyOnChange) notifyDataSetChanged();
}
/**
diff --git a/core/java/android/widget/EdgeGlow.java b/core/java/android/widget/EdgeGlow.java
index 416be86..a11de6f 100644
--- a/core/java/android/widget/EdgeGlow.java
+++ b/core/java/android/widget/EdgeGlow.java
@@ -317,6 +317,7 @@
mEdgeScaleY = mEdgeScaleYStart +
(mEdgeScaleYFinish - mEdgeScaleYStart) *
interp * factor;
+ mState = STATE_RECEDE;
break;
case STATE_RECEDE:
mState = STATE_IDLE;
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 54c4b36..aa68a74 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -154,6 +154,11 @@
int textColorNormal = textColor.getDefaultColor();
mPaint.setColor(textColorNormal);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+
+ // to show mOverlayDrawable properly
+ if (mList.getWidth() > 0 && mList.getHeight() > 0) {
+ onSizeChanged(mList.getWidth(), mList.getHeight(), 0, 0);
+ }
mState = STATE_NONE;
}
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 4482b5b..10e7634 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -66,7 +66,8 @@
public static final NumberPicker.Formatter TWO_DIGIT_FORMATTER =
new NumberPicker.Formatter() {
final StringBuilder mBuilder = new StringBuilder();
- final java.util.Formatter mFmt = new java.util.Formatter(mBuilder);
+ final java.util.Formatter mFmt = new java.util.Formatter(
+ mBuilder, java.util.Locale.US);
final Object[] mArgs = new Object[1];
public String toString(int value) {
mArgs[0] = value;
diff --git a/core/java/android/widget/NumberPickerButton.java b/core/java/android/widget/NumberPickerButton.java
index 1c8579c..292b668 100644
--- a/core/java/android/widget/NumberPickerButton.java
+++ b/core/java/android/widget/NumberPickerButton.java
@@ -85,4 +85,12 @@
mNumberPicker.cancelDecrement();
}
}
+
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+ if (!hasWindowFocus) {
+ cancelLongpress();
+ }
+ }
+
}
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index ec7d927..2a695e5 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -666,8 +666,8 @@
if (mProgress > max) {
mProgress = max;
- refreshProgress(R.id.progress, mProgress, false);
}
+ refreshProgress(R.id.progress, mProgress, false);
}
}
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index afae7ef..b4b355a 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -109,7 +109,6 @@
}
private void initTabWidget() {
- setOrientation(LinearLayout.HORIZONTAL);
mGroupFlags |= FLAG_USE_CHILD_DRAWING_ORDER;
final Context context = mContext;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 572bd8c..68600cf 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -982,6 +982,22 @@
setTypeface(tf, styleIndex);
}
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (enabled == isEnabled()) {
+ return;
+ }
+
+ if (!enabled) {
+ // Hide the soft input if the currently active TextView is disabled
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null && imm.isActive(this)) {
+ imm.hideSoftInputFromWindow(getWindowToken(), 0);
+ }
+ }
+ super.setEnabled(enabled);
+ }
+
/**
* Sets the typeface and style in which the text should be displayed,
* and turns on the fake bold and italic bits in the Paint if the
@@ -4592,7 +4608,7 @@
}
@Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
- if (onCheckIsTextEditor()) {
+ if (onCheckIsTextEditor() && isEnabled()) {
if (mInputMethodState == null) {
mInputMethodState = new InputMethodState();
}
@@ -4666,6 +4682,9 @@
partialStartOffset = 0;
partialEndOffset = N;
} else {
+ // Now use the delta to determine the actual amount of text
+ // we need.
+ partialEndOffset += delta;
// Adjust offsets to ensure we contain full spans.
if (content instanceof Spanned) {
Spanned spanned = (Spanned)content;
@@ -4681,10 +4700,8 @@
}
}
outText.partialStartOffset = partialStartOffset;
- outText.partialEndOffset = partialEndOffset;
- // Now use the delta to determine the actual amount of text
- // we need.
- partialEndOffset += delta;
+ outText.partialEndOffset = partialEndOffset - delta;
+
if (partialStartOffset > N) {
partialStartOffset = N;
} else if (partialStartOffset < 0) {
@@ -4748,6 +4765,10 @@
+ ": " + ims.mTmpExtracted.text);
imm.updateExtractedText(this, req.token,
mInputMethodState.mTmpExtracted);
+ ims.mChangedStart = EXTRACT_UNKNOWN;
+ ims.mChangedEnd = EXTRACT_UNKNOWN;
+ ims.mChangedDelta = 0;
+ ims.mContentChanged = false;
return true;
}
}
@@ -6348,8 +6369,8 @@
ims.mChangedStart = start;
ims.mChangedEnd = start+before;
} else {
- if (ims.mChangedStart > start) ims.mChangedStart = start;
- if (ims.mChangedEnd < (start+before)) ims.mChangedEnd = start+before;
+ ims.mChangedStart = Math.min(ims.mChangedStart, start);
+ ims.mChangedEnd = Math.max(ims.mChangedEnd, start + before - ims.mChangedDelta);
}
ims.mChangedDelta += after-before;
}
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 81ca912..fefdcea 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -434,6 +434,7 @@
View titleTemplate = mWindow.findViewById(R.id.title_template);
titleTemplate.setVisibility(View.GONE);
mIconView.setVisibility(View.GONE);
+ topPanel.setVisibility(View.GONE);
hasTitle = false;
}
}
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index 36f45b2..6039cc2 100755
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -26,6 +26,8 @@
import android.widget.Toast;
import android.util.Log;
import android.location.LocationManager;
+
+import com.android.internal.R;
import com.android.internal.location.GpsNetInitiatedHandler;
/**
@@ -42,10 +44,6 @@
private static final int POSITIVE_BUTTON = AlertDialog.BUTTON_POSITIVE;
private static final int NEGATIVE_BUTTON = AlertDialog.BUTTON_NEGATIVE;
- // Dialog button text
- public static final String BUTTON_TEXT_ACCEPT = "Accept";
- public static final String BUTTON_TEXT_DENY = "Deny";
-
// Received ID from intent, -1 when no notification is in progress
private int notificationId = -1;
@@ -67,12 +65,13 @@
// Set up the "dialog"
final Intent intent = getIntent();
final AlertController.AlertParams p = mAlertParams;
+ Context context = getApplicationContext();
p.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
p.mTitle = intent.getStringExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_TITLE);
p.mMessage = intent.getStringExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_MESSAGE);
- p.mPositiveButtonText = BUTTON_TEXT_ACCEPT;
+ p.mPositiveButtonText = String.format(context.getString(R.string.gpsVerifYes));
p.mPositiveButtonListener = this;
- p.mNegativeButtonText = BUTTON_TEXT_DENY;
+ p.mNegativeButtonText = String.format(context.getString(R.string.gpsVerifNo));
p.mNegativeButtonListener = this;
notificationId = intent.getIntExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_NOTIF_ID, -1);
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index b7255bb..cd1b569 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -71,7 +71,8 @@
private boolean mActionDone;
private Context mContext;
private PowerManager mPowerManager;
- private PowerManager.WakeLock mWakeLock;
+ private PowerManager.WakeLock mCpuWakeLock;
+ private PowerManager.WakeLock mScreenWakeLock;
private Handler mHandler;
private ShutdownThread() {
@@ -138,7 +139,7 @@
private static void beginShutdownSequence(Context context) {
synchronized (sIsStartedGuard) {
if (sIsStarted) {
- Log.d(TAG, "Request to shutdown already running, returning.");
+ Log.d(TAG, "Shutdown sequence already running, returning.");
return;
}
sIsStarted = true;
@@ -159,20 +160,36 @@
pd.show();
- // start the thread that initiates shutdown
sInstance.mContext = context;
sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- sInstance.mWakeLock = null;
+
+ // make sure we never fall asleep again
+ sInstance.mCpuWakeLock = null;
+ try {
+ sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
+ sInstance.mCpuWakeLock.setReferenceCounted(false);
+ sInstance.mCpuWakeLock.acquire();
+ } catch (SecurityException e) {
+ Log.w(TAG, "No permission to acquire wake lock", e);
+ sInstance.mCpuWakeLock = null;
+ }
+
+ // also make sure the screen stays on for better user experience
+ sInstance.mScreenWakeLock = null;
if (sInstance.mPowerManager.isScreenOn()) {
try {
- sInstance.mWakeLock = sInstance.mPowerManager.newWakeLock(
- PowerManager.FULL_WAKE_LOCK, "Shutdown");
- sInstance.mWakeLock.acquire();
+ sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
+ PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
+ sInstance.mScreenWakeLock.setReferenceCounted(false);
+ sInstance.mScreenWakeLock.acquire();
} catch (SecurityException e) {
Log.w(TAG, "No permission to acquire wake lock", e);
- sInstance.mWakeLock = null;
+ sInstance.mScreenWakeLock = null;
}
}
+
+ // start the thread that initiates shutdown
sInstance.mHandler = new Handler() {
};
sInstance.start();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 5000517..1339b44 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -984,7 +984,7 @@
private final Map<String, KernelWakelockStats> readKernelWakelockStats() {
- byte[] buffer = new byte[4096];
+ byte[] buffer = new byte[8192];
int len;
try {
@@ -1031,9 +1031,11 @@
for (endIndex=startIndex;
endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0';
endIndex++);
- // Don't go over the end of the buffer
- if (endIndex < len) {
- endIndex++; // endIndex is an exclusive upper bound.
+ endIndex++; // endIndex is an exclusive upper bound.
+ // Don't go over the end of the buffer, Process.parseProcLine might
+ // write to wlBuffer[endIndex]
+ if (endIndex >= (len - 1) ) {
+ return m;
}
String[] nameStringArray = mProcWakelocksName;
diff --git a/core/java/com/android/internal/os/SamplingProfilerIntegration.java b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
index a3efbc8..ea278a6 100644
--- a/core/java/com/android/internal/os/SamplingProfilerIntegration.java
+++ b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
@@ -96,7 +96,8 @@
pending = true;
snapshotWriter.execute(new Runnable() {
public void run() {
- String dir = "/sdcard/snapshots";
+ String dir =
+ Environment.getExternalStorageDirectory().getPath() + "/snapshots";
if (!dirMade) {
new File(dir).mkdirs();
if (new File(dir).isDirectory()) {
diff --git a/core/java/com/google/android/mms/pdu/PduParser.java b/core/java/com/google/android/mms/pdu/PduParser.java
index 6df4c63..3f185aa 100755
--- a/core/java/com/google/android/mms/pdu/PduParser.java
+++ b/core/java/com/google/android/mms/pdu/PduParser.java
@@ -165,6 +165,13 @@
// or "application/vnd.wap.multipart.related"
// or "application/vnd.wap.multipart.alternative"
return retrieveConf;
+ } else if (ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) {
+ // "application/vnd.wap.multipart.alternative"
+ // should take only the first part.
+ PduPart firstPart = mBody.getPart(0);
+ mBody.removeAll();
+ mBody.addPart(0, firstPart);
+ return retrieveConf;
}
return null;
case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
diff --git a/core/java/com/google/android/mms/util/PduCache.java b/core/java/com/google/android/mms/util/PduCache.java
index 7c3fad7..866ca1e 100644
--- a/core/java/com/google/android/mms/util/PduCache.java
+++ b/core/java/com/google/android/mms/util/PduCache.java
@@ -235,7 +235,7 @@
}
private void removeFromMessageBoxes(Uri key, PduCacheEntry entry) {
- HashSet<Uri> msgBox = mThreads.get(entry.getMessageBox());
+ HashSet<Uri> msgBox = mThreads.get(Long.valueOf(entry.getMessageBox()));
if (msgBox != null) {
msgBox.remove(key);
}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index fe151cd..4d9bb3b 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -25,9 +25,8 @@
extern "C" {
int ifc_enable(const char *ifname);
int ifc_disable(const char *ifname);
-int ifc_add_host_route(const char *ifname, uint32_t addr);
+int ifc_add_route(const char *ifname, const char *destStr, uint32_t prefixLen, const char *gwStr);
int ifc_remove_host_routes(const char *ifname);
-int ifc_set_default_route(const char *ifname, uint32_t gateway);
int ifc_get_default_route(const char *ifname);
int ifc_remove_default_route(const char *ifname);
int ifc_reset_connections(const char *ifname);
@@ -96,13 +95,23 @@
return (jint)result;
}
-static jint android_net_utils_addHostRoute(JNIEnv* env, jobject clazz, jstring ifname, jint addr)
+static jint android_net_utils_addRoute(JNIEnv* env, jobject clazz, jstring ifname,
+ jstring dst, jint prefixLength, jstring gw)
{
int result;
const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- result = ::ifc_add_host_route(nameStr, addr);
+ const char *dstStr = env->GetStringUTFChars(dst, NULL);
+ const char *gwStr = NULL;
+ if (gw != NULL) {
+ gwStr = env->GetStringUTFChars(gw, NULL);
+ }
+ result = ::ifc_add_route(nameStr, dstStr, prefixLength, gwStr);
env->ReleaseStringUTFChars(ifname, nameStr);
+ env->ReleaseStringUTFChars(dst, dstStr);
+ if (gw != NULL) {
+ env->ReleaseStringUTFChars(gw, gwStr);
+ }
return (jint)result;
}
@@ -116,16 +125,6 @@
return (jint)result;
}
-static jint android_net_utils_setDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname, jint gateway)
-{
- int result;
-
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- result = ::ifc_set_default_route(nameStr, gateway);
- env->ReleaseStringUTFChars(ifname, nameStr);
- return (jint)result;
-}
-
static jint android_net_utils_getDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname)
{
int result;
@@ -253,9 +252,9 @@
{ "enableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_enableInterface },
{ "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface },
- { "addHostRoute", "(Ljava/lang/String;I)I", (void *)android_net_utils_addHostRoute },
+ { "addRoute", "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;)I",
+ (void *)android_net_utils_addRoute },
{ "removeHostRoutes", "(Ljava/lang/String;)I", (void *)android_net_utils_removeHostRoutes },
- { "setDefaultRoute", "(Ljava/lang/String;I)I", (void *)android_net_utils_setDefaultRoute },
{ "getDefaultRoute", "(Ljava/lang/String;)I", (void *)android_net_utils_getDefaultRoute },
{ "removeDefaultRoute", "(Ljava/lang/String;)I", (void *)android_net_utils_removeDefaultRoute },
{ "resetConnections", "(Ljava/lang/String;)I", (void *)android_net_utils_resetConnections },
diff --git a/core/jni/android_text_AndroidBidi.cpp b/core/jni/android_text_AndroidBidi.cpp
index 7696bb3..53028c3 100644
--- a/core/jni/android_text_AndroidBidi.cpp
+++ b/core/jni/android_text_AndroidBidi.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "AndroidUnicode"
-#include <jni.h>
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include "utils/misc.h"
#include "utils/Log.h"
@@ -25,14 +25,6 @@
namespace android {
-static void jniThrowException(JNIEnv* env, const char* exc, const char* msg = NULL)
-{
- jclass excClazz = env->FindClass(exc);
- LOG_ASSERT(excClazz, "Unable to find class %s", exc);
-
- env->ThrowNew(excClazz, msg);
-}
-
static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray,
jbyteArray infoArray, int n, jboolean haveInfo)
{
diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp
index 5d8d419..6b90541 100644
--- a/core/jni/android_text_AndroidCharacter.cpp
+++ b/core/jni/android_text_AndroidCharacter.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "AndroidUnicode"
-#include <jni.h>
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include "utils/misc.h"
#include "utils/Log.h"
@@ -50,14 +50,6 @@
namespace android {
-static void jniThrowException(JNIEnv* env, const char* exc, const char* msg = NULL)
-{
- jclass excClazz = env->FindClass(exc);
- LOG_ASSERT(excClazz, "Unable to find class %s", exc);
-
- env->ThrowNew(excClazz, msg);
-}
-
static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray, jbyteArray destArray, int count)
{
jchar* src = env->GetCharArrayElements(srcArray, NULL);
diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp
index 641fbce..a021efd 100644
--- a/core/jni/android_util_StringBlock.cpp
+++ b/core/jni/android_util_StringBlock.cpp
@@ -147,25 +147,6 @@
return array;
}
-static jint android_content_StringBlock_nativeIndexOfString(JNIEnv* env, jobject clazz,
- jint token, jstring str)
-{
- ResStringPool* osb = (ResStringPool*)token;
- if (osb == NULL || str == NULL) {
- doThrow(env, "java/lang/NullPointerException");
- return 0;
- }
-
- const char16_t* str16 = env->GetStringChars(str, NULL);
- jsize strLen = env->GetStringLength(str);
-
- ssize_t idx = osb->indexOfString(str16, strLen);
-
- env->ReleaseStringChars(str, str16);
-
- return idx;
-}
-
static void android_content_StringBlock_nativeDestroy(JNIEnv* env, jobject clazz,
jint token)
{
@@ -193,8 +174,6 @@
(void*) android_content_StringBlock_nativeGetString },
{ "nativeGetStyle", "(II)[I",
(void*) android_content_StringBlock_nativeGetStyle },
- { "nativeIndexOfString","(ILjava/lang/String;)I",
- (void*) android_content_StringBlock_nativeIndexOfString },
{ "nativeDestroy", "(I)V",
(void*) android_content_StringBlock_nativeDestroy },
};
diff --git a/core/res/res/layout/always_use_checkbox.xml b/core/res/res/layout/always_use_checkbox.xml
index baa4bee..a955352 100644
--- a/core/res/res/layout/always_use_checkbox.xml
+++ b/core/res/res/layout/always_use_checkbox.xml
@@ -26,14 +26,14 @@
<CheckBox
android:id="@+id/alwaysUse"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:clickable="true" />
<TextView
android:id="@+id/clearDefaultHint"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:paddingLeft="36dip"
diff --git a/core/res/res/layout/tab_content.xml b/core/res/res/layout/tab_content.xml
index 0ee87ce..79147fb 100644
--- a/core/res/res/layout/tab_content.xml
+++ b/core/res/res/layout/tab_content.xml
@@ -22,8 +22,9 @@
android:layout_width="match_parent" android:layout_height="match_parent">
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent" android:layout_height="match_parent">
- <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent"
- android:layout_height="wrap_content" android:layout_weight="0" />
+ <TabWidget android:id="@android:id/tabs"
+ android:orientation="horizontal" android:layout_width="match_parent"
+ android:layout_height="wrap_content" android:layout_weight="0" />
<FrameLayout android:id="@android:id/tabcontent"
android:layout_width="match_parent" android:layout_height="0dip"
android:layout_weight="1"/>
diff --git a/core/res/res/raw-ar/loaderror.html b/core/res/res/raw-ar/loaderror.html
index edcd63a..aa9805c 100644
--- a/core/res/res/raw-ar/loaderror.html
+++ b/core/res/res/raw-ar/loaderror.html
@@ -2,7 +2,7 @@
<head>
<title>صفحة الويب غير متوفرة</title>
<style type="text/css">
- body { margin-top: 0px; padding-top: 0px; }
+ body { margin-top: 0px; padding-top: 0px; direction: rtl; }
h2 { margin-top: 5px; padding-top: 0px; }
</style>
diff --git a/core/res/res/raw-ar/nodomain.html b/core/res/res/raw-ar/nodomain.html
index bc070f6..2e5849f 100644
--- a/core/res/res/raw-ar/nodomain.html
+++ b/core/res/res/raw-ar/nodomain.html
@@ -2,7 +2,7 @@
<head>
<title>صفحة الويب غير متوفرة</title>
<style type="text/css">
- body { margin-top: 0px; padding-top: 0px; }
+ body { margin-top: 0px; padding-top: 0px; direction: rtl; }
h2 { margin-top: 5px; padding-top: 0px; }
</style>
diff --git a/core/res/res/raw-iw/loaderror.html b/core/res/res/raw-iw/loaderror.html
index 8155432..8d5a53f 100644
--- a/core/res/res/raw-iw/loaderror.html
+++ b/core/res/res/raw-iw/loaderror.html
@@ -2,7 +2,7 @@
<head>
<title>דף אינטרנט לא זמין</title>
<style type="text/css">
- body { margin-top: 0px; padding-top: 0px; }
+ body { margin-top: 0px; padding-top: 0px; direction: rtl; }
h2 { margin-top: 5px; padding-top: 0px; }
</style>
diff --git a/core/res/res/raw-iw/nodomain.html b/core/res/res/raw-iw/nodomain.html
index f7b9f42..0dcd7f8 100644
--- a/core/res/res/raw-iw/nodomain.html
+++ b/core/res/res/raw-iw/nodomain.html
@@ -2,7 +2,7 @@
<head>
<title>דף אינטרנט לא זמין</title>
<style type="text/css">
- body { margin-top: 0px; padding-top: 0px; }
+ body { margin-top: 0px; padding-top: 0px; direction: rtl; }
h2 { margin-top: 5px; padding-top: 0px; }
</style>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5a9551e..cbb5852 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -273,6 +273,21 @@
<!-- Default LED off time for notification LED in milliseconds. -->
<integer name="config_defaultNotificationLedOff">2000</integer>
+ <!-- Default value for led color when battery is low on charge -->
+ <integer name="config_notificationsBatteryLowARGB">0xFFFF0000</integer>
+
+ <!-- Default value for led color when battery is medium charged -->
+ <integer name="config_notificationsBatteryMediumARGB">0xFFFFFF00</integer>
+
+ <!-- Default value for led color when battery is fully charged -->
+ <integer name="config_notificationsBatteryFullARGB">0xFF00FF00</integer>
+
+ <!-- Default value for LED on time when the battery is low on charge in miliseconds -->
+ <integer name="config_notificationsBatteryLedOn">125</integer>
+
+ <!-- Default value for LED off time when the battery is low on charge in miliseconds -->
+ <integer name="config_notificationsBatteryLedOff">2875</integer>
+
<!-- Allow the menu hard key to be disabled in LockScreen on some devices -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
@@ -374,6 +389,13 @@
<!-- The restoring is handled by modem if it is true-->
<bool translatable="false" name="skip_restoring_network_selection">false</bool>
+ <!-- The URL that should be sent in an x-wap-profile header with an HTTP request,
+ as defined in the Open Mobile Alliance User Agent Profile specification
+ OMA-TS-UAProf-V2_0-20060206-A Section 8.1.1.1. If the URL contains a '%s'
+ format string then that substring will be replaced with the value of
+ Build.MODEL. The format string shall not be escaped. -->
+ <string name="config_useragentprofile_url"></string>
+
<!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
<string-array name="config_twoDigitNumberPattern">
</string-array>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 613e611..ae3cd02 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2362,4 +2362,19 @@
<!-- Text for message for an unknown external media state [CHAR LIMIT=NONE] -->
<string name="media_unknown_state">External media in unknown state.</string>
+ <!-- Network positioning notification ticker. The name of the user (e.g. John Doe) who sent
+ the request is shown as a dynamic string. -->
+ <string name="gpsNotifTicker">Location request from <xliff:g id="name">%s</xliff:g></string>
+ <!-- Network positioning notification and verification title to inform the user about
+ an incoming location request. -->
+ <string name="gpsNotifTitle">Location request</string>
+ <!-- Network positioning notification message. The name of the user (e.g. John Doe) and
+ service (SUPL-service) who sent the request is shown as dynamic strings.
+ Translation should not be longer than master text. -->
+ <string name="gpsNotifMessage">Requested by <xliff:g id="name">%1$s</xliff:g> (<xliff:g id="service" example="SUPL-service">%2$s</xliff:g>)</string>
+ <!-- Network positioning verification Yes. Button to push to share location information. -->
+ <string name="gpsVerifYes">Yes</string>
+ <!-- Network positioning verification No. Button to push to deny sharing of location
+ information. -->
+ <string name="gpsVerifNo">No</string>
</resources>
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
new file mode 100644
index 0000000..2b6dee8b
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content;
+
+import android.content.ContentResolver;
+import android.provider.ContactsContract;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+public class ContentResolverTest extends AndroidTestCase {
+ private ContentResolver mContentResolver;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContentResolver = mContext.getContentResolver();
+ }
+
+ @LargeTest
+ public void testCursorFinalizer() throws Exception {
+ // TODO: Want a test case that more predictably reproduce this issue. Selected
+ // 600 as this causes the problem 100% of the runs on current hw, it might not
+ // do so on some other configuration though.
+ for (int i = 0; i < 600; i++) {
+ mContentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/preference/ListPreferenceTest.java b/core/tests/coretests/src/android/preference/ListPreferenceTest.java
new file mode 100644
index 0000000..41f8e03
--- /dev/null
+++ b/core/tests/coretests/src/android/preference/ListPreferenceTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.preference;
+
+import android.preference.ListPreference;
+import android.test.AndroidTestCase;
+
+public class ListPreferenceTest extends AndroidTestCase {
+ public void testListPreferenceSummaryFromEntries() {
+ String[] entries = { "one", "two", "three" };
+ String[] entryValues = { "1" , "2", "3" };
+ ListPreference lp = new ListPreference(getContext());
+ lp.setEntries(entries);
+ lp.setEntryValues(entryValues);
+
+ lp.setValue(entryValues[1]);
+ assertTrue(lp.getSummary() == null);
+
+ lp.setSummary("%1$s");
+ assertEquals(entries[1], lp.getSummary());
+
+ lp.setValue(entryValues[2]);
+ assertEquals(entries[2], lp.getSummary());
+
+ lp.setSummary(null);
+ assertTrue(lp.getSummary() == null);
+
+ lp.setSummary("The color is %1$s");
+ assertEquals("The color is " + entries[2], lp.getSummary());
+ }
+}
diff --git a/core/tests/hosttests/Android.mk b/core/tests/hosttests/Android.mk
index 0001201..07d99cb 100644
--- a/core/tests/hosttests/Android.mk
+++ b/core/tests/hosttests/Android.mk
@@ -23,7 +23,7 @@
LOCAL_MODULE := FrameworkCoreHostTests
-LOCAL_JAVA_LIBRARIES := hosttestlib ddmlib junit
+LOCAL_JAVA_LIBRARIES := hosttestlib ddmlib-prebuilt junit
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java
index fed39c9..ebe13e1 100644
--- a/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java
+++ b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java
@@ -16,24 +16,24 @@
package android.content.pm;
+import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.IShellOutputReceiver;
+import com.android.ddmlib.InstallException;
import com.android.ddmlib.Log;
import com.android.ddmlib.MultiLineReceiver;
-import com.android.ddmlib.SyncService;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.SyncException;
+import com.android.ddmlib.TimeoutException;
import com.android.ddmlib.SyncService.ISyncProgressMonitor;
-import com.android.ddmlib.SyncService.SyncResult;
import com.android.ddmlib.testrunner.ITestRunListener;
import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
import com.android.ddmlib.testrunner.TestIdentifier;
-import com.android.hosttest.DeviceTestCase;
-import com.android.hosttest.DeviceTestSuite;
import java.io.BufferedReader;
-import java.io.File;
-import java.io.InputStreamReader;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.StringReader;
import java.lang.Runtime;
import java.lang.Process;
@@ -44,7 +44,6 @@
import java.util.regex.Pattern;
import junit.framework.Assert;
-import com.android.hosttest.DeviceTestCase;
/**
* Set of tests that verify host side install cases
@@ -128,11 +127,16 @@
* @param methodName (optional) The method in the class of which to test
* @param runnerName (optional) The name of the TestRunner of the test on the device to be run
* @param params (optional) Any additional parameters to pass into the Test Runner
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
* @return the {@link CollectingTestRunListener}
*/
- private CollectingTestRunListener doRunTests(String pkgName, String className, String
- methodName, String runnerName, Map<String, String> params) throws IOException {
-
+ private CollectingTestRunListener doRunTests(String pkgName, String className,
+ String methodName, String runnerName, Map<String, String> params) throws IOException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(pkgName, runnerName,
mDevice);
@@ -163,7 +167,8 @@
* @return true if test passed, false otherwise.
*/
public boolean runDeviceTestsDidAllTestsPass(String pkgName, String className,
- String methodName, String runnerName, Map<String, String> params) throws IOException {
+ String methodName, String runnerName, Map<String, String> params) throws IOException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
CollectingTestRunListener listener = doRunTests(pkgName, className, methodName,
runnerName, params);
return listener.didAllTestsPass();
@@ -173,9 +178,15 @@
* Runs the specified packages tests, and returns whether all tests passed or not.
*
* @param pkgName Android application package for tests
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
* @return true if every test passed, false otherwise.
*/
- public boolean runDeviceTestsDidAllTestsPass(String pkgName) throws IOException {
+ public boolean runDeviceTestsDidAllTestsPass(String pkgName) throws IOException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
CollectingTestRunListener listener = doRunTests(pkgName, null, null, null, null);
return listener.didAllTestsPass();
}
@@ -183,22 +194,26 @@
/**
* Helper method to push a file to device
* @param apkAppPrivatePath
- * @throws IOException
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws IOException if connection to device was lost.
+ * @throws SyncException if the sync failed for another reason.
*/
public void pushFile(final String localFilePath, final String destFilePath)
- throws IOException {
- SyncResult result = mDevice.getSyncService().pushFile(
- localFilePath, destFilePath, new NullSyncProgressMonitor());
- assertEquals(SyncService.RESULT_OK, result.getCode());
+ throws IOException, SyncException, TimeoutException, AdbCommandRejectedException {
+ mDevice.getSyncService().pushFile(localFilePath,
+ destFilePath, new NullSyncProgressMonitor());
}
/**
* Helper method to install a file
* @param localFilePath the absolute file system path to file on local host to install
* @param reinstall set to <code>true</code> if re-install of app should be performed
- * @throws IOException
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed
*/
- public void installFile(final String localFilePath, final boolean replace) throws IOException {
+ public void installFile(final String localFilePath, final boolean replace) throws IOException,
+ InstallException {
String result = mDevice.installPackage(localFilePath, replace);
assertEquals(null, result);
}
@@ -208,10 +223,11 @@
* @param localFilePath the absolute file system path to file on local host to install
* @param reinstall set to <code>true</code> if re-install of app should be performed
* @return the string output of the failed install attempt
- * @throws IOException
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed
*/
public String installFileFail(final String localFilePath, final boolean replace)
- throws IOException {
+ throws IOException, InstallException {
String result = mDevice.installPackage(localFilePath, replace);
assertNotNull(result);
return result;
@@ -221,10 +237,17 @@
* Helper method to install a file to device as forward locked
* @param localFilePath the absolute file system path to file on local host to install
* @param reinstall set to <code>true</code> if re-install of app should be performed
- * @throws IOException
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws SyncException if the sync failed for another reason.
+ * @throws InstallException if the install failed.
*/
public String installFileForwardLocked(final String localFilePath, final boolean replace)
- throws IOException {
+ throws IOException, SyncException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException, InstallException {
String remoteFilePath = mDevice.syncPackageToDevice(localFilePath);
InstallReceiver receiver = new InstallReceiver();
String cmd = String.format(replace ? "pm install -r -l \"%1$s\"" :
@@ -239,9 +262,14 @@
*
* @param destPath the absolute path of file on device to check
* @return <code>true</code> if file exists, <code>false</code> otherwise.
- * @throws IOException if adb shell command failed
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
*/
- public boolean doesRemoteFileExist(String destPath) throws IOException {
+ public boolean doesRemoteFileExist(String destPath) throws IOException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
String lsGrep = executeShellCommand(String.format("ls %s", destPath));
return !lsGrep.contains("No such file or directory");
}
@@ -252,10 +280,15 @@
* @param destPath the absolute path of the file
* @return <code>true</code> if file exists containing given string,
* <code>false</code> otherwise.
- * @throws IOException if adb shell command failed
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
*/
public boolean doesRemoteFileExistContainingString(String destPath, String searchString)
- throws IOException {
+ throws IOException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
String lsResult = executeShellCommand(String.format("ls %s", destPath));
return lsResult.contains(searchString);
}
@@ -265,9 +298,14 @@
*
* @param packageName the Android manifest package to check.
* @return <code>true</code> if package exists, <code>false</code> otherwise
- * @throws IOException if adb shell command failed
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
*/
- public boolean doesPackageExist(String packageName) throws IOException {
+ public boolean doesPackageExist(String packageName) throws IOException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
String pkgGrep = executeShellCommand(String.format("pm path %s", packageName));
return pkgGrep.contains("package:");
}
@@ -277,9 +315,14 @@
*
* @param packageName package name to check for
* @return <code>true</code> if file exists, <code>false</code> otherwise.
- * @throws IOException if adb shell command failed
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
*/
- public boolean doesAppExistOnDevice(String packageName) throws IOException {
+ public boolean doesAppExistOnDevice(String packageName) throws IOException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
return doesRemoteFileExistContainingString(DEVICE_APP_PATH, packageName);
}
@@ -288,9 +331,14 @@
*
* @param packageName package name to check for
* @return <code>true</code> if file exists, <code>false</code> otherwise.
- * @throws IOException if adb shell command failed
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
*/
- public boolean doesAppExistOnSDCard(String packageName) throws IOException {
+ public boolean doesAppExistOnSDCard(String packageName) throws IOException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
return doesRemoteFileExistContainingString(SDCARD_APP_PATH, packageName);
}
@@ -299,9 +347,14 @@
*
* @param packageName package name to check for
* @return <code>true</code> if file exists, <code>false</code> otherwise.
- * @throws IOException if adb shell command failed
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
*/
- public boolean doesAppExistAsForwardLocked(String packageName) throws IOException {
+ public boolean doesAppExistAsForwardLocked(String packageName) throws IOException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
return doesRemoteFileExistContainingString(APP_PRIVATE_PATH, packageName);
}
@@ -309,9 +362,14 @@
* Waits for device's package manager to respond.
*
* @throws InterruptedException
- * @throws IOException
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
*/
- public void waitForPackageManager() throws InterruptedException, IOException {
+ public void waitForPackageManager() throws InterruptedException, IOException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "waiting for device");
int currentWaitTime = 0;
// poll the package manager until it returns something for android
@@ -377,9 +435,14 @@
*
* @param packageName The name of the package to wait to load
* @throws InterruptedException
- * @throws IOException
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
*/
- public void waitForApp(String packageName) throws InterruptedException, IOException {
+ public void waitForApp(String packageName) throws InterruptedException, IOException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "waiting for app to launch");
int currentWaitTime = 0;
// poll the package manager until it returns something for the package we're looking for
@@ -396,9 +459,14 @@
/**
* Helper method which executes a adb shell command and returns output as a {@link String}
* @return the output of the command
- * @throws IOException
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
*/
- public String executeShellCommand(String command) throws IOException {
+ public String executeShellCommand(String command) throws IOException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, String.format("adb shell %s", command));
CollectingOutputReceiver receiver = new CollectingOutputReceiver();
mDevice.executeShellCommand(command, receiver);
@@ -410,9 +478,14 @@
/**
* Helper method ensures we are in root mode on the host side. It returns only after
* PackageManager is actually up and running.
- * @throws IOException
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
*/
- public void runAdbRoot() throws IOException, InterruptedException {
+ public void runAdbRoot() throws IOException, InterruptedException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "adb root");
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec("adb root"); // adb should be in the path
@@ -430,10 +503,15 @@
/**
* Helper method which reboots the device and returns once the device is online again
* and package manager is up and running (note this function is synchronous to callers).
- * @throws IOException
* @throws InterruptedException
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
*/
- public void rebootDevice() throws IOException, InterruptedException {
+ public void rebootDevice() throws IOException, InterruptedException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
String command = "reboot"; // no need for -s since mDevice is already tied to a device
Log.i(LOG_TAG, command);
CollectingOutputReceiver receiver = new CollectingOutputReceiver();
@@ -498,7 +576,7 @@
private boolean mAllTestsPassed = true;
private String mTestRunErrorMessage = null;
- public void testEnded(TestIdentifier test) {
+ public void testEnded(TestIdentifier test, Map<String, String> metrics) {
// ignore
}
@@ -509,7 +587,7 @@
mAllTestsPassed = false;
}
- public void testRunEnded(long elapsedTime) {
+ public void testRunEnded(long elapsedTime, Map<String, String> resultBundle) {
// ignore
}
@@ -519,7 +597,7 @@
mTestRunErrorMessage = errorMessage;
}
- public void testRunStarted(int testCount) {
+ public void testRunStarted(String runName, int testCount) {
// ignore
}
@@ -586,17 +664,23 @@
/**
* Helper method for installing an app to wherever is specified in its manifest, and
* then verifying the app was installed onto SD Card.
+ * <p/>
+ * Assumes adb is running as root in device under test.
*
* @param the path of the apk to install
* @param the name of the package
* @param <code>true</code> if the app should be overwritten, <code>false</code> otherwise
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
- * <p/>
- * Assumes adb is running as root in device under test.
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
public void installAppAndVerifyExistsOnSDCard(String apkPath, String pkgName, boolean overwrite)
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, InstallException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
// Start with a clean slate if we're not overwriting
if (!overwrite) {
// cleanup test app just in case it already exists
@@ -617,17 +701,23 @@
/**
* Helper method for installing an app to wherever is specified in its manifest, and
* then verifying the app was installed onto device.
+ * <p/>
+ * Assumes adb is running as root in device under test.
*
* @param the path of the apk to install
* @param the name of the package
* @param <code>true</code> if the app should be overwritten, <code>false</code> otherwise
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
- * <p/>
- * Assumes adb is running as root in device under test.
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
public void installAppAndVerifyExistsOnDevice(String apkPath, String pkgName, boolean overwrite)
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, InstallException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
// Start with a clean slate if we're not overwriting
if (!overwrite) {
// cleanup test app just in case it already exists
@@ -648,17 +738,24 @@
/**
* Helper method for installing an app as forward-locked, and
* then verifying the app was installed in the proper forward-locked location.
+ * <p/>
+ * Assumes adb is running as root in device under test.
*
* @param the path of the apk to install
* @param the name of the package
* @param <code>true</code> if the app should be overwritten, <code>false</code> otherwise
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
- * <p/>
- * Assumes adb is running as root in device under test.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
*/
public void installFwdLockedAppAndVerifyExists(String apkPath,
- String pkgName, boolean overwrite) throws IOException, InterruptedException {
+ String pkgName, boolean overwrite) throws IOException, InterruptedException,
+ InstallException, SyncException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
// Start with a clean slate if we're not overwriting
if (!overwrite) {
// cleanup test app just in case it already exists
@@ -679,14 +776,21 @@
/**
* Helper method for uninstalling an app.
- *
- * @param pkgName package name to uninstall
- * @throws IOException if adb shell command failed
- * @throws InterruptedException if the thread was interrupted
* <p/>
* Assumes adb is running as root in device under test.
+ *
+ * @param pkgName package name to uninstall
+ * @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the uninstall failed.
*/
- public void uninstallApp(String pkgName) throws IOException, InterruptedException {
+ public void uninstallApp(String pkgName) throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
mDevice.uninstallPackage(pkgName);
// make sure its not installed anymore
assertFalse(doesPackageExist(pkgName));
@@ -696,12 +800,18 @@
* Helper method for clearing any installed non-system apps.
* Useful ensuring no non-system apps are installed, and for cleaning up stale files that
* may be lingering on the system for whatever reason.
- *
- * @throws IOException if adb shell command failed
* <p/>
* Assumes adb is running as root in device under test.
+ *
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the uninstall failed.
*/
- public void wipeNonSystemApps() throws IOException {
+ public void wipeNonSystemApps() throws IOException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException, InstallException {
String allInstalledPackages = executeShellCommand("pm list packages -f");
BufferedReader outputReader = new BufferedReader(new StringReader(allInstalledPackages));
@@ -726,8 +836,14 @@
*
* <p/>
* Assumes adb is running as root in device under test.
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
*/
- public void setDevicePreferredInstallLocation(InstallLocPreference pref) throws IOException {
+ public void setDevicePreferredInstallLocation(InstallLocPreference pref) throws IOException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
String command = "pm setInstallLocation %d";
int locValue = 0;
switch (pref) {
@@ -749,8 +865,14 @@
*
* <p/>
* Assumes adb is running as root in device under test.
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
*/
- public InstallLocPreference getDevicePreferredInstallLocation() throws IOException {
+ public InstallLocPreference getDevicePreferredInstallLocation() throws IOException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
String result = executeShellCommand("pm getInstallLocation");
if (result.indexOf('0') != -1) {
return InstallLocPreference.AUTO;
diff --git a/core/tests/hosttests/src/android/content/pm/PackageManagerHostTests.java b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTests.java
index 1b797d5..22a2be6 100644
--- a/core/tests/hosttests/src/android/content/pm/PackageManagerHostTests.java
+++ b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTests.java
@@ -16,13 +16,12 @@
package android.content.pm;
-import com.android.ddmlib.IDevice;
-import com.android.ddmlib.IShellOutputReceiver;
+import com.android.ddmlib.AdbCommandRejectedException;
+import com.android.ddmlib.InstallException;
import com.android.ddmlib.Log;
-import com.android.ddmlib.MultiLineReceiver;
-import com.android.ddmlib.SyncService;
-import com.android.ddmlib.SyncService.ISyncProgressMonitor;
-import com.android.ddmlib.SyncService.SyncResult;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.SyncException;
+import com.android.ddmlib.TimeoutException;
import com.android.hosttest.DeviceTestCase;
import com.android.hosttest.DeviceTestSuite;
@@ -156,10 +155,18 @@
* the app, and otherwise cause the system to blow up.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws SyncException if the sync failed for another reason.
+ * @throws InstallException if the install failed.
*/
- public void testPushAppPrivate() throws IOException, InterruptedException {
+ public void testPushAppPrivate() throws IOException, InterruptedException, InstallException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ SyncException {
Log.i(LOG_TAG, "testing pushing an apk to /data/app-private");
final String apkAppPrivatePath = appPrivatePath + SIMPLE_APK;
@@ -187,12 +194,18 @@
* @param apkName the file name of the test app apk
* @param pkgName the package name of the test app apk
* @param expectedLocation the file name of the test app apk
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
private void doStandardInstall(String apkName, String pkgName,
PackageManagerHostTestUtils.InstallLocation expectedLocation)
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, InstallException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
if (expectedLocation == PackageManagerHostTestUtils.InstallLocation.DEVICE) {
mPMHostUtils.installAppAndVerifyExistsOnDevice(
@@ -211,12 +224,18 @@
* Assumes adb is running as root in device under test.
* @param preference the device's preferred location of where to install apps
* @param expectedLocation the expected location of where the apk was installed
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
public void installAppAutoLoc(PackageManagerHostTestUtils.InstallLocPreference preference,
PackageManagerHostTestUtils.InstallLocation expectedLocation)
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException, InstallException {
PackageManagerHostTestUtils.InstallLocPreference savedPref =
PackageManagerHostTestUtils.InstallLocPreference.AUTO;
@@ -239,10 +258,16 @@
* will install the app to the device when device's preference is auto.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testInstallAppAutoLocPrefIsAuto() throws IOException, InterruptedException {
+ public void testInstallAppAutoLocPrefIsAuto() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test installLocation=auto, prefer=auto gets installed on device");
installAppAutoLoc(PackageManagerHostTestUtils.InstallLocPreference.AUTO,
PackageManagerHostTestUtils.InstallLocation.DEVICE);
@@ -253,10 +278,17 @@
* will install the app to the device when device's preference is internal.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testInstallAppAutoLocPrefIsInternal() throws IOException, InterruptedException {
+ public void testInstallAppAutoLocPrefIsInternal() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test installLocation=auto, prefer=internal gets installed on device");
installAppAutoLoc(PackageManagerHostTestUtils.InstallLocPreference.INTERNAL,
PackageManagerHostTestUtils.InstallLocation.DEVICE);
@@ -267,10 +299,17 @@
* will install the app to the SD card when device's preference is external.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testInstallAppAutoLocPrefIsExternal() throws IOException, InterruptedException {
+ public void testInstallAppAutoLocPrefIsExternal() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test installLocation=auto, prefer=external gets installed on device");
installAppAutoLoc(PackageManagerHostTestUtils.InstallLocPreference.EXTERNAL,
PackageManagerHostTestUtils.InstallLocation.DEVICE);
@@ -283,12 +322,18 @@
* Assumes adb is running as root in device under test.
* @param preference the device's preferred location of where to install apps
* @param expectedLocation the expected location of where the apk was installed
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the (un)install failed.
*/
public void installAppInternalLoc(PackageManagerHostTestUtils.InstallLocPreference preference,
PackageManagerHostTestUtils.InstallLocation expectedLocation)
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException, InstallException {
PackageManagerHostTestUtils.InstallLocPreference savedPref =
PackageManagerHostTestUtils.InstallLocPreference.AUTO;
@@ -311,10 +356,17 @@
* will install the app to the device when device's preference is auto.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testInstallAppInternalLocPrefIsAuto() throws IOException, InterruptedException {
+ public void testInstallAppInternalLocPrefIsAuto() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test installLocation=internal, prefer=auto gets installed on device");
installAppInternalLoc(PackageManagerHostTestUtils.InstallLocPreference.AUTO,
PackageManagerHostTestUtils.InstallLocation.DEVICE);
@@ -325,10 +377,17 @@
* will install the app to the device when device's preference is internal.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testInstallAppInternalLocPrefIsInternal() throws IOException, InterruptedException {
+ public void testInstallAppInternalLocPrefIsInternal() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test installLocation=internal, prefer=internal is installed on device");
installAppInternalLoc(PackageManagerHostTestUtils.InstallLocPreference.INTERNAL,
PackageManagerHostTestUtils.InstallLocation.DEVICE);
@@ -339,10 +398,17 @@
* will install the app to the device when device's preference is external.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testInstallAppInternalLocPrefIsExternal() throws IOException, InterruptedException {
+ public void testInstallAppInternalLocPrefIsExternal() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test installLocation=internal, prefer=external is installed on device");
installAppInternalLoc(PackageManagerHostTestUtils.InstallLocPreference.EXTERNAL,
PackageManagerHostTestUtils.InstallLocation.DEVICE);
@@ -355,12 +421,18 @@
* Assumes adb is running as root in device under test.
* @param preference the device's preferred location of where to install apps
* @param expectedLocation the expected location of where the apk was installed
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
public void installAppExternalLoc(PackageManagerHostTestUtils.InstallLocPreference preference,
PackageManagerHostTestUtils.InstallLocation expectedLocation)
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException, InstallException {
PackageManagerHostTestUtils.InstallLocPreference savedPref =
PackageManagerHostTestUtils.InstallLocPreference.AUTO;
@@ -384,10 +456,17 @@
* will install the app to the device when device's preference is auto.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testInstallAppExternalLocPrefIsAuto() throws IOException, InterruptedException {
+ public void testInstallAppExternalLocPrefIsAuto() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test installLocation=external, pref=auto gets installed on SD Card");
installAppExternalLoc(PackageManagerHostTestUtils.InstallLocPreference.AUTO,
PackageManagerHostTestUtils.InstallLocation.SDCARD);
@@ -398,10 +477,17 @@
* will install the app to the device when device's preference is internal.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testInstallAppExternalLocPrefIsInternal() throws IOException, InterruptedException {
+ public void testInstallAppExternalLocPrefIsInternal() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test installLocation=external, pref=internal gets installed on SD Card");
installAppExternalLoc(PackageManagerHostTestUtils.InstallLocPreference.INTERNAL,
PackageManagerHostTestUtils.InstallLocation.SDCARD);
@@ -412,10 +498,17 @@
* will install the app to the device when device's preference is external.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testInstallAppExternalLocPrefIsExternal() throws IOException, InterruptedException {
+ public void testInstallAppExternalLocPrefIsExternal() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test installLocation=external, pref=external gets installed on SD Card");
installAppExternalLoc(PackageManagerHostTestUtils.InstallLocPreference.EXTERNAL,
PackageManagerHostTestUtils.InstallLocation.SDCARD);
@@ -427,10 +520,17 @@
* system decide.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testInstallAppNoLocPrefIsAuto() throws IOException, InterruptedException {
+ public void testInstallAppNoLocPrefIsAuto() throws IOException, InterruptedException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ InstallException {
Log.i(LOG_TAG, "Test an app with no installLocation gets installed on device");
PackageManagerHostTestUtils.InstallLocPreference savedPref =
@@ -456,10 +556,17 @@
* external.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testInstallAppNoLocPrefIsExternal() throws IOException, InterruptedException {
+ public void testInstallAppNoLocPrefIsExternal() throws IOException, InterruptedException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ InstallException {
Log.i(LOG_TAG, "Test an app with no installLocation gets installed on SD card");
PackageManagerHostTestUtils.InstallLocPreference savedPref =
@@ -485,10 +592,17 @@
* internal.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testInstallAppNoLocPrefIsInternal() throws IOException, InterruptedException {
+ public void testInstallAppNoLocPrefIsInternal() throws IOException, InterruptedException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ InstallException {
Log.i(LOG_TAG, "Test an app with no installLocation gets installed on device");
PackageManagerHostTestUtils.InstallLocPreference savedPref =
@@ -513,10 +627,18 @@
* forward-locked will get installed to the correct location.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws SyncException if the sync failed for another reason.
+ * @throws InstallException if the install failed.
*/
- public void testInstallFwdLockedAppInternal() throws IOException, InterruptedException {
+ public void testInstallFwdLockedAppInternal() throws IOException, InterruptedException,
+ InstallException, SyncException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test an app with installLoc set to Internal gets installed to app-private");
try {
@@ -534,10 +656,18 @@
* forward-locked will get installed to the correct location.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws SyncException if the sync failed for another reason.
+ * @throws InstallException if the install failed.
*/
- public void testInstallFwdLockedAppExternal() throws IOException, InterruptedException {
+ public void testInstallFwdLockedAppExternal() throws IOException, InterruptedException,
+ InstallException, SyncException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test an app with installLoc set to Internal gets installed to app-private");
try {
@@ -555,10 +685,18 @@
* forward-locked will get installed to the correct location.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws SyncException if the sync failed for another reason.
+ * @throws InstallException if the install failed.
*/
- public void testInstallFwdLockedAppAuto() throws IOException, InterruptedException {
+ public void testInstallFwdLockedAppAuto() throws IOException, InterruptedException,
+ InstallException, SyncException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test an app with installLoc set to Auto gets installed to app-private");
try {
@@ -576,10 +714,18 @@
* forward-locked installed will get installed to the correct location.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws SyncException if the sync failed for another reason.
+ * @throws InstallException if the install failed.
*/
- public void testInstallFwdLockedAppNone() throws IOException, InterruptedException {
+ public void testInstallFwdLockedAppNone() throws IOException, InterruptedException,
+ InstallException, SyncException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test an app with no installLoc set gets installed to app-private");
try {
@@ -597,14 +743,21 @@
* uninstall it, and reinstall it onto the SD card.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
// TODO: This currently relies on the app's manifest to switch from device to
// SD card install locations. We might want to make Device's installPackage()
// accept a installLocation flag so we can install a package to the
// destination of our choosing.
- public void testReinstallInternalToExternal() throws IOException, InterruptedException {
+ public void testReinstallInternalToExternal() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test installing an app first to the device, then to the SD Card");
try {
@@ -625,14 +778,21 @@
* uninstall it, and reinstall it onto the device.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
// TODO: This currently relies on the app's manifest to switch from device to
// SD card install locations. We might want to make Device's installPackage()
// accept a installLocation flag so we can install a package to the
// destination of our choosing.
- public void testReinstallExternalToInternal() throws IOException, InterruptedException {
+ public void testReinstallExternalToInternal() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test installing an app first to the SD Care, then to the device");
try {
@@ -655,10 +815,16 @@
* the update onto the SD card as well when location is set to external for both versions
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testUpdateBothExternal() throws IOException, InterruptedException {
+ public void testUpdateBothExternal() throws IOException, InterruptedException, InstallException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test updating an app on the SD card stays on the SD card");
try {
@@ -681,10 +847,16 @@
* updated apps' manifest file.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testUpdateToSDCard() throws IOException, InterruptedException {
+ public void testUpdateToSDCard() throws IOException, InterruptedException, InstallException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test updating an app on the SD card stays on the SD card");
try {
@@ -706,10 +878,17 @@
* the update onto the device if the manifest has changed to installLocation=internalOnly
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
- public void testUpdateSDCardToDevice() throws IOException, InterruptedException {
+ public void testUpdateSDCardToDevice() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test updating an app on the SD card to the Device through manifest change");
try {
@@ -731,11 +910,18 @@
* the update onto the device's forward-locked location
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws SyncException if the sync failed for another reason.
+ * @throws InstallException if the install failed.
*/
public void testInstallAndUpdateExternalLocForwardLockedApp()
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, InstallException, SyncException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test updating a forward-locked app marked preferExternal");
try {
@@ -757,11 +943,18 @@
* the update onto the device's forward-locked location
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws SyncException if the sync failed for another reason.
+ * @throws InstallException if the install failed.
*/
public void testInstallAndUpdateNoLocForwardLockedApp()
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, InstallException, SyncException,
+ TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test updating a forward-locked app with no installLocation pref set");
try {
@@ -783,11 +976,18 @@
* and then launched without crashing.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws SyncException if the sync failed for another reason.
+ * @throws InstallException if the install failed.
*/
public void testInstallAndLaunchAllPermsAppOnSD()
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, InstallException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test launching an app with all perms set, installed on SD card");
try {
@@ -808,11 +1008,17 @@
* run without permissions errors.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
public void testInstallAndLaunchFLPermsAppOnSD()
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, InstallException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test launching an app with location perms set, installed on SD card");
try {
@@ -833,11 +1039,17 @@
* run without permissions errors.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
public void testInstallAndLaunchBTPermsAppOnSD()
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, InstallException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test launching an app with bluetooth perms set, installed on SD card");
try {
@@ -858,11 +1070,17 @@
* SecurityException when launched if its other shared apps are not installed.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
public void testInstallAndLaunchSharedPermsAppOnSD_NoPerms()
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, InstallException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test launching an app with no explicit perms set, installed on SD card");
try {
@@ -888,11 +1106,17 @@
* shared apps are installed.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
public void testInstallAndLaunchSharedPermsAppOnSD_GrantedPerms()
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, InstallException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test launching an app with no explicit perms set, installed on SD card");
try {
@@ -921,11 +1145,17 @@
* run without permissions errors even after a reboot
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
public void testInstallAndLaunchFLPermsAppOnSD_Reboot()
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, InstallException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test launching an app with location perms set, installed on SD card");
try {
@@ -951,11 +1181,17 @@
* shared apps are installed, even after a reboot.
* <p/>
* Assumes adb is running as root in device under test.
- * @throws IOException if adb shell command failed
* @throws InterruptedException if the thread was interrupted
+ * @throws TimeoutException in case of a timeout on the connection.
+ * @throws AdbCommandRejectedException if adb rejects the command
+ * @throws ShellCommandUnresponsiveException if the device did not output anything for
+ * a period longer than the max time to output.
+ * @throws IOException if connection to device was lost.
+ * @throws InstallException if the install failed.
*/
public void testInstallAndLaunchSharedPermsAppOnSD_Reboot()
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException, InstallException, TimeoutException,
+ AdbCommandRejectedException, ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test launching an app on SD, with no explicit perms set after reboot");
try {
diff --git a/core/tests/hosttests/src/android/content/pm/PackageManagerStressHostTests.java b/core/tests/hosttests/src/android/content/pm/PackageManagerStressHostTests.java
index 715c55b..a2a5dd3 100644
--- a/core/tests/hosttests/src/android/content/pm/PackageManagerStressHostTests.java
+++ b/core/tests/hosttests/src/android/content/pm/PackageManagerStressHostTests.java
@@ -16,8 +16,11 @@
package android.content.pm;
-import com.android.ddmlib.IDevice;
+import com.android.ddmlib.AdbCommandRejectedException;
+import com.android.ddmlib.InstallException;
import com.android.ddmlib.Log;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.TimeoutException;
import com.android.hosttest.DeviceTestCase;
import com.android.hosttest.DeviceTestSuite;
@@ -138,7 +141,9 @@
* <p/>
* Assumes adb is running as root in device under test.
*/
- public void testUpdateAppManyTimesOnSD() throws IOException, InterruptedException {
+ public void testUpdateAppManyTimesOnSD() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test updating an app on SD numerous times");
// cleanup test app just in case it already exists
@@ -173,7 +178,9 @@
* <p/>
* Assumes adb is running as root in device under test.
*/
- public void testUninstallReinstallAppOnSDManyTimes() throws IOException, InterruptedException {
+ public void testUninstallReinstallAppOnSDManyTimes() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test updating an app on the SD card stays on the SD card");
// cleanup test app just in case it was already exists
@@ -207,7 +214,9 @@
* <p/>
* Assumes adb is running as root in device under test.
*/
- public void testInstallManyLargeAppsOnSD() throws IOException, InterruptedException {
+ public void testInstallManyLargeAppsOnSD() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test installing 20 large apps onto the sd card");
try {
@@ -251,7 +260,9 @@
* <p/>
* Assumes adb is running as root in device under test.
*/
- public void testInstallManyAppsOnSD() throws IOException, InterruptedException {
+ public void testInstallManyAppsOnSD() throws IOException, InterruptedException,
+ InstallException, TimeoutException, AdbCommandRejectedException,
+ ShellCommandUnresponsiveException {
Log.i(LOG_TAG, "Test installing 500 small apps onto SD");
try {
diff --git a/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java b/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java
index df781bf..a94555c 100644
--- a/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java
+++ b/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java
@@ -17,20 +17,12 @@
package android.net;
import android.content.pm.PackageManagerHostTestUtils;
-import android.content.pm.PackageManagerHostTestUtils.CollectingTestRunListener;
-import com.android.ddmlib.IDevice;
-import com.android.ddmlib.IShellOutputReceiver;
import com.android.ddmlib.Log;
-import com.android.ddmlib.MultiLineReceiver;
-import com.android.ddmlib.SyncService;
-import com.android.ddmlib.SyncService.ISyncProgressMonitor;
-import com.android.ddmlib.SyncService.SyncResult;
import com.android.hosttest.DeviceTestCase;
import com.android.hosttest.DeviceTestSuite;
import java.io.File;
-import java.io.IOException;
import java.util.Hashtable;
import junit.framework.Test;
diff --git a/data/keyboards/AVRCP.kl b/data/keyboards/AVRCP.kl
new file mode 100644
index 0000000..aeb3c8a
--- /dev/null
+++ b/data/keyboards/AVRCP.kl
@@ -0,0 +1,7 @@
+key 200 MEDIA_PLAY_PAUSE WAKE
+key 201 MEDIA_PLAY_PAUSE WAKE
+key 166 MEDIA_STOP WAKE
+key 163 MEDIA_NEXT WAKE
+key 165 MEDIA_PREVIOUS WAKE
+key 168 MEDIA_REWIND WAKE
+key 208 MEDIA_FAST_FORWARD WAKE
diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk
new file mode 100644
index 0000000..81ac530
--- /dev/null
+++ b/data/keyboards/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := qwerty.kcm
+include $(BUILD_KEY_CHAR_MAP)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := qwerty2.kcm
+include $(BUILD_KEY_CHAR_MAP)
+
+file := $(TARGET_OUT_KEYLAYOUT)/qwerty.kl
+ALL_PREBUILT += $(file)
+$(file): $(LOCAL_PATH)/qwerty.kl | $(ACP)
+ $(transform-prebuilt-to-target)
+
+file := $(TARGET_OUT_KEYLAYOUT)/AVRCP.kl
+ALL_PREBUILT += $(file)
+$(file) : $(LOCAL_PATH)/AVRCP.kl | $(ACP)
+ $(transform-prebuilt-to-target)
diff --git a/data/keyboards/qwerty.kcm b/data/keyboards/qwerty.kcm
new file mode 100644
index 0000000..8056364
--- /dev/null
+++ b/data/keyboards/qwerty.kcm
@@ -0,0 +1,64 @@
+[type=QWERTY]
+
+# keycode display number base caps fn caps_fn
+
+A 'A' '2' 'a' 'A' '#' 0x00
+B 'B' '2' 'b' 'B' '<' 0x00
+C 'C' '2' 'c' 'C' '9' 0x00E7
+D 'D' '3' 'd' 'D' '5' 0x00
+E 'E' '3' 'e' 'E' '2' 0x0301
+F 'F' '3' 'f' 'F' '6' 0x00A5
+G 'G' '4' 'g' 'G' '-' '_'
+H 'H' '4' 'h' 'H' '[' '{'
+I 'I' '4' 'i' 'I' '$' 0x0302
+J 'J' '5' 'j' 'J' ']' '}'
+K 'K' '5' 'k' 'K' '"' '~'
+L 'L' '5' 'l' 'L' ''' '`'
+M 'M' '6' 'm' 'M' '!' 0x00
+N 'N' '6' 'n' 'N' '>' 0x0303
+O 'O' '6' 'o' 'O' '(' 0x00
+P 'P' '7' 'p' 'P' ')' 0x00
+Q 'Q' '7' 'q' 'Q' '*' 0x0300
+R 'R' '7' 'r' 'R' '3' 0x20AC
+S 'S' '7' 's' 'S' '4' 0x00DF
+T 'T' '8' 't' 'T' '+' 0x00A3
+U 'U' '8' 'u' 'U' '&' 0x0308
+V 'V' '8' 'v' 'V' '=' '^'
+W 'W' '9' 'w' 'W' '1' 0x00
+X 'X' '9' 'x' 'X' '8' 0xEF00
+Y 'Y' '9' 'y' 'Y' '%' 0x00A1
+Z 'Z' '9' 'z' 'Z' '7' 0x00
+
+# on pc keyboards
+COMMA ',' ',' ',' ';' ';' '|'
+PERIOD '.' '.' '.' ':' ':' 0x2026
+AT '@' '0' '@' '0' '0' 0x2022
+SLASH '/' '/' '/' '?' '?' '\'
+
+SPACE 0x20 0x20 0x20 0x20 0xEF01 0xEF01
+ENTER 0xa 0xa 0xa 0xa 0xa 0xa
+
+TAB 0x9 0x9 0x9 0x9 0x9 0x9
+0 '0' '0' '0' ')' ')' ')'
+1 '1' '1' '1' '!' '!' '!'
+2 '2' '2' '2' '@' '@' '@'
+3 '3' '3' '3' '#' '#' '#'
+4 '4' '4' '4' '$' '$' '$'
+5 '5' '5' '5' '%' '%' '%'
+6 '6' '6' '6' '^' '^' '^'
+7 '7' '7' '7' '&' '&' '&'
+8 '8' '8' '8' '*' '*' '*'
+9 '9' '9' '9' '(' '(' '('
+
+GRAVE '`' '`' '`' '~' '`' '~'
+MINUS '-' '-' '-' '_' '-' '_'
+EQUALS '=' '=' '=' '+' '=' '+'
+LEFT_BRACKET '[' '[' '[' '{' '[' '{'
+RIGHT_BRACKET ']' ']' ']' '}' ']' '}'
+BACKSLASH '\' '\' '\' '|' '\' '|'
+SEMICOLON ';' ';' ';' ':' ';' ':'
+APOSTROPHE ''' ''' ''' '"' ''' '"'
+STAR '*' '*' '*' '*' '*' '*'
+POUND '#' '#' '#' '#' '#' '#'
+PLUS '+' '+' '+' '+' '+' '+'
+
diff --git a/data/keyboards/qwerty.kl b/data/keyboards/qwerty.kl
new file mode 100644
index 0000000..201c798
--- /dev/null
+++ b/data/keyboards/qwerty.kl
@@ -0,0 +1,91 @@
+key 399 GRAVE
+key 2 1
+key 3 2
+key 4 3
+key 5 4
+key 6 5
+key 7 6
+key 8 7
+key 9 8
+key 10 9
+key 11 0
+key 158 BACK WAKE_DROPPED
+key 230 SOFT_RIGHT WAKE
+key 60 SOFT_RIGHT WAKE
+key 107 ENDCALL WAKE_DROPPED
+key 62 ENDCALL WAKE_DROPPED
+key 229 MENU WAKE_DROPPED
+key 139 MENU WAKE_DROPPED
+key 59 MENU WAKE_DROPPED
+key 127 SEARCH WAKE_DROPPED
+key 217 SEARCH WAKE_DROPPED
+key 228 POUND
+key 227 STAR
+key 231 CALL WAKE_DROPPED
+key 61 CALL WAKE_DROPPED
+key 232 DPAD_CENTER WAKE_DROPPED
+key 108 DPAD_DOWN WAKE_DROPPED
+key 103 DPAD_UP WAKE_DROPPED
+key 102 HOME WAKE
+key 105 DPAD_LEFT WAKE_DROPPED
+key 106 DPAD_RIGHT WAKE_DROPPED
+key 115 VOLUME_UP WAKE
+key 114 VOLUME_DOWN WAKE
+key 116 POWER WAKE
+key 212 CAMERA
+
+key 16 Q
+key 17 W
+key 18 E
+key 19 R
+key 20 T
+key 21 Y
+key 22 U
+key 23 I
+key 24 O
+key 25 P
+key 26 LEFT_BRACKET
+key 27 RIGHT_BRACKET
+key 43 BACKSLASH
+
+key 30 A
+key 31 S
+key 32 D
+key 33 F
+key 34 G
+key 35 H
+key 36 J
+key 37 K
+key 38 L
+key 39 SEMICOLON
+key 40 APOSTROPHE
+key 14 DEL
+
+key 44 Z
+key 45 X
+key 46 C
+key 47 V
+key 48 B
+key 49 N
+key 50 M
+key 51 COMMA
+key 52 PERIOD
+key 53 SLASH
+key 28 ENTER
+
+key 56 ALT_LEFT
+key 100 ALT_RIGHT
+key 42 SHIFT_LEFT
+key 54 SHIFT_RIGHT
+key 15 TAB
+key 57 SPACE
+key 150 EXPLORER
+key 155 ENVELOPE
+
+key 12 MINUS
+key 13 EQUALS
+key 215 AT
+
+# On an AT keyboard: ESC, F10
+key 1 BACK WAKE_DROPPED
+key 68 MENU WAKE_DROPPED
diff --git a/data/keyboards/qwerty2.kcm b/data/keyboards/qwerty2.kcm
new file mode 100644
index 0000000..1487fb7
--- /dev/null
+++ b/data/keyboards/qwerty2.kcm
@@ -0,0 +1,81 @@
+[type=QWERTY]
+
+# this keymap is to be used in the emulator only. note
+# that I have liberally modified certain key strokes to
+# make it more usable in this context. the main differences
+# with the reference keyboard image are:
+#
+# - cap-2 produces '@', and not '"', without that, typing
+# email addresses becomes a major pain with a qwerty
+# keyboard. note that you can type '"' with fn-E anyway.
+#
+# - cap-COMMA and cap-PERIOD return '<' and '>', instead
+# of nothing.
+#
+#
+
+# keycode display number base caps fn caps_fn
+
+A 'A' '2' 'a' 'A' 'a' 'A'
+B 'B' '2' 'b' 'B' 'b' 'B'
+C 'C' '2' 'c' 'C' 0x00e7 0x00E7
+D 'D' '3' 'd' 'D' ''' '''
+E 'E' '3' 'e' 'E' '"' 0x0301
+F 'F' '3' 'f' 'F' '[' '['
+G 'G' '4' 'g' 'G' ']' ']'
+H 'H' '4' 'h' 'H' '<' '<'
+I 'I' '4' 'i' 'I' '-' 0x0302
+J 'J' '5' 'j' 'J' '>' '>'
+K 'K' '5' 'k' 'K' ';' '~'
+L 'L' '5' 'l' 'L' ':' '`'
+M 'M' '6' 'm' 'M' '%' 0x00
+N 'N' '6' 'n' 'N' 0x00 0x0303
+O 'O' '6' 'o' 'O' '+' '+'
+P 'P' '7' 'p' 'P' '=' 0x00A5
+Q 'Q' '7' 'q' 'Q' '|' 0x0300
+R 'R' '7' 'r' 'R' '`' 0x20AC
+S 'S' '7' 's' 'S' '\' 0x00DF
+T 'T' '8' 't' 'T' '{' 0x00A3
+U 'U' '8' 'u' 'U' '_' 0x0308
+V 'V' '8' 'v' 'V' 'v' 'V'
+W 'W' '9' 'w' 'W' '~' '~'
+X 'X' '9' 'x' 'X' 'x' 0xEF00
+Y 'Y' '9' 'y' 'Y' '}' 0x00A1
+Z 'Z' '9' 'z' 'Z' 'z' 'Z'
+
+COMMA ',' ',' ',' '<' ',' ','
+PERIOD '.' '.' '.' '>' '.' 0x2026
+AT '@' '@' '@' '@' '@' 0x2022
+SLASH '/' '/' '/' '?' '?' '?'
+
+SPACE 0x20 0x20 0x20 0x20 0xEF01 0xEF01
+ENTER 0xa 0xa 0xa 0xa 0xa 0xa
+
+0 '0' '0' '0' ')' ')' ')'
+1 '1' '1' '1' '!' '!' '!'
+2 '2' '2' '2' '@' '@' '@'
+3 '3' '3' '3' '#' '#' '#'
+4 '4' '4' '4' '$' '$' '$'
+5 '5' '5' '5' '%' '%' '%'
+6 '6' '6' '6' '^' '^' '^'
+7 '7' '7' '7' '&' '&' '&'
+8 '8' '8' '8' '*' '*' '*'
+9 '9' '9' '9' '(' '(' '('
+
+# the rest is for a qwerty keyboard
+#
+TAB 0x9 0x9 0x9 0x9 0x9 0x9
+GRAVE '`' '`' '`' '~' '`' '~'
+MINUS '-' '-' '-' '_' '-' '_'
+EQUALS '=' '=' '=' '+' '=' '+'
+LEFT_BRACKET '[' '[' '[' '{' '[' '{'
+RIGHT_BRACKET ']' ']' ']' '}' ']' '}'
+BACKSLASH '\' '\' '\' '|' '\' '|'
+SEMICOLON ';' ';' ';' ':' ';' ':'
+APOSTROPHE ''' ''' ''' '"' ''' '"'
+STAR '*' '*' '*' '*' '*' '*'
+POUND '#' '#' '#' '#' '#' '#'
+PLUS '+' '+' '+' '+' '+' '+'
+
+
+
diff --git a/data/sounds/AllAudio.mk b/data/sounds/AllAudio.mk
new file mode 100644
index 0000000..4e7a403
--- /dev/null
+++ b/data/sounds/AllAudio.mk
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+$(call inherit-product, frameworks/base/data/sounds/OriginalAudio.mk)
+$(call inherit-product, frameworks/base/data/sounds/AudioPackage2.mk)
+$(call inherit-product, frameworks/base/data/sounds/AudioPackage3.mk)
+$(call inherit-product, frameworks/base/data/sounds/AudioPackage4.mk)
diff --git a/docs/html/community/index.jd b/docs/html/community/index.jd
index 3e69de4..23203c1 100644
--- a/docs/html/community/index.jd
+++ b/docs/html/community/index.jd
@@ -4,10 +4,10 @@
<div id="mainBodyFluid">
<h1>Community</h1>
-<p>Welcome to the Android developers community! We're glad you're here and invite you to participate in these discussions. Before posting, please read the <a href="http://source.android.com/discuss/android-discussion-groups-charter">Groups Charter</a> that covers the community guidelines.</p>
+<p>Welcome to the Android developers community! We're glad you're here and invite you to participate in these discussions. Before posting, please read the <a href="http://source.android.com/community/groups-charter.html">Groups Charter</a> that covers the community guidelines.</p>
<p class="note"><strong>Note:</strong> If you are seeking discussion about Android source code (not application development),
-then please refer to the <a href="http://source.android.com/discuss">Open Source Project Mailing lists</a>.</p>
+then please refer to the <a href="http://source.android.com/community">Open Source Project Mailing lists</a>.</p>
<p style="margin-bottom:.5em"><strong>Contents</strong></p>
<ol class="toc">
@@ -31,7 +31,7 @@
As you write your post, please do the following:
<ol>
<li><b>Read
-the <a href="http://sites.google.com/a/android.com/opensource/discuss/android-discussion-groups-charter">mailing list charter</a></b> that covers the community guidelines.
+the <a href="http://source.android.com/community/groups-charter.html">mailing list charter</a></b> that covers the community guidelines.
</li>
<li><b>Select the most appropriate mailing list for your question</b>. There are several different lists for
developers, described below.</li>
diff --git a/docs/html/guide/topics/resources/animation-resource.jd b/docs/html/guide/topics/resources/animation-resource.jd
index e0ce051..972dd72 100644
--- a/docs/html/guide/topics/resources/animation-resource.jd
+++ b/docs/html/guide/topics/resources/animation-resource.jd
@@ -65,10 +65,10 @@
android:pivotX="<em>float</em>"
android:pivotY="<em>float</em>" />
<<a href="#translate-element">translate</a>
- android:fromX="<em>float</em>"
- android:toX="<em>float</em>"
- android:fromY="<em>float</em>"
- android:toY="<em>float</em>" />
+ android:fromXDelta="<em>float</em>"
+ android:toXDelta="<em>float</em>"
+ android:fromYDelta="<em>float</em>"
+ android:toYDelta="<em>float</em>" />
<<a href="#rotate-element">rotate</a>
android:fromDegrees="<em>float</em>"
android:toDegrees="<em>float</em>"
@@ -212,10 +212,10 @@
android:shareInterpolator="false">
<scale
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
- android:fromXScale="1.0"
- android:toXScale="1.4"
- android:fromYScale="1.0"
- android:toYScale="0.6"
+ android:fromXScale="1.0"
+ android:toXScale="1.4"
+ android:fromYScale="1.0"
+ android:toYScale="0.6"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="false"
@@ -224,18 +224,18 @@
android:interpolator="@android:anim/accelerate_interpolator"
android:startOffset="700">
<scale
- android:fromXScale="1.4"
+ android:fromXScale="1.4"
android:toXScale="0.0"
android:fromYScale="0.6"
- android:toYScale="0.0"
- android:pivotX="50%"
- android:pivotY="50%"
+ android:toYScale="0.0"
+ android:pivotX="50%"
+ android:pivotY="50%"
android:duration="400" />
<rotate
- android:fromDegrees="0"
+ android:fromDegrees="0"
android:toDegrees="-45"
- android:toYScale="0.0"
- android:pivotX="50%"
+ android:toYScale="0.0"
+ android:pivotX="50%"
android:pivotY="50%"
android:duration="400" />
</set>
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 5d00db1..60030f0 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -494,6 +494,7 @@
<td>Keyboard availability</td>
<td>
<code>keysexposed</code><br/>
+ <code>keyshidden</code><br/>
<code>keyssoft</code>
</td>
<td>
diff --git a/docs/html/intl/ja/community/index.jd b/docs/html/intl/ja/community/index.jd
index 659aee7..490b23f 100644
--- a/docs/html/intl/ja/community/index.jd
+++ b/docs/html/intl/ja/community/index.jd
@@ -4,9 +4,9 @@
<div id="mainBodyFluid">
<h1>コミュニティ</h1>
- <p>Android デベロッパー コミュニティへようこそ。コミュニティでのディスカッションにぜひ参加してください。投稿する前に、コミュニティ ガイドラインが記載されている<a href="http://source.android.com/discuss/android-discussion-groups-charter">グループの趣意</a>をお読みください。</p>
+ <p>Android デベロッパー コミュニティへようこそ。コミュニティでのディスカッションにぜひ参加してください。投稿する前に、コミュニティ ガイドラインが記載されている<a href="http://source.android.com/community/groups-charter.html">グループの趣意</a>をお読みください。</p>
-<p class="note"><strong>注:</strong> Android ソース コード(アプリケーション開発ではなく)に関するディスカッションは、<a href="http://source.android.com/discuss">オープンソース プロジェクトのメーリング リスト</a>(英語)を参照してください。</p>
+<p class="note"><strong>注:</strong> Android ソース コード(アプリケーション開発ではなく)に関するディスカッションは、<a href="http://source.android.com/community">オープンソース プロジェクトのメーリング リスト</a>(英語)を参照してください。</p>
<p style="margin-bottom:.5em"><strong>目次</strong></p>
<ol class="toc">
@@ -28,7 +28,7 @@
<p>質問への答えが見つからない場合、コミュニティで質問することをおすすめします。投稿する際は、次の手順に従ってください。
<ol>
-<li>コミュニティ ガイドラインが記載されている<b><a href="http://sites.google.com/a/android.com/opensource/discuss/android-discussion-groups-charter">Android メーリングリストの趣意</a></b>をお読みください。
+<li>コミュニティ ガイドラインが記載されている<b><a href="http://source.android.com/community/groups-charter.html">Android メーリングリストの趣意</a></b>をお読みください。
</li>
<li><b>質問に最適なメーリング リストを選択してください</b>。後述するように、デベロッパー向けのメーリング リストは何種類かに分かれています。</li>
<li>
diff --git a/docs/html/intl/ja/resources/community-groups.jd b/docs/html/intl/ja/resources/community-groups.jd
index c99b1f8..ecedde1 100644
--- a/docs/html/intl/ja/resources/community-groups.jd
+++ b/docs/html/intl/ja/resources/community-groups.jd
@@ -4,9 +4,9 @@
<div id="mainBodyFluid">
<h1>コミュニティ</h1>
- <p>Android デベロッパー コミュニティへようこそ。コミュニティでのディスカッションにぜひ参加してください。投稿する前に、コミュニティ ガイドラインが記載されている<a href="http://source.android.com/discuss/android-discussion-groups-charter">グループの趣意</a>をお読みください。</p>
+ <p>Android デベロッパー コミュニティへようこそ。コミュニティでのディスカッションにぜひ参加してください。投稿する前に、コミュニティ ガイドラインが記載されている<a href="http://source.android.com/community/groups-charter.html">グループの趣意</a>をお読みください。</p>
-<p class="note"><strong>注:</strong> Android ソース コード(アプリケーション開発ではなく)に関するディスカッションは、<a href="http://source.android.com/discuss">オープンソース プロジェクトのメーリング リスト</a>(英語)を参照してください。</p>
+<p class="note"><strong>注:</strong> Android ソース コード(アプリケーション開発ではなく)に関するディスカッションは、<a href="http://source.android.com/community">オープンソース プロジェクトのメーリング リスト</a>(英語)を参照してください。</p>
<p style="margin-bottom:.5em"><strong>目次</strong></p>
<ol class="toc">
@@ -28,7 +28,7 @@
<p>質問への答えが見つからない場合、コミュニティで質問することをおすすめします。投稿する際は、次の手順に従ってください。
<ol>
-<li>コミュニティ ガイドラインが記載されている<b><a href="http://sites.google.com/a/android.com/opensource/discuss/android-discussion-groups-charter">Android メーリングリストの趣意</a></b>をお読みください。
+<li>コミュニティ ガイドラインが記載されている<b><a href="http://source.android.com/community/groups-charter.html">Android メーリングリストの趣意</a></b>をお読みください。
</li>
<li><b>質問に最適なメーリング リストを選択してください</b>。後述するように、デベロッパー向けのメーリング リストは何種類かに分かれています。</li>
<li>
diff --git a/docs/html/resources/articles/painless-threading.jd b/docs/html/resources/articles/painless-threading.jd
index 921f4df..17cec35 100644
--- a/docs/html/resources/articles/painless-threading.jd
+++ b/docs/html/resources/articles/painless-threading.jd
@@ -108,7 +108,7 @@
new DownloadImageTask().execute("http://example.com/image.png");
}
-private class DownloadImageTask extends AsyncTask<string, void,="" bitmap=""> {
+private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
diff --git a/docs/html/resources/community-groups.jd b/docs/html/resources/community-groups.jd
index ef1960b..599c4ae 100644
--- a/docs/html/resources/community-groups.jd
+++ b/docs/html/resources/community-groups.jd
@@ -22,7 +22,7 @@
<p>Welcome to the Android developers community! We're glad you're here and invite you to participate in discussions with other Android application developers on topics that interest you.</p>
-<p>The lists on this page are primarily for discussion about Android application development. If you are seeking discussion about Android source code (not application development), then please refer to the <a href="http://source.android.com/discuss">Open Source Project Mailing lists</a>.</p>
+<p>The lists on this page are primarily for discussion about Android application development. If you are seeking discussion about Android source code (not application development), then please refer to the <a href="http://source.android.com/community">Open Source Project Mailing lists</a>.</p>
<h2 id="StackOverflow">Stack Overflow</h2>
diff --git a/drm/common/Android.mk b/drm/common/Android.mk
new file mode 100644
index 0000000..c79a91a
--- /dev/null
+++ b/drm/common/Android.mk
@@ -0,0 +1,44 @@
+#
+# 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:= \
+ DrmConstraints.cpp \
+ DrmMetadata.cpp \
+ DrmConvertedStatus.cpp \
+ DrmEngineBase.cpp \
+ DrmInfo.cpp \
+ DrmInfoRequest.cpp \
+ DrmInfoStatus.cpp \
+ DrmRights.cpp \
+ DrmSupportInfo.cpp \
+ IDrmIOService.cpp \
+ IDrmManagerService.cpp \
+ IDrmServiceListener.cpp \
+ DrmInfoEvent.cpp \
+ ReadWriteUtils.cpp
+
+LOCAL_C_INCLUDES := \
+ $(TOP)/frameworks/base/include \
+ $(TOP)/frameworks/base/drm/libdrmframework/include \
+ $(TOP)/frameworks/base/drm/libdrmframework/plugins/common/include
+
+LOCAL_MODULE:= libdrmframeworkcommon
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/common/DrmConstraints.cpp b/drm/common/DrmConstraints.cpp
new file mode 100644
index 0000000..4a4d798
--- /dev/null
+++ b/drm/common/DrmConstraints.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 <drm/DrmConstraints.h>
+
+using namespace android;
+
+const String8 DrmConstraints::MAX_REPEAT_COUNT("max_repeat_count");
+const String8 DrmConstraints::REMAINING_REPEAT_COUNT("remaining_repeat_count");
+const String8 DrmConstraints::LICENSE_START_TIME("license_start_time");
+const String8 DrmConstraints::LICENSE_EXPIRY_TIME("license_expiry_time");
+const String8 DrmConstraints::LICENSE_AVAILABLE_TIME("license_available_time");
+const String8 DrmConstraints::EXTENDED_METADATA("extended_metadata");
+
+int DrmConstraints::getCount(void) const {
+ return mConstraintMap.size();
+}
+
+status_t DrmConstraints::put(const String8* key, const char* value) {
+ int length = strlen(value);
+ char* charValue = new char[length + 1];
+ if (NULL != charValue) {
+ strncpy(charValue, value, length);
+ charValue[length] = '\0';
+ mConstraintMap.add(*key, charValue);
+ }
+ return DRM_NO_ERROR;
+}
+
+String8 DrmConstraints::get(const String8& key) const {
+ if (NULL != getValue(&key)) {
+ return String8(getValue(&key));
+ }
+ return String8("");
+}
+
+const char* DrmConstraints::getValue(const String8* key) const {
+ if (NAME_NOT_FOUND != mConstraintMap.indexOfKey(*key)) {
+ return mConstraintMap.valueFor(*key);
+ }
+ return NULL;
+}
+
+const char* DrmConstraints::getAsByteArray(const String8* key) const {
+ return getValue(key);
+}
+
+bool DrmConstraints::KeyIterator::hasNext() {
+ return mIndex < mDrmConstraints->mConstraintMap.size();
+}
+
+const String8& DrmConstraints::KeyIterator::next() {
+ const String8& key = mDrmConstraints->mConstraintMap.keyAt(mIndex);
+ mIndex++;
+ return key;
+}
+
+DrmConstraints::KeyIterator DrmConstraints::keyIterator() {
+ return KeyIterator(this);
+}
+
+DrmConstraints::KeyIterator::KeyIterator(const DrmConstraints::KeyIterator& keyIterator)
+ : mDrmConstraints(keyIterator.mDrmConstraints),
+ mIndex(keyIterator.mIndex) {
+}
+
+DrmConstraints::KeyIterator& DrmConstraints::KeyIterator::operator=(
+ const DrmConstraints::KeyIterator& keyIterator) {
+ mDrmConstraints = keyIterator.mDrmConstraints;
+ mIndex = keyIterator.mIndex;
+ return *this;
+}
+
+
+DrmConstraints::Iterator DrmConstraints::iterator() {
+ return Iterator(this);
+}
+
+DrmConstraints::Iterator::Iterator(const DrmConstraints::Iterator& iterator) :
+ mDrmConstraints(iterator.mDrmConstraints),
+ mIndex(iterator.mIndex) {
+}
+
+DrmConstraints::Iterator& DrmConstraints::Iterator::operator=(
+ const DrmConstraints::Iterator& iterator) {
+ mDrmConstraints = iterator.mDrmConstraints;
+ mIndex = iterator.mIndex;
+ return *this;
+}
+
+bool DrmConstraints::Iterator::hasNext() {
+ return mIndex < mDrmConstraints->mConstraintMap.size();
+}
+
+String8 DrmConstraints::Iterator::next() {
+ String8 value = String8(mDrmConstraints->mConstraintMap.editValueAt(mIndex));
+ mIndex++;
+ return value;
+}
+
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java b/drm/common/DrmConvertedStatus.cpp
similarity index 64%
copy from telephony/java/com/android/internal/telephony/gsm/stk/StkException.java
copy to drm/common/DrmConvertedStatus.cpp
index 86de366..5d035f5 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java
+++ b/drm/common/DrmConvertedStatus.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,15 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+#include <drm/DrmConvertedStatus.h>
-import android.util.AndroidException;
+using namespace android;
+DrmConvertedStatus::DrmConvertedStatus(
+ int _statusCode, const DrmBuffer* _convertedData, int _offset) :
+ statusCode(_statusCode),
+ convertedData(_convertedData),
+ offset(_offset) {
-/**
- * Base class for all the exceptions in STK service.
- *
- * {@hide}
- */
-class StkException extends AndroidException {
- public StkException() {
- super();
- }
}
+
diff --git a/drm/common/DrmEngineBase.cpp b/drm/common/DrmEngineBase.cpp
new file mode 100644
index 0000000..ac360eb
--- /dev/null
+++ b/drm/common/DrmEngineBase.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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 "DrmEngineBase.h"
+
+using namespace android;
+
+DrmEngineBase::DrmEngineBase() {
+
+}
+
+DrmEngineBase::~DrmEngineBase() {
+
+}
+
+DrmConstraints* DrmEngineBase::getConstraints(
+ int uniqueId, const String8* path, int action) {
+ return onGetConstraints(uniqueId, path, action);
+}
+
+DrmMetadata* DrmEngineBase::getMetadata(int uniqueId, const String8* path) {
+ return onGetMetadata(uniqueId, path);
+}
+
+status_t DrmEngineBase::initialize(int uniqueId) {
+ return onInitialize(uniqueId);
+}
+
+status_t DrmEngineBase::setOnInfoListener(
+ int uniqueId, const IDrmEngine::OnInfoListener* infoListener) {
+ return onSetOnInfoListener(uniqueId, infoListener);
+}
+
+status_t DrmEngineBase::terminate(int uniqueId) {
+ return onTerminate(uniqueId);
+}
+
+bool DrmEngineBase::canHandle(int uniqueId, const String8& path) {
+ return onCanHandle(uniqueId, path);
+}
+
+DrmInfoStatus* DrmEngineBase::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+ return onProcessDrmInfo(uniqueId, drmInfo);
+}
+
+status_t DrmEngineBase::saveRights(
+ int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath) {
+ return onSaveRights(uniqueId, drmRights, rightsPath, contentPath);
+}
+
+DrmInfo* DrmEngineBase::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+ return onAcquireDrmInfo(uniqueId, drmInfoRequest);
+}
+
+String8 DrmEngineBase::getOriginalMimeType(int uniqueId, const String8& path) {
+ return onGetOriginalMimeType(uniqueId, path);
+}
+
+int DrmEngineBase::getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType) {
+ return onGetDrmObjectType(uniqueId, path, mimeType);
+}
+
+int DrmEngineBase::checkRightsStatus(int uniqueId, const String8& path, int action) {
+ return onCheckRightsStatus(uniqueId, path, action);
+}
+
+status_t DrmEngineBase::consumeRights(
+ int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+ return onConsumeRights(uniqueId, decryptHandle, action, reserve);
+}
+
+status_t DrmEngineBase::setPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+ return onSetPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
+}
+
+bool DrmEngineBase::validateAction(
+ int uniqueId, const String8& path,
+ int action, const ActionDescription& description) {
+ return onValidateAction(uniqueId, path, action, description);
+}
+
+status_t DrmEngineBase::removeRights(int uniqueId, const String8& path) {
+ return onRemoveRights(uniqueId, path);
+}
+
+status_t DrmEngineBase::removeAllRights(int uniqueId) {
+ return onRemoveAllRights(uniqueId);
+}
+
+status_t DrmEngineBase::openConvertSession(int uniqueId, int convertId) {
+ return onOpenConvertSession(uniqueId, convertId);
+}
+
+DrmConvertedStatus* DrmEngineBase::convertData(
+ int uniqueId, int convertId, const DrmBuffer* inputData) {
+ return onConvertData(uniqueId, convertId, inputData);
+}
+
+DrmConvertedStatus* DrmEngineBase::closeConvertSession(int uniqueId, int convertId) {
+ return onCloseConvertSession(uniqueId, convertId);
+}
+
+DrmSupportInfo* DrmEngineBase::getSupportInfo(int uniqueId) {
+ return onGetSupportInfo(uniqueId);
+}
+
+status_t DrmEngineBase::openDecryptSession(
+ int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) {
+ return onOpenDecryptSession(uniqueId, decryptHandle, fd, offset, length);
+}
+
+status_t DrmEngineBase::openDecryptSession(
+ int uniqueId, DecryptHandle* decryptHandle, const char* uri) {
+ return onOpenDecryptSession(uniqueId, decryptHandle, uri);
+}
+
+status_t DrmEngineBase::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+ return onCloseDecryptSession(uniqueId, decryptHandle);
+}
+
+status_t DrmEngineBase::initializeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) {
+ return onInitializeDecryptUnit(uniqueId, decryptHandle, decryptUnitId, headerInfo);
+}
+
+status_t DrmEngineBase::decrypt(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+ return onDecrypt(uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
+}
+
+status_t DrmEngineBase::finalizeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+ return onFinalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
+}
+
+ssize_t DrmEngineBase::pread(
+ int uniqueId, DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset) {
+ return onPread(uniqueId, decryptHandle, buffer, numBytes, offset);
+}
+
diff --git a/drm/common/DrmInfo.cpp b/drm/common/DrmInfo.cpp
new file mode 100644
index 0000000..ddcab33
--- /dev/null
+++ b/drm/common/DrmInfo.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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 <drm/DrmInfo.h>
+
+using namespace android;
+
+DrmInfo::DrmInfo(int infoType, const DrmBuffer& drmBuffer, const String8& mimeType) :
+ mInfoType(infoType),
+ mData(drmBuffer),
+ mMimeType(mimeType) {
+
+}
+
+int DrmInfo::getInfoType(void) const {
+ return mInfoType;
+}
+
+String8 DrmInfo::getMimeType(void) const {
+ return mMimeType;
+}
+
+const DrmBuffer& DrmInfo::getData(void) const {
+ return mData;
+}
+
+int DrmInfo::getCount(void) const {
+ return mAttributes.size();
+}
+
+status_t DrmInfo::put(const String8& key, const String8& value) {
+ mAttributes.add(key, value);
+ return DRM_NO_ERROR;
+}
+
+String8 DrmInfo::get(const String8& key) const {
+ if (NAME_NOT_FOUND != mAttributes.indexOfKey(key)) {
+ return mAttributes.valueFor(key);
+ }
+ return String8("");
+}
+
+int DrmInfo::indexOfKey(const String8& key) const {
+ return mAttributes.indexOfKey(key);
+}
+
+DrmInfo::KeyIterator DrmInfo::keyIterator() const {
+ return KeyIterator(this);
+}
+
+DrmInfo::Iterator DrmInfo::iterator() const {
+ return Iterator(this);
+}
+
+// KeyIterator implementation
+DrmInfo::KeyIterator::KeyIterator(const DrmInfo::KeyIterator& keyIterator) :
+ mDrmInfo(keyIterator.mDrmInfo), mIndex(keyIterator.mIndex) {
+
+}
+
+bool DrmInfo::KeyIterator::hasNext() {
+ return (mIndex < mDrmInfo->mAttributes.size());
+}
+
+const String8& DrmInfo::KeyIterator::next() {
+ const String8& key = mDrmInfo->mAttributes.keyAt(mIndex);
+ mIndex++;
+ return key;
+}
+
+DrmInfo::KeyIterator& DrmInfo::KeyIterator::operator=(const DrmInfo::KeyIterator& keyIterator) {
+ mDrmInfo = keyIterator.mDrmInfo;
+ mIndex = keyIterator.mIndex;
+ return *this;
+}
+
+// Iterator implementation
+DrmInfo::Iterator::Iterator(const DrmInfo::Iterator& iterator)
+ : mDrmInfo(iterator.mDrmInfo), mIndex(iterator.mIndex) {
+
+}
+
+DrmInfo::Iterator& DrmInfo::Iterator::operator=(const DrmInfo::Iterator& iterator) {
+ mDrmInfo = iterator.mDrmInfo;
+ mIndex = iterator.mIndex;
+ return *this;
+}
+
+bool DrmInfo::Iterator::hasNext() {
+ return mIndex < mDrmInfo->mAttributes.size();
+}
+
+String8& DrmInfo::Iterator::next() {
+ String8& value = mDrmInfo->mAttributes.editValueAt(mIndex);
+ mIndex++;
+ return value;
+}
+
diff --git a/drm/common/DrmInfoEvent.cpp b/drm/common/DrmInfoEvent.cpp
new file mode 100644
index 0000000..8d115a8
--- /dev/null
+++ b/drm/common/DrmInfoEvent.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 <utils/String8.h>
+#include <drm/DrmInfoEvent.h>
+
+using namespace android;
+
+DrmInfoEvent::DrmInfoEvent(int uniqueId, int infoType, const String8& message)
+ : mUniqueId(uniqueId),
+ mInfoType(infoType),
+ mMessage(message) {
+
+}
+
+int DrmInfoEvent::getUniqueId() const {
+ return mUniqueId;
+}
+
+int DrmInfoEvent::getType() const {
+ return mInfoType;
+}
+
+const String8& DrmInfoEvent::getMessage() const {
+ return mMessage;
+}
+
diff --git a/drm/common/DrmInfoRequest.cpp b/drm/common/DrmInfoRequest.cpp
new file mode 100644
index 0000000..a646859
--- /dev/null
+++ b/drm/common/DrmInfoRequest.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 <drm/DrmInfoRequest.h>
+
+using namespace android;
+
+const String8 DrmInfoRequest::ACCOUNT_ID("account_id");
+const String8 DrmInfoRequest::SUBSCRIPTION_ID("subscription_id");
+
+DrmInfoRequest::DrmInfoRequest(int infoType, const String8& mimeType) :
+ mInfoType(infoType), mMimeType(mimeType) {
+
+}
+
+String8 DrmInfoRequest::getMimeType(void) const {
+ return mMimeType;
+}
+
+int DrmInfoRequest::getInfoType(void) const {
+ return mInfoType;
+}
+
+int DrmInfoRequest::getCount(void) const {
+ return mRequestInformationMap.size();
+}
+
+status_t DrmInfoRequest::put(const String8& key, const String8& value) {
+ mRequestInformationMap.add(key, value);
+ return DRM_NO_ERROR;
+}
+
+String8 DrmInfoRequest::get(const String8& key) const {
+ if (NAME_NOT_FOUND != mRequestInformationMap.indexOfKey(key)) {
+ return mRequestInformationMap.valueFor(key);
+ }
+ return String8("");
+}
+
+DrmInfoRequest::KeyIterator DrmInfoRequest::keyIterator() const {
+ return KeyIterator(this);
+}
+
+DrmInfoRequest::Iterator DrmInfoRequest::iterator() const {
+ return Iterator(this);
+}
+
+// KeyIterator implementation
+DrmInfoRequest::KeyIterator::KeyIterator(const DrmInfoRequest::KeyIterator& keyIterator)
+ : mDrmInfoRequest(keyIterator.mDrmInfoRequest),
+ mIndex(keyIterator.mIndex) {
+
+}
+
+bool DrmInfoRequest::KeyIterator::hasNext() {
+ return (mIndex < mDrmInfoRequest->mRequestInformationMap.size());
+}
+
+const String8& DrmInfoRequest::KeyIterator::next() {
+ const String8& key = mDrmInfoRequest->mRequestInformationMap.keyAt(mIndex);
+ mIndex++;
+ return key;
+}
+
+DrmInfoRequest::KeyIterator& DrmInfoRequest::KeyIterator::operator=(
+ const DrmInfoRequest::KeyIterator& keyIterator) {
+ mDrmInfoRequest = keyIterator.mDrmInfoRequest;
+ mIndex = keyIterator.mIndex;
+ return *this;
+}
+
+// Iterator implementation
+DrmInfoRequest::Iterator::Iterator(const DrmInfoRequest::Iterator& iterator) :
+ mDrmInfoRequest(iterator.mDrmInfoRequest), mIndex(iterator.mIndex) {
+}
+
+DrmInfoRequest::Iterator& DrmInfoRequest::Iterator::operator=(
+ const DrmInfoRequest::Iterator& iterator) {
+ mDrmInfoRequest = iterator.mDrmInfoRequest;
+ mIndex = iterator.mIndex;
+ return *this;
+}
+
+bool DrmInfoRequest::Iterator::hasNext() {
+ return mIndex < mDrmInfoRequest->mRequestInformationMap.size();
+}
+
+String8& DrmInfoRequest::Iterator::next() {
+ String8& value = mDrmInfoRequest->mRequestInformationMap.editValueAt(mIndex);
+ mIndex++;
+ return value;
+}
+
diff --git a/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java b/drm/common/DrmInfoStatus.cpp
similarity index 62%
rename from tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java
rename to drm/common/DrmInfoStatus.cpp
index 7bb4c35..8ec7311 100644
--- a/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java
+++ b/drm/common/DrmInfoStatus.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-package com.android.testplugin;
+#include <drm/DrmInfoStatus.h>
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
+using namespace android;
-public class TestPlugin extends Service {
-
- @Override
- public IBinder onBind(Intent intent) {
- // TODO Auto-generated method stub
- return null;
- }
+DrmInfoStatus::DrmInfoStatus(
+ int _statusCode, int _infoType, const DrmBuffer* _drmBuffer, const String8& _mimeType) :
+ statusCode(_statusCode),
+ infoType(_infoType),
+ drmBuffer(_drmBuffer),
+ mimeType(_mimeType) {
}
+
diff --git a/drm/common/DrmMetadata.cpp b/drm/common/DrmMetadata.cpp
new file mode 100644
index 0000000..6cc5ec1
--- /dev/null
+++ b/drm/common/DrmMetadata.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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 <drm/DrmMetadata.h>
+
+using namespace android;
+
+int DrmMetadata::getCount(void) const {
+ return mMetadataMap.size();
+}
+
+status_t DrmMetadata::put(const String8* key,
+ const char* value) {
+ if((value != NULL) && (key != NULL)) {
+ int length = strlen(value);
+ char* charValue = new char[length + 1];
+
+ memcpy(charValue, value, length);
+ charValue[length] = '\0';
+ mMetadataMap.add(*key, charValue);
+ }
+ return NO_ERROR;
+}
+
+String8 DrmMetadata::get(const String8& key) const {
+ if (NULL != getValue(&key)) {
+ return String8(getValue(&key));
+ }
+ else {
+ return String8("");
+ }
+}
+
+const char* DrmMetadata::getValue(const String8* key) const {
+ if(key != NULL) {
+ if (NAME_NOT_FOUND != mMetadataMap.indexOfKey(*key)) {
+ return mMetadataMap.valueFor(*key);
+ }
+ else {
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+}
+
+const char* DrmMetadata::getAsByteArray(const String8* key) const {
+ return getValue(key);
+}
+
+bool DrmMetadata::KeyIterator::hasNext() {
+ return mIndex < mDrmMetadata->mMetadataMap.size();
+}
+
+const String8& DrmMetadata::KeyIterator::next() {
+ const String8& key = mDrmMetadata->mMetadataMap.keyAt(mIndex);
+ mIndex++;
+ return key;
+}
+
+DrmMetadata::KeyIterator DrmMetadata::keyIterator() {
+ return KeyIterator(this);
+}
+
+DrmMetadata::KeyIterator::KeyIterator(const DrmMetadata::KeyIterator& keyIterator) :
+ mDrmMetadata(keyIterator.mDrmMetadata),
+ mIndex(keyIterator.mIndex) {
+ LOGV("DrmMetadata::KeyIterator::KeyIterator");
+}
+
+DrmMetadata::KeyIterator& DrmMetadata::KeyIterator::operator=(const DrmMetadata::KeyIterator& keyIterator) {
+ LOGV("DrmMetadata::KeyIterator::operator=");
+ mDrmMetadata = keyIterator.mDrmMetadata;
+ mIndex = keyIterator.mIndex;
+ return *this;
+}
+
+
+DrmMetadata::Iterator DrmMetadata::iterator() {
+ return Iterator(this);
+}
+
+DrmMetadata::Iterator::Iterator(const DrmMetadata::Iterator& iterator) :
+ mDrmMetadata(iterator.mDrmMetadata),
+ mIndex(iterator.mIndex) {
+ LOGV("DrmMetadata::Iterator::Iterator");
+}
+
+DrmMetadata::Iterator& DrmMetadata::Iterator::operator=(const DrmMetadata::Iterator& iterator) {
+ LOGV("DrmMetadata::Iterator::operator=");
+ mDrmMetadata = iterator.mDrmMetadata;
+ mIndex = iterator.mIndex;
+ return *this;
+}
+
+bool DrmMetadata::Iterator::hasNext() {
+ return mIndex < mDrmMetadata->mMetadataMap.size();
+}
+
+String8 DrmMetadata::Iterator::next() {
+ String8 value = String8(mDrmMetadata->mMetadataMap.editValueAt(mIndex));
+ mIndex++;
+ return value;
+}
diff --git a/drm/common/DrmRights.cpp b/drm/common/DrmRights.cpp
new file mode 100644
index 0000000..3aecb3d
--- /dev/null
+++ b/drm/common/DrmRights.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <drm/DrmRights.h>
+#include <ReadWriteUtils.h>
+
+using namespace android;
+
+DrmRights::DrmRights(const String8& rightsFilePath, const String8& mimeType,
+ const String8& accountId, const String8& subscriptionId) :
+ mMimeType(mimeType),
+ mAccountId(accountId),
+ mSubscriptionId(subscriptionId),
+ mRightsFromFile(NULL) {
+ int rightsLength = 0;
+ if (String8("") != rightsFilePath) {
+ rightsLength = ReadWriteUtils::readBytes(rightsFilePath, &mRightsFromFile);
+ }
+ mData = DrmBuffer(mRightsFromFile, rightsLength);
+}
+
+DrmRights::DrmRights(const DrmBuffer& rightsData, const String8& mimeType,
+ const String8& accountId, const String8& subscriptionId) :
+ mData(rightsData),
+ mMimeType(mimeType),
+ mAccountId(accountId),
+ mSubscriptionId(subscriptionId),
+ mRightsFromFile(NULL) {
+}
+
+DrmRights::~DrmRights() {
+ delete[] mRightsFromFile; mRightsFromFile = NULL;
+}
+
+const DrmBuffer& DrmRights::getData(void) const {
+ return mData;
+}
+
+String8 DrmRights::getMimeType(void) const {
+ return mMimeType;
+}
+
+String8 DrmRights::getAccountId(void) const {
+ return mAccountId;
+}
+
+String8 DrmRights::getSubscriptionId(void) const {
+ return mSubscriptionId;
+}
+
diff --git a/drm/common/DrmSupportInfo.cpp b/drm/common/DrmSupportInfo.cpp
new file mode 100644
index 0000000..ffc8953
--- /dev/null
+++ b/drm/common/DrmSupportInfo.cpp
@@ -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.
+ */
+
+#include <drm/DrmSupportInfo.h>
+
+using namespace android;
+
+DrmSupportInfo::DrmSupportInfo() {
+
+}
+
+DrmSupportInfo::DrmSupportInfo(const DrmSupportInfo& drmSupportInfo):
+ mMimeTypeVector(drmSupportInfo.mMimeTypeVector),
+ mFileSuffixVector(drmSupportInfo.mFileSuffixVector),
+ mDescription(drmSupportInfo.mDescription) {
+
+}
+
+bool DrmSupportInfo::operator<(const DrmSupportInfo& drmSupportInfo) const {
+ // Do we need to check mMimeTypeVector & mFileSuffixVector ?
+ // Note Vector doesn't overrides "<" operator
+ return mDescription < drmSupportInfo.mDescription;
+}
+
+bool DrmSupportInfo::operator==(const DrmSupportInfo& drmSupportInfo) const {
+ // Do we need to check mMimeTypeVector & mFileSuffixVector ?
+ // Note Vector doesn't overrides "==" operator
+ return (mDescription == drmSupportInfo.mDescription);
+}
+
+bool DrmSupportInfo::isSupportedMimeType(const String8& mimeType) const {
+ for (unsigned int i = 0; i < mMimeTypeVector.size(); i++) {
+ const String8 item = mMimeTypeVector.itemAt(i);
+
+ if (String8("") != mimeType && item.find(mimeType) != -1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DrmSupportInfo::isSupportedFileSuffix(const String8& fileType) const {
+ for (unsigned int i = 0; i < mFileSuffixVector.size(); i++) {
+ const String8 item = mFileSuffixVector.itemAt(i);
+
+ if (String8("") != fileType && item.find(fileType) != -1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+DrmSupportInfo& DrmSupportInfo::operator=(const DrmSupportInfo& drmSupportInfo) {
+ mMimeTypeVector = drmSupportInfo.mMimeTypeVector;
+ mFileSuffixVector = drmSupportInfo.mFileSuffixVector;
+ mDescription = drmSupportInfo.mDescription;
+ return *this;
+}
+
+int DrmSupportInfo::getMimeTypeCount(void) const {
+ return mMimeTypeVector.size();
+}
+
+int DrmSupportInfo::getFileSuffixCount(void) const {
+ return mFileSuffixVector.size();
+}
+
+status_t DrmSupportInfo::addMimeType(const String8& mimeType) {
+ mMimeTypeVector.push(mimeType);
+ return DRM_NO_ERROR;
+}
+
+status_t DrmSupportInfo::addFileSuffix(const String8& fileSuffix) {
+ mFileSuffixVector.push(fileSuffix);
+ return DRM_NO_ERROR;
+}
+
+status_t DrmSupportInfo::setDescription(const String8& description) {
+ mDescription = description;
+ return DRM_NO_ERROR;
+}
+
+String8 DrmSupportInfo::getDescription() const {
+ return mDescription;
+}
+
+DrmSupportInfo::FileSuffixIterator DrmSupportInfo::getFileSuffixIterator() {
+ return FileSuffixIterator(this);
+}
+
+DrmSupportInfo::MimeTypeIterator DrmSupportInfo::getMimeTypeIterator() {
+ return MimeTypeIterator(this);
+}
+
+DrmSupportInfo::FileSuffixIterator::FileSuffixIterator(
+ const DrmSupportInfo::FileSuffixIterator& iterator) :
+ mDrmSupportInfo(iterator.mDrmSupportInfo),
+ mIndex(iterator.mIndex) {
+
+}
+
+DrmSupportInfo::FileSuffixIterator& DrmSupportInfo::FileSuffixIterator::operator=(
+ const DrmSupportInfo::FileSuffixIterator& iterator) {
+ mDrmSupportInfo = iterator.mDrmSupportInfo;
+ mIndex = iterator.mIndex;
+ return *this;
+}
+
+bool DrmSupportInfo::FileSuffixIterator::hasNext() {
+ return mIndex < mDrmSupportInfo->mFileSuffixVector.size();
+}
+
+String8& DrmSupportInfo::FileSuffixIterator::next() {
+ String8& value = mDrmSupportInfo->mFileSuffixVector.editItemAt(mIndex);
+ mIndex++;
+ return value;
+}
+
+DrmSupportInfo::MimeTypeIterator::MimeTypeIterator(
+ const DrmSupportInfo::MimeTypeIterator& iterator) :
+ mDrmSupportInfo(iterator.mDrmSupportInfo),
+ mIndex(iterator.mIndex) {
+
+}
+
+DrmSupportInfo::MimeTypeIterator& DrmSupportInfo::MimeTypeIterator::operator=(
+ const DrmSupportInfo::MimeTypeIterator& iterator) {
+ mDrmSupportInfo = iterator.mDrmSupportInfo;
+ mIndex = iterator.mIndex;
+ return *this;
+}
+
+bool DrmSupportInfo::MimeTypeIterator::hasNext() {
+ return mIndex < mDrmSupportInfo->mMimeTypeVector.size();
+}
+
+String8& DrmSupportInfo::MimeTypeIterator::next() {
+ String8& value = mDrmSupportInfo->mMimeTypeVector.editItemAt(mIndex);
+ mIndex++;
+ return value;
+}
+
diff --git a/drm/common/IDrmIOService.cpp b/drm/common/IDrmIOService.cpp
new file mode 100644
index 0000000..e44ca55
--- /dev/null
+++ b/drm/common/IDrmIOService.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <drm/drm_framework_common.h>
+#include "IDrmIOService.h"
+
+using namespace android;
+
+void BpDrmIOService::writeToFile(const String8& filePath, const String8& dataBuffer) {
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmIOService::getInterfaceDescriptor());
+ data.writeString8(filePath);
+ data.writeString8(dataBuffer);
+
+ remote()->transact(WRITE_TO_FILE, data, &reply);
+}
+
+String8 BpDrmIOService::readFromFile(const String8& filePath) {
+
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmIOService::getInterfaceDescriptor());
+ data.writeString8(filePath);
+
+ remote()->transact(READ_FROM_FILE, data, &reply);
+ return reply.readString8();
+}
+
+IMPLEMENT_META_INTERFACE(DrmIOService, "drm.IDrmIOService");
+
+status_t BnDrmIOService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+
+ switch (code) {
+ case WRITE_TO_FILE:
+ {
+ CHECK_INTERFACE(IDrmIOService, data, reply);
+
+ writeToFile(data.readString8(), data.readString8());
+ return DRM_NO_ERROR;
+ }
+
+ case READ_FROM_FILE:
+ {
+ CHECK_INTERFACE(IDrmIOService, data, reply);
+
+ String8 dataBuffer = readFromFile(data.readString8());
+ reply->writeString8(dataBuffer);
+ return DRM_NO_ERROR;
+ }
+
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
diff --git a/drm/common/IDrmManagerService.cpp b/drm/common/IDrmManagerService.cpp
new file mode 100644
index 0000000..723b50e
--- /dev/null
+++ b/drm/common/IDrmManagerService.cpp
@@ -0,0 +1,1501 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "IDrmManagerService(Native)"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/IPCThreadState.h>
+
+#include <drm/DrmInfo.h>
+#include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
+#include <drm/DrmRights.h>
+#include <drm/DrmInfoStatus.h>
+#include <drm/DrmConvertedStatus.h>
+#include <drm/DrmInfoRequest.h>
+#include <drm/DrmSupportInfo.h>
+
+#include "IDrmManagerService.h"
+
+#define INVALID_BUFFER_LENGTH -1
+
+using namespace android;
+
+int BpDrmManagerService::addUniqueId(int uniqueId) {
+ LOGV("add uniqueid");
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ remote()->transact(ADD_UNIQUEID, data, &reply);
+ return reply.readInt32();
+}
+
+void BpDrmManagerService::removeUniqueId(int uniqueId) {
+ LOGV("remove uniqueid");
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ remote()->transact(REMOVE_UNIQUEID, data, &reply);
+}
+
+void BpDrmManagerService::addClient(int uniqueId) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ remote()->transact(ADD_CLIENT, data, &reply);
+}
+
+void BpDrmManagerService::removeClient(int uniqueId) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ remote()->transact(REMOVE_CLIENT, data, &reply);
+}
+
+status_t BpDrmManagerService::setDrmServiceListener(
+ int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) {
+ LOGV("setDrmServiceListener");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ data.writeStrongBinder(drmServiceListener->asBinder());
+ remote()->transact(SET_DRM_SERVICE_LISTENER, data, &reply);
+ return reply.readInt32();
+}
+
+status_t BpDrmManagerService::installDrmEngine(int uniqueId, const String8& drmEngineFile) {
+ LOGV("Install DRM Engine");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ data.writeString8(drmEngineFile);
+
+ remote()->transact(INSTALL_DRM_ENGINE, data, &reply);
+ return reply.readInt32();
+}
+
+DrmConstraints* BpDrmManagerService::getConstraints(
+ int uniqueId, const String8* path, const int action) {
+ LOGV("Get Constraints");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ data.writeString8(*path);
+ data.writeInt32(action);
+
+ remote()->transact(GET_CONSTRAINTS_FROM_CONTENT, data, &reply);
+
+ DrmConstraints* drmConstraints = NULL;
+ if (0 != reply.dataAvail()) {
+ //Filling Drm Constraints
+ drmConstraints = new DrmConstraints();
+
+ const int size = reply.readInt32();
+ for (int index = 0; index < size; ++index) {
+ const String8 key(reply.readString8());
+ const int bufferSize = reply.readInt32();
+ char* data = NULL;
+ if (0 < bufferSize) {
+ data = new char[bufferSize];
+ reply.read(data, bufferSize);
+ }
+ drmConstraints->put(&key, data);
+ }
+ }
+ return drmConstraints;
+}
+
+DrmMetadata* BpDrmManagerService::getMetadata(int uniqueId, const String8* path) {
+ LOGV("Get Metadata");
+ Parcel data, reply;
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ DrmMetadata* drmMetadata = NULL;
+ data.writeString8(*path);
+ remote()->transact(GET_METADATA_FROM_CONTENT, data, &reply);
+
+ if (0 != reply.dataAvail()) {
+ //Filling Drm Metadata
+ drmMetadata = new DrmMetadata();
+
+ const int size = reply.readInt32();
+ for (int index = 0; index < size; ++index) {
+ const String8 key(reply.readString8());
+ const int bufferSize = reply.readInt32();
+ char* data = NULL;
+ if (0 < bufferSize) {
+ data = new char[bufferSize];
+ reply.read(data, bufferSize);
+ }
+ drmMetadata->put(&key, data);
+ }
+ }
+ return drmMetadata;
+}
+
+bool BpDrmManagerService::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
+ LOGV("Can Handle");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ data.writeString8(path);
+ data.writeString8(mimeType);
+
+ remote()->transact(CAN_HANDLE, data, &reply);
+
+ return static_cast<bool>(reply.readInt32());
+}
+
+DrmInfoStatus* BpDrmManagerService::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+ LOGV("Process DRM Info");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ //Filling DRM info
+ data.writeInt32(drmInfo->getInfoType());
+ const DrmBuffer dataBuffer = drmInfo->getData();
+ const int dataBufferSize = dataBuffer.length;
+ data.writeInt32(dataBufferSize);
+ if (0 < dataBufferSize) {
+ data.write(dataBuffer.data, dataBufferSize);
+ }
+ data.writeString8(drmInfo->getMimeType());
+
+ data.writeInt32(drmInfo->getCount());
+ DrmInfo::KeyIterator keyIt = drmInfo->keyIterator();
+
+ while (keyIt.hasNext()) {
+ const String8 key = keyIt.next();
+ data.writeString8(key);
+ const String8 value = drmInfo->get(key);
+ data.writeString8((value == String8("")) ? String8("NULL") : value);
+ }
+
+ remote()->transact(PROCESS_DRM_INFO, data, &reply);
+
+ DrmInfoStatus* drmInfoStatus = NULL;
+ if (0 != reply.dataAvail()) {
+ //Filling DRM Info Status
+ const int statusCode = reply.readInt32();
+ const int infoType = reply.readInt32();
+ const String8 mimeType = reply.readString8();
+
+ DrmBuffer* drmBuffer = NULL;
+ if (0 != reply.dataAvail()) {
+ const int bufferSize = reply.readInt32();
+ char* data = NULL;
+ if (0 < bufferSize) {
+ data = new char[bufferSize];
+ reply.read(data, bufferSize);
+ }
+ drmBuffer = new DrmBuffer(data, bufferSize);
+ }
+ drmInfoStatus = new DrmInfoStatus(statusCode, infoType, drmBuffer, mimeType);
+ }
+ return drmInfoStatus;
+}
+
+DrmInfo* BpDrmManagerService::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest) {
+ LOGV("Acquire DRM Info");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ //Filling DRM Info Request
+ data.writeInt32(drmInforequest->getInfoType());
+ data.writeString8(drmInforequest->getMimeType());
+
+ data.writeInt32(drmInforequest->getCount());
+ DrmInfoRequest::KeyIterator keyIt = drmInforequest->keyIterator();
+
+ while (keyIt.hasNext()) {
+ const String8 key = keyIt.next();
+ data.writeString8(key);
+ const String8 value = drmInforequest->get(key);
+ data.writeString8((value == String8("")) ? String8("NULL") : value);
+ }
+
+ remote()->transact(ACQUIRE_DRM_INFO, data, &reply);
+
+ DrmInfo* drmInfo = NULL;
+ if (0 != reply.dataAvail()) {
+ //Filling DRM Info
+ const int infoType = reply.readInt32();
+ const int bufferSize = reply.readInt32();
+ char* data = NULL;
+
+ if (0 < bufferSize) {
+ data = new char[bufferSize];
+ reply.read(data, bufferSize);
+ }
+ drmInfo = new DrmInfo(infoType, DrmBuffer(data, bufferSize), reply.readString8());
+
+ const int size = reply.readInt32();
+ for (int index = 0; index < size; ++index) {
+ const String8 key(reply.readString8());
+ const String8 value(reply.readString8());
+ drmInfo->put(key, (value == String8("NULL")) ? String8("") : value);
+ }
+ }
+ return drmInfo;
+}
+
+status_t BpDrmManagerService::saveRights(
+ int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath) {
+ LOGV("Save Rights");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ //Filling Drm Rights
+ const DrmBuffer dataBuffer = drmRights.getData();
+ data.writeInt32(dataBuffer.length);
+ data.write(dataBuffer.data, dataBuffer.length);
+
+ const String8 mimeType = drmRights.getMimeType();
+ data.writeString8((mimeType == String8("")) ? String8("NULL") : mimeType);
+
+ const String8 accountId = drmRights.getAccountId();
+ data.writeString8((accountId == String8("")) ? String8("NULL") : accountId);
+
+ const String8 subscriptionId = drmRights.getSubscriptionId();
+ data.writeString8((subscriptionId == String8("")) ? String8("NULL") : subscriptionId);
+
+ data.writeString8((rightsPath == String8("")) ? String8("NULL") : rightsPath);
+ data.writeString8((contentPath == String8("")) ? String8("NULL") : contentPath);
+
+ remote()->transact(SAVE_RIGHTS, data, &reply);
+ return reply.readInt32();
+}
+
+String8 BpDrmManagerService::getOriginalMimeType(int uniqueId, const String8& path) {
+ LOGV("Get Original MimeType");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ data.writeString8(path);
+
+ remote()->transact(GET_ORIGINAL_MIMETYPE, data, &reply);
+ return reply.readString8();
+}
+
+int BpDrmManagerService::getDrmObjectType(
+ int uniqueId, const String8& path, const String8& mimeType) {
+ LOGV("Get Drm object type");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ data.writeString8(path);
+ data.writeString8(mimeType);
+
+ remote()->transact(GET_DRM_OBJECT_TYPE, data, &reply);
+
+ return reply.readInt32();
+}
+
+int BpDrmManagerService::checkRightsStatus(int uniqueId, const String8& path, int action) {
+ LOGV("checkRightsStatus");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ data.writeString8(path);
+ data.writeInt32(action);
+
+ remote()->transact(CHECK_RIGHTS_STATUS, data, &reply);
+
+ return reply.readInt32();
+}
+
+status_t BpDrmManagerService::consumeRights(
+ int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+ LOGV("consumeRights");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ data.writeInt32(decryptHandle->decryptId);
+ data.writeString8(decryptHandle->mimeType);
+ data.writeInt32(decryptHandle->decryptApiType);
+ data.writeInt32(decryptHandle->status);
+
+ if (NULL != decryptHandle->decryptInfo) {
+ data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+ } else {
+ data.writeInt32(INVALID_BUFFER_LENGTH);
+ }
+
+ data.writeInt32(action);
+ data.writeInt32(static_cast< int>(reserve));
+
+ remote()->transact(CONSUME_RIGHTS, data, &reply);
+ return reply.readInt32();
+}
+
+status_t BpDrmManagerService::setPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+ LOGV("setPlaybackStatus");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ data.writeInt32(decryptHandle->decryptId);
+ data.writeString8(decryptHandle->mimeType);
+ data.writeInt32(decryptHandle->decryptApiType);
+ data.writeInt32(decryptHandle->status);
+
+ if (NULL != decryptHandle->decryptInfo) {
+ data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+ } else {
+ data.writeInt32(INVALID_BUFFER_LENGTH);
+ }
+
+ data.writeInt32(playbackStatus);
+ data.writeInt32(position);
+
+ remote()->transact(SET_PLAYBACK_STATUS, data, &reply);
+ return reply.readInt32();
+}
+
+bool BpDrmManagerService::validateAction(
+ int uniqueId, const String8& path,
+ int action, const ActionDescription& description) {
+ LOGV("validateAction");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ data.writeString8(path);
+ data.writeInt32(action);
+ data.writeInt32(description.outputType);
+ data.writeInt32(description.configuration);
+
+ remote()->transact(VALIDATE_ACTION, data, &reply);
+
+ return static_cast<bool>(reply.readInt32());
+}
+
+status_t BpDrmManagerService::removeRights(int uniqueId, const String8& path) {
+ LOGV("removeRights");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ data.writeString8(path);
+
+ remote()->transact(REMOVE_RIGHTS, data, &reply);
+ return reply.readInt32();
+}
+
+status_t BpDrmManagerService::removeAllRights(int uniqueId) {
+ LOGV("removeAllRights");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ remote()->transact(REMOVE_ALL_RIGHTS, data, &reply);
+ return reply.readInt32();
+}
+
+int BpDrmManagerService::openConvertSession(int uniqueId, const String8& mimeType) {
+ LOGV("openConvertSession");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ data.writeString8(mimeType);
+
+ remote()->transact(OPEN_CONVERT_SESSION, data, &reply);
+ return reply.readInt32();
+}
+
+DrmConvertedStatus* BpDrmManagerService::convertData(
+ int uniqueId, int convertId, const DrmBuffer* inputData) {
+ LOGV("convertData");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ data.writeInt32(convertId);
+ data.writeInt32(inputData->length);
+ data.write(inputData->data, inputData->length);
+
+ remote()->transact(CONVERT_DATA, data, &reply);
+
+ DrmConvertedStatus* drmConvertedStatus = NULL;
+
+ if (0 != reply.dataAvail()) {
+ //Filling DRM Converted Status
+ const int statusCode = reply.readInt32();
+ const int offset = reply.readInt32();
+
+ DrmBuffer* convertedData = NULL;
+ if (0 != reply.dataAvail()) {
+ const int bufferSize = reply.readInt32();
+ char* data = NULL;
+ if (0 < bufferSize) {
+ data = new char[bufferSize];
+ reply.read(data, bufferSize);
+ }
+ convertedData = new DrmBuffer(data, bufferSize);
+ }
+ drmConvertedStatus = new DrmConvertedStatus(statusCode, convertedData, offset);
+ }
+ return drmConvertedStatus;
+}
+
+DrmConvertedStatus* BpDrmManagerService::closeConvertSession(int uniqueId, int convertId) {
+ LOGV("closeConvertSession");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ data.writeInt32(convertId);
+
+ remote()->transact(CLOSE_CONVERT_SESSION, data, &reply);
+
+ DrmConvertedStatus* drmConvertedStatus = NULL;
+
+ if (0 != reply.dataAvail()) {
+ //Filling DRM Converted Status
+ const int statusCode = reply.readInt32();
+ const int offset = reply.readInt32();
+
+ DrmBuffer* convertedData = NULL;
+ if (0 != reply.dataAvail()) {
+ const int bufferSize = reply.readInt32();
+ char* data = NULL;
+ if (0 < bufferSize) {
+ data = new char[bufferSize];
+ reply.read(data, bufferSize);
+ }
+ convertedData = new DrmBuffer(data, bufferSize);
+ }
+ drmConvertedStatus = new DrmConvertedStatus(statusCode, convertedData, offset);
+ }
+ return drmConvertedStatus;
+}
+
+status_t BpDrmManagerService::getAllSupportInfo(
+ int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) {
+ LOGV("Get All Support Info");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ remote()->transact(GET_ALL_SUPPORT_INFO, data, &reply);
+
+ //Filling DRM Support Info
+ const int arraySize = reply.readInt32();
+ if (0 < arraySize) {
+ *drmSupportInfoArray = new DrmSupportInfo[arraySize];
+
+ for (int index = 0; index < arraySize; ++index) {
+ DrmSupportInfo drmSupportInfo;
+
+ const int fileSuffixVectorSize = reply.readInt32();
+ for (int i = 0; i < fileSuffixVectorSize; ++i) {
+ drmSupportInfo.addFileSuffix(reply.readString8());
+ }
+
+ const int mimeTypeVectorSize = reply.readInt32();
+ for (int i = 0; i < mimeTypeVectorSize; ++i) {
+ drmSupportInfo.addMimeType(reply.readString8());
+ }
+
+ drmSupportInfo.setDescription(reply.readString8());
+ (*drmSupportInfoArray)[index] = drmSupportInfo;
+ }
+ }
+ *length = arraySize;
+ return reply.readInt32();
+}
+
+DecryptHandle* BpDrmManagerService::openDecryptSession(
+ int uniqueId, int fd, int offset, int length) {
+ LOGV("Entering BpDrmManagerService::openDecryptSession");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ data.writeFileDescriptor(fd);
+ data.writeInt32(offset);
+ data.writeInt32(length);
+
+ remote()->transact(OPEN_DECRYPT_SESSION, data, &reply);
+
+ DecryptHandle* handle = NULL;
+ if (0 != reply.dataAvail()) {
+ handle = new DecryptHandle();
+ handle->decryptId = reply.readInt32();
+ handle->mimeType = reply.readString8();
+ handle->decryptApiType = reply.readInt32();
+ handle->status = reply.readInt32();
+ handle->decryptInfo = NULL;
+ if (0 != reply.dataAvail()) {
+ handle->decryptInfo = new DecryptInfo();
+ handle->decryptInfo->decryptBufferLength = reply.readInt32();
+ }
+ } else {
+ LOGE("no decryptHandle is generated in service side");
+ }
+ return handle;
+}
+
+DecryptHandle* BpDrmManagerService::openDecryptSession(int uniqueId, const char* uri) {
+ LOGV("Entering BpDrmManagerService::openDecryptSession");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+ data.writeString8(String8(uri));
+
+ remote()->transact(OPEN_DECRYPT_SESSION_FROM_URI, data, &reply);
+
+ DecryptHandle* handle = NULL;
+ if (0 != reply.dataAvail()) {
+ handle = new DecryptHandle();
+ handle->decryptId = reply.readInt32();
+ handle->mimeType = reply.readString8();
+ handle->decryptApiType = reply.readInt32();
+ handle->status = reply.readInt32();
+ handle->decryptInfo = NULL;
+ if (0 != reply.dataAvail()) {
+ handle->decryptInfo = new DecryptInfo();
+ handle->decryptInfo->decryptBufferLength = reply.readInt32();
+ }
+ } else {
+ LOGE("no decryptHandle is generated in service side");
+ }
+ return handle;
+}
+
+status_t BpDrmManagerService::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+ LOGV("closeDecryptSession");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ data.writeInt32(decryptHandle->decryptId);
+ data.writeString8(decryptHandle->mimeType);
+ data.writeInt32(decryptHandle->decryptApiType);
+ data.writeInt32(decryptHandle->status);
+
+ if (NULL != decryptHandle->decryptInfo) {
+ data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+ } else {
+ data.writeInt32(INVALID_BUFFER_LENGTH);
+ }
+
+ remote()->transact(CLOSE_DECRYPT_SESSION, data, &reply);
+
+ if (NULL != decryptHandle->decryptInfo) {
+ LOGV("deleting decryptInfo");
+ delete decryptHandle->decryptInfo; decryptHandle->decryptInfo = NULL;
+ }
+ delete decryptHandle; decryptHandle = NULL;
+ return reply.readInt32();
+}
+
+status_t BpDrmManagerService::initializeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo) {
+ LOGV("initializeDecryptUnit");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ data.writeInt32(decryptHandle->decryptId);
+ data.writeString8(decryptHandle->mimeType);
+ data.writeInt32(decryptHandle->decryptApiType);
+ data.writeInt32(decryptHandle->status);
+
+ if (NULL != decryptHandle->decryptInfo) {
+ data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+ } else {
+ data.writeInt32(INVALID_BUFFER_LENGTH);
+ }
+ data.writeInt32(decryptUnitId);
+
+ data.writeInt32(headerInfo->length);
+ data.write(headerInfo->data, headerInfo->length);
+
+ remote()->transact(INITIALIZE_DECRYPT_UNIT, data, &reply);
+ return reply.readInt32();
+}
+
+status_t BpDrmManagerService::decrypt(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+ LOGV("decrypt");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ data.writeInt32(decryptHandle->decryptId);
+ data.writeString8(decryptHandle->mimeType);
+ data.writeInt32(decryptHandle->decryptApiType);
+ data.writeInt32(decryptHandle->status);
+
+ if (NULL != decryptHandle->decryptInfo) {
+ data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+ } else {
+ data.writeInt32(INVALID_BUFFER_LENGTH);
+ }
+
+ data.writeInt32(decryptUnitId);
+ data.writeInt32((*decBuffer)->length);
+
+ data.writeInt32(encBuffer->length);
+ data.write(encBuffer->data, encBuffer->length);
+
+ if (NULL != IV) {
+ data.writeInt32(IV->length);
+ data.write(IV->data, IV->length);
+ }
+
+ remote()->transact(DECRYPT, data, &reply);
+
+ const status_t status = reply.readInt32();
+ LOGV("Return value of decrypt() is %d", status);
+
+ const int size = reply.readInt32();
+ (*decBuffer)->length = size;
+ reply.read((void *)(*decBuffer)->data, size);
+
+ return status;
+}
+
+status_t BpDrmManagerService::finalizeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+ LOGV("finalizeDecryptUnit");
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ data.writeInt32(decryptHandle->decryptId);
+ data.writeString8(decryptHandle->mimeType);
+ data.writeInt32(decryptHandle->decryptApiType);
+ data.writeInt32(decryptHandle->status);
+
+ if (NULL != decryptHandle->decryptInfo) {
+ data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+ } else {
+ data.writeInt32(INVALID_BUFFER_LENGTH);
+ }
+
+ data.writeInt32(decryptUnitId);
+
+ remote()->transact(FINALIZE_DECRYPT_UNIT, data, &reply);
+ return reply.readInt32();
+}
+
+ssize_t BpDrmManagerService::pread(
+ int uniqueId, DecryptHandle* decryptHandle, void* buffer,
+ ssize_t numBytes, off_t offset) {
+ LOGV("read");
+ Parcel data, reply;
+ int result;
+
+ data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+ data.writeInt32(uniqueId);
+
+ data.writeInt32(decryptHandle->decryptId);
+ data.writeString8(decryptHandle->mimeType);
+ data.writeInt32(decryptHandle->decryptApiType);
+ data.writeInt32(decryptHandle->status);
+
+ if (NULL != decryptHandle->decryptInfo) {
+ data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+ } else {
+ data.writeInt32(INVALID_BUFFER_LENGTH);
+ }
+
+ data.writeInt32(numBytes);
+ data.writeInt32(offset);
+
+ remote()->transact(PREAD, data, &reply);
+ result = reply.readInt32();
+ if (0 < result) {
+ reply.read(buffer, result);
+ }
+ return result;
+}
+
+IMPLEMENT_META_INTERFACE(DrmManagerService, "drm.IDrmManagerService");
+
+status_t BnDrmManagerService::onTransact(
+ uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ LOGV("Entering BnDrmManagerService::onTransact with code %d", code);
+
+ switch (code) {
+ case ADD_UNIQUEID:
+ {
+ LOGV("BnDrmManagerService::onTransact :ADD_UNIQUEID");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+ int uniqueId = addUniqueId(data.readInt32());
+ reply->writeInt32(uniqueId);
+ return DRM_NO_ERROR;
+ }
+
+ case REMOVE_UNIQUEID:
+ {
+ LOGV("BnDrmManagerService::onTransact :REMOVE_UNIQUEID");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+ removeUniqueId(data.readInt32());
+ return DRM_NO_ERROR;
+ }
+
+ case ADD_CLIENT:
+ {
+ LOGV("BnDrmManagerService::onTransact :ADD_CLIENT");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+ addClient(data.readInt32());
+ return DRM_NO_ERROR;
+ }
+
+ case REMOVE_CLIENT:
+ {
+ LOGV("BnDrmManagerService::onTransact :REMOVE_CLIENT");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+ removeClient(data.readInt32());
+ return DRM_NO_ERROR;
+ }
+
+ case SET_DRM_SERVICE_LISTENER:
+ {
+ LOGV("BnDrmManagerService::onTransact :SET_DRM_SERVICE_LISTENER");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+ const sp<IDrmServiceListener> drmServiceListener
+ = interface_cast<IDrmServiceListener> (data.readStrongBinder());
+
+ status_t status = setDrmServiceListener(uniqueId, drmServiceListener);
+
+ reply->writeInt32(status);
+ return DRM_NO_ERROR;
+ }
+
+ case INSTALL_DRM_ENGINE:
+ {
+ LOGV("BnDrmManagerService::onTransact :INSTALL_DRM_ENGINE");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ status_t status = installDrmEngine(data.readInt32(), data.readString8());
+
+ reply->writeInt32(status);
+ return DRM_NO_ERROR;
+ }
+
+ case GET_CONSTRAINTS_FROM_CONTENT:
+ {
+ LOGV("BnDrmManagerService::onTransact :GET_CONSTRAINTS_FROM_CONTENT");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+ const String8 path = data.readString8();
+
+ DrmConstraints* drmConstraints = getConstraints(uniqueId, &path, data.readInt32());
+
+ if (NULL != drmConstraints) {
+ //Filling DRM Constraints contents
+ reply->writeInt32(drmConstraints->getCount());
+
+ DrmConstraints::KeyIterator keyIt = drmConstraints->keyIterator();
+ while (keyIt.hasNext()) {
+ const String8 key = keyIt.next();
+ reply->writeString8(key);
+ const char* value = drmConstraints->getAsByteArray(&key);
+ int bufferSize = 0;
+ if (NULL != value) {
+ bufferSize = strlen(value);
+ }
+ reply->writeInt32(bufferSize + 1);
+ reply->write(value, bufferSize + 1);
+ }
+ }
+ delete drmConstraints; drmConstraints = NULL;
+ return DRM_NO_ERROR;
+ }
+
+ case GET_METADATA_FROM_CONTENT:
+ {
+ LOGV("BnDrmManagerService::onTransact :GET_METADATA_FROM_CONTENT");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+ const String8 path = data.readString8();
+
+ DrmMetadata* drmMetadata = getMetadata(uniqueId, &path);
+ if (NULL != drmMetadata) {
+ //Filling DRM Metadata contents
+ reply->writeInt32(drmMetadata->getCount());
+
+ DrmMetadata::KeyIterator keyIt = drmMetadata->keyIterator();
+ while (keyIt.hasNext()) {
+ const String8 key = keyIt.next();
+ reply->writeString8(key);
+ const char* value = drmMetadata->getAsByteArray(&key);
+ int bufferSize = 0;
+ if (NULL != value) {
+ bufferSize = strlen(value);
+ reply->writeInt32(bufferSize + 1);
+ reply->write(value, bufferSize + 1);
+ } else {
+ reply->writeInt32(0);
+ }
+ }
+ }
+ delete drmMetadata; drmMetadata = NULL;
+ return NO_ERROR;
+ }
+
+ case CAN_HANDLE:
+ {
+ LOGV("BnDrmManagerService::onTransact :CAN_HANDLE");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+ const String8 path = data.readString8();
+ const String8 mimeType = data.readString8();
+
+ bool result = canHandle(uniqueId, path, mimeType);
+
+ reply->writeInt32(result);
+ return DRM_NO_ERROR;
+ }
+
+ case PROCESS_DRM_INFO:
+ {
+ LOGV("BnDrmManagerService::onTransact :PROCESS_DRM_INFO");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+
+ //Filling DRM info
+ const int infoType = data.readInt32();
+ const int bufferSize = data.readInt32();
+ char* buffer = NULL;
+ if (0 < bufferSize) {
+ buffer = (char *)data.readInplace(bufferSize);
+ }
+ const DrmBuffer drmBuffer(buffer, bufferSize);
+ DrmInfo* drmInfo = new DrmInfo(infoType, drmBuffer, data.readString8());
+
+ const int size = data.readInt32();
+ for (int index = 0; index < size; ++index) {
+ const String8 key(data.readString8());
+ const String8 value(data.readString8());
+ drmInfo->put(key, (value == String8("NULL")) ? String8("") : value);
+ }
+
+ DrmInfoStatus* drmInfoStatus = processDrmInfo(uniqueId, drmInfo);
+
+ if (NULL != drmInfoStatus) {
+ //Filling DRM Info Status contents
+ reply->writeInt32(drmInfoStatus->statusCode);
+ reply->writeInt32(drmInfoStatus->infoType);
+ reply->writeString8(drmInfoStatus->mimeType);
+
+ if (NULL != drmInfoStatus->drmBuffer) {
+ const DrmBuffer* drmBuffer = drmInfoStatus->drmBuffer;
+ const int bufferSize = drmBuffer->length;
+ reply->writeInt32(bufferSize);
+ if (0 < bufferSize) {
+ reply->write(drmBuffer->data, bufferSize);
+ }
+ delete [] drmBuffer->data;
+ delete drmBuffer; drmBuffer = NULL;
+ }
+ }
+ delete drmInfo; drmInfo = NULL;
+ delete drmInfoStatus; drmInfoStatus = NULL;
+ return DRM_NO_ERROR;
+ }
+
+ case ACQUIRE_DRM_INFO:
+ {
+ LOGV("BnDrmManagerService::onTransact :ACQUIRE_DRM_INFO");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+
+ //Filling DRM info Request
+ DrmInfoRequest* drmInfoRequest = new DrmInfoRequest(data.readInt32(), data.readString8());
+
+ const int size = data.readInt32();
+ for (int index = 0; index < size; ++index) {
+ const String8 key(data.readString8());
+ const String8 value(data.readString8());
+ drmInfoRequest->put(key, (value == String8("NULL")) ? String8("") : value);
+ }
+
+ DrmInfo* drmInfo = acquireDrmInfo(uniqueId, drmInfoRequest);
+
+ if (NULL != drmInfo) {
+ //Filling DRM Info
+ const DrmBuffer drmBuffer = drmInfo->getData();
+ reply->writeInt32(drmInfo->getInfoType());
+
+ const int bufferSize = drmBuffer.length;
+ reply->writeInt32(bufferSize);
+ if (0 < bufferSize) {
+ reply->write(drmBuffer.data, bufferSize);
+ }
+ reply->writeString8(drmInfo->getMimeType());
+ reply->writeInt32(drmInfo->getCount());
+
+ DrmInfo::KeyIterator keyIt = drmInfo->keyIterator();
+ while (keyIt.hasNext()) {
+ const String8 key = keyIt.next();
+ reply->writeString8(key);
+ const String8 value = drmInfo->get(key);
+ reply->writeString8((value == String8("")) ? String8("NULL") : value);
+ }
+ delete [] drmBuffer.data;
+ }
+ delete drmInfoRequest; drmInfoRequest = NULL;
+ delete drmInfo; drmInfo = NULL;
+ return DRM_NO_ERROR;
+ }
+
+ case SAVE_RIGHTS:
+ {
+ LOGV("BnDrmManagerService::onTransact :SAVE_RIGHTS");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+
+ //Filling DRM Rights
+ const int bufferSize = data.readInt32();
+ const DrmBuffer drmBuffer((char *)data.readInplace(bufferSize), bufferSize);
+
+ const String8 mimeType(data.readString8());
+ const String8 accountId(data.readString8());
+ const String8 subscriptionId(data.readString8());
+ const String8 rightsPath(data.readString8());
+ const String8 contentPath(data.readString8());
+
+ DrmRights drmRights(drmBuffer,
+ ((mimeType == String8("NULL")) ? String8("") : mimeType),
+ ((accountId == String8("NULL")) ? String8("") : accountId),
+ ((subscriptionId == String8("NULL")) ? String8("") : subscriptionId));
+
+ const status_t status = saveRights(uniqueId, drmRights,
+ ((rightsPath == String8("NULL")) ? String8("") : rightsPath),
+ ((contentPath == String8("NULL")) ? String8("") : contentPath));
+
+ reply->writeInt32(status);
+ return DRM_NO_ERROR;
+ }
+
+ case GET_ORIGINAL_MIMETYPE:
+ {
+ LOGV("BnDrmManagerService::onTransact :GET_ORIGINAL_MIMETYPE");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const String8 originalMimeType = getOriginalMimeType(data.readInt32(), data.readString8());
+
+ reply->writeString8(originalMimeType);
+ return DRM_NO_ERROR;
+ }
+
+ case GET_DRM_OBJECT_TYPE:
+ {
+ LOGV("BnDrmManagerService::onTransact :GET_DRM_OBJECT_TYPE");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int drmObjectType
+ = getDrmObjectType(data.readInt32(), data.readString8(), data.readString8());
+
+ reply->writeInt32(drmObjectType);
+ return DRM_NO_ERROR;
+ }
+
+ case CHECK_RIGHTS_STATUS:
+ {
+ LOGV("BnDrmManagerService::onTransact :CHECK_RIGHTS_STATUS");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int result
+ = checkRightsStatus(data.readInt32(), data.readString8(), data.readInt32());
+
+ reply->writeInt32(result);
+ return DRM_NO_ERROR;
+ }
+
+ case CONSUME_RIGHTS:
+ {
+ LOGV("BnDrmManagerService::onTransact :CONSUME_RIGHTS");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+
+ DecryptHandle handle;
+ handle.decryptId = data.readInt32();
+ handle.mimeType = data.readString8();
+ handle.decryptApiType = data.readInt32();
+ handle.status = data.readInt32();
+ handle.decryptInfo = NULL;
+
+ const int bufferLength = data.readInt32();
+ if (INVALID_BUFFER_LENGTH != bufferLength) {
+ handle.decryptInfo = new DecryptInfo();
+ handle.decryptInfo->decryptBufferLength = bufferLength;
+ }
+
+ const status_t status
+ = consumeRights(uniqueId, &handle, data.readInt32(),
+ static_cast<bool>(data.readInt32()));
+ reply->writeInt32(status);
+
+ delete handle.decryptInfo; handle.decryptInfo = NULL;
+ return DRM_NO_ERROR;
+ }
+
+ case SET_PLAYBACK_STATUS:
+ {
+ LOGV("BnDrmManagerService::onTransact :SET_PLAYBACK_STATUS");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+
+ DecryptHandle handle;
+ handle.decryptId = data.readInt32();
+ handle.mimeType = data.readString8();
+ handle.decryptApiType = data.readInt32();
+ handle.status = data.readInt32();
+ handle.decryptInfo = NULL;
+
+ const int bufferLength = data.readInt32();
+ if (INVALID_BUFFER_LENGTH != bufferLength) {
+ handle.decryptInfo = new DecryptInfo();
+ handle.decryptInfo->decryptBufferLength = bufferLength;
+ }
+
+ const status_t status
+ = setPlaybackStatus(uniqueId, &handle, data.readInt32(), data.readInt32());
+ reply->writeInt32(status);
+
+ delete handle.decryptInfo; handle.decryptInfo = NULL;
+ return DRM_NO_ERROR;
+ }
+
+ case VALIDATE_ACTION:
+ {
+ LOGV("BnDrmManagerService::onTransact :VALIDATE_ACTION");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ bool result = validateAction(
+ data.readInt32(),
+ data.readString8(),
+ data.readInt32(),
+ ActionDescription(data.readInt32(), data.readInt32()));
+
+ reply->writeInt32(result);
+ return DRM_NO_ERROR;
+ }
+
+ case REMOVE_RIGHTS:
+ {
+ LOGV("BnDrmManagerService::onTransact :REMOVE_RIGHTS");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const status_t status = removeRights(data.readInt32(), data.readString8());
+ reply->writeInt32(status);
+
+ return DRM_NO_ERROR;
+ }
+
+ case REMOVE_ALL_RIGHTS:
+ {
+ LOGV("BnDrmManagerService::onTransact :REMOVE_ALL_RIGHTS");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const status_t status = removeAllRights(data.readInt32());
+ reply->writeInt32(status);
+
+ return DRM_NO_ERROR;
+ }
+
+ case OPEN_CONVERT_SESSION:
+ {
+ LOGV("BnDrmManagerService::onTransact :OPEN_CONVERT_SESSION");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int convertId = openConvertSession(data.readInt32(), data.readString8());
+
+ reply->writeInt32(convertId);
+ return DRM_NO_ERROR;
+ }
+
+ case CONVERT_DATA:
+ {
+ LOGV("BnDrmManagerService::onTransact :CONVERT_DATA");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+ const int convertId = data.readInt32();
+
+ //Filling input data
+ const int bufferSize = data.readInt32();
+ DrmBuffer* inputData = new DrmBuffer((char *)data.readInplace(bufferSize), bufferSize);
+
+ DrmConvertedStatus* drmConvertedStatus = convertData(uniqueId, convertId, inputData);
+
+ if (NULL != drmConvertedStatus) {
+ //Filling Drm Converted Ststus
+ reply->writeInt32(drmConvertedStatus->statusCode);
+ reply->writeInt32(drmConvertedStatus->offset);
+
+ if (NULL != drmConvertedStatus->convertedData) {
+ const DrmBuffer* convertedData = drmConvertedStatus->convertedData;
+ const int bufferSize = convertedData->length;
+ reply->writeInt32(bufferSize);
+ if (0 < bufferSize) {
+ reply->write(convertedData->data, bufferSize);
+ }
+ delete [] convertedData->data;
+ delete convertedData; convertedData = NULL;
+ }
+ }
+ delete inputData; inputData = NULL;
+ delete drmConvertedStatus; drmConvertedStatus = NULL;
+ return DRM_NO_ERROR;
+ }
+
+ case CLOSE_CONVERT_SESSION:
+ {
+ LOGV("BnDrmManagerService::onTransact :CLOSE_CONVERT_SESSION");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ DrmConvertedStatus* drmConvertedStatus
+ = closeConvertSession(data.readInt32(), data.readInt32());
+
+ if (NULL != drmConvertedStatus) {
+ //Filling Drm Converted Ststus
+ reply->writeInt32(drmConvertedStatus->statusCode);
+ reply->writeInt32(drmConvertedStatus->offset);
+
+ if (NULL != drmConvertedStatus->convertedData) {
+ const DrmBuffer* convertedData = drmConvertedStatus->convertedData;
+ const int bufferSize = convertedData->length;
+ reply->writeInt32(bufferSize);
+ if (0 < bufferSize) {
+ reply->write(convertedData->data, bufferSize);
+ }
+ delete [] convertedData->data;
+ delete convertedData; convertedData = NULL;
+ }
+ }
+ delete drmConvertedStatus; drmConvertedStatus = NULL;
+ return DRM_NO_ERROR;
+ }
+
+ case GET_ALL_SUPPORT_INFO:
+ {
+ LOGV("BnDrmManagerService::onTransact :GET_ALL_SUPPORT_INFO");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+ int length = 0;
+ DrmSupportInfo* drmSupportInfoArray = NULL;
+
+ status_t status = getAllSupportInfo(uniqueId, &length, &drmSupportInfoArray);
+
+ reply->writeInt32(length);
+ for (int i = 0; i < length; ++i) {
+ DrmSupportInfo drmSupportInfo = drmSupportInfoArray[i];
+
+ reply->writeInt32(drmSupportInfo.getFileSuffixCount());
+ DrmSupportInfo::FileSuffixIterator fileSuffixIt
+ = drmSupportInfo.getFileSuffixIterator();
+ while (fileSuffixIt.hasNext()) {
+ reply->writeString8(fileSuffixIt.next());
+ }
+
+ reply->writeInt32(drmSupportInfo.getMimeTypeCount());
+ DrmSupportInfo::MimeTypeIterator mimeTypeIt = drmSupportInfo.getMimeTypeIterator();
+ while (mimeTypeIt.hasNext()) {
+ reply->writeString8(mimeTypeIt.next());
+ }
+ reply->writeString8(drmSupportInfo.getDescription());
+ }
+ delete [] drmSupportInfoArray; drmSupportInfoArray = NULL;
+ reply->writeInt32(status);
+ return DRM_NO_ERROR;
+ }
+
+ case OPEN_DECRYPT_SESSION:
+ {
+ LOGV("BnDrmManagerService::onTransact :OPEN_DECRYPT_SESSION");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+ const int fd = data.readFileDescriptor();
+
+ DecryptHandle* handle
+ = openDecryptSession(uniqueId, fd, data.readInt32(), data.readInt32());
+
+ if (NULL != handle) {
+ reply->writeInt32(handle->decryptId);
+ reply->writeString8(handle->mimeType);
+ reply->writeInt32(handle->decryptApiType);
+ reply->writeInt32(handle->status);
+ if (NULL != handle->decryptInfo) {
+ reply->writeInt32(handle->decryptInfo->decryptBufferLength);
+ delete handle->decryptInfo; handle->decryptInfo = NULL;
+ }
+ } else {
+ LOGE("NULL decryptHandle is returned");
+ }
+ delete handle; handle = NULL;
+ return DRM_NO_ERROR;
+ }
+
+ case OPEN_DECRYPT_SESSION_FROM_URI:
+ {
+ LOGV("BnDrmManagerService::onTransact :OPEN_DECRYPT_SESSION_FROM_URI");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+ const String8 uri = data.readString8();
+
+ DecryptHandle* handle = openDecryptSession(uniqueId, uri.string());
+
+ if (NULL != handle) {
+ reply->writeInt32(handle->decryptId);
+ reply->writeString8(handle->mimeType);
+ reply->writeInt32(handle->decryptApiType);
+ reply->writeInt32(handle->status);
+ if (NULL != handle->decryptInfo) {
+ reply->writeInt32(handle->decryptInfo->decryptBufferLength);
+ delete handle->decryptInfo; handle->decryptInfo = NULL;
+ }
+ } else {
+ LOGE("NULL decryptHandle is returned");
+ }
+ delete handle; handle = NULL;
+ return DRM_NO_ERROR;
+ }
+
+ case CLOSE_DECRYPT_SESSION:
+ {
+ LOGV("BnDrmManagerService::onTransact :CLOSE_DECRYPT_SESSION");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+
+ DecryptHandle* handle = new DecryptHandle();
+ handle->decryptId = data.readInt32();
+ handle->mimeType = data.readString8();
+ handle->decryptApiType = data.readInt32();
+ handle->status = data.readInt32();
+ handle->decryptInfo = NULL;
+
+ const int bufferLength = data.readInt32();
+ if (INVALID_BUFFER_LENGTH != bufferLength) {
+ handle->decryptInfo = new DecryptInfo();
+ handle->decryptInfo->decryptBufferLength = bufferLength;
+ }
+
+ const status_t status = closeDecryptSession(uniqueId, handle);
+ reply->writeInt32(status);
+ return DRM_NO_ERROR;
+ }
+
+ case INITIALIZE_DECRYPT_UNIT:
+ {
+ LOGV("BnDrmManagerService::onTransact :INITIALIZE_DECRYPT_UNIT");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+
+ DecryptHandle handle;
+ handle.decryptId = data.readInt32();
+ handle.mimeType = data.readString8();
+ handle.decryptApiType = data.readInt32();
+ handle.status = data.readInt32();
+ handle.decryptInfo = NULL;
+
+ const int bufferLength = data.readInt32();
+ if (INVALID_BUFFER_LENGTH != bufferLength) {
+ handle.decryptInfo = new DecryptInfo();
+ handle.decryptInfo->decryptBufferLength = bufferLength;
+ }
+ const int decryptUnitId = data.readInt32();
+
+ //Filling Header info
+ const int bufferSize = data.readInt32();
+ DrmBuffer* headerInfo = NULL;
+ headerInfo = new DrmBuffer((char *)data.readInplace(bufferSize), bufferSize);
+
+ const status_t status
+ = initializeDecryptUnit(uniqueId, &handle, decryptUnitId, headerInfo);
+ reply->writeInt32(status);
+
+ delete handle.decryptInfo; handle.decryptInfo = NULL;
+ delete headerInfo; headerInfo = NULL;
+ return DRM_NO_ERROR;
+ }
+
+ case DECRYPT:
+ {
+ LOGV("BnDrmManagerService::onTransact :DECRYPT");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+
+ DecryptHandle handle;
+ handle.decryptId = data.readInt32();
+ handle.mimeType = data.readString8();
+ handle.decryptApiType = data.readInt32();
+ handle.status = data.readInt32();
+ handle.decryptInfo = NULL;
+
+ const int bufferLength = data.readInt32();
+ if (INVALID_BUFFER_LENGTH != bufferLength) {
+ handle.decryptInfo = new DecryptInfo();
+ handle.decryptInfo->decryptBufferLength = bufferLength;
+ }
+ const int decryptUnitId = data.readInt32();
+ const int decBufferSize = data.readInt32();
+
+ const int encBufferSize = data.readInt32();
+ DrmBuffer* encBuffer
+ = new DrmBuffer((char *)data.readInplace(encBufferSize), encBufferSize);
+
+ char* buffer = NULL;
+ buffer = new char[decBufferSize];
+ DrmBuffer* decBuffer = new DrmBuffer(buffer, decBufferSize);
+
+ DrmBuffer* IV = NULL;
+ if (0 != data.dataAvail()) {
+ const int ivBufferlength = data.readInt32();
+ IV = new DrmBuffer((char *)data.readInplace(ivBufferlength), ivBufferlength);
+ }
+
+ const status_t status
+ = decrypt(uniqueId, &handle, decryptUnitId, encBuffer, &decBuffer, IV);
+
+ reply->writeInt32(status);
+
+ const int size = decBuffer->length;
+ reply->writeInt32(size);
+ reply->write(decBuffer->data, size);
+
+ delete handle.decryptInfo; handle.decryptInfo = NULL;
+ delete encBuffer; encBuffer = NULL;
+ delete decBuffer; decBuffer = NULL;
+ delete [] buffer; buffer = NULL;
+ delete IV; IV = NULL;
+ return DRM_NO_ERROR;
+ }
+
+ case FINALIZE_DECRYPT_UNIT:
+ {
+ LOGV("BnDrmManagerService::onTransact :FINALIZE_DECRYPT_UNIT");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+
+ DecryptHandle handle;
+ handle.decryptId = data.readInt32();
+ handle.mimeType = data.readString8();
+ handle.decryptApiType = data.readInt32();
+ handle.status = data.readInt32();
+ handle.decryptInfo = NULL;
+
+ const int bufferLength = data.readInt32();
+ if (INVALID_BUFFER_LENGTH != bufferLength) {
+ handle.decryptInfo = new DecryptInfo();
+ handle.decryptInfo->decryptBufferLength = bufferLength;
+ }
+
+ const status_t status = finalizeDecryptUnit(uniqueId, &handle, data.readInt32());
+ reply->writeInt32(status);
+
+ delete handle.decryptInfo; handle.decryptInfo = NULL;
+ return DRM_NO_ERROR;
+ }
+
+ case PREAD:
+ {
+ LOGV("BnDrmManagerService::onTransact :READ");
+ CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+ const int uniqueId = data.readInt32();
+
+ DecryptHandle handle;
+ handle.decryptId = data.readInt32();
+ handle.mimeType = data.readString8();
+ handle.decryptApiType = data.readInt32();
+ handle.status = data.readInt32();
+ handle.decryptInfo = NULL;
+
+ const int bufferLength = data.readInt32();
+ if (INVALID_BUFFER_LENGTH != bufferLength) {
+ handle.decryptInfo = new DecryptInfo();
+ handle.decryptInfo->decryptBufferLength = bufferLength;
+ }
+
+ const int numBytes = data.readInt32();
+ char* buffer = new char[numBytes];
+
+ const off_t offset = data.readInt32();
+
+ ssize_t result = pread(uniqueId, &handle, buffer, numBytes, offset);
+ reply->writeInt32(result);
+ if (0 < result) {
+ reply->write(buffer, result);
+ }
+
+ delete handle.decryptInfo; handle.decryptInfo = NULL;
+ delete [] buffer, buffer = NULL;
+ return DRM_NO_ERROR;
+ }
+
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
diff --git a/drm/common/IDrmServiceListener.cpp b/drm/common/IDrmServiceListener.cpp
new file mode 100644
index 0000000..6eeea40
--- /dev/null
+++ b/drm/common/IDrmServiceListener.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <drm/drm_framework_common.h>
+#include <drm/DrmInfoEvent.h>
+#include "IDrmServiceListener.h"
+
+using namespace android;
+
+status_t BpDrmServiceListener::notify(const DrmInfoEvent& event) {
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IDrmServiceListener::getInterfaceDescriptor());
+ data.writeInt32(event.getUniqueId());
+ data.writeInt32(event.getType());
+ data.writeString8(event.getMessage());
+
+ remote()->transact(NOTIFY, data, &reply);
+ return reply.readInt32();
+}
+
+IMPLEMENT_META_INTERFACE(DrmServiceListener, "drm.IDrmServiceListener");
+
+status_t BnDrmServiceListener::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+
+ switch (code) {
+ case NOTIFY:
+ {
+ CHECK_INTERFACE(IDrmServiceListener, data, reply);
+ int uniqueId = data.readInt32();
+ int type = data.readInt32();
+ const String8& message = data.readString8();
+
+ status_t status = notify(DrmInfoEvent(uniqueId, type, message));
+ reply->writeInt32(status);
+
+ return DRM_NO_ERROR;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
diff --git a/drm/common/ReadWriteUtils.cpp b/drm/common/ReadWriteUtils.cpp
new file mode 100644
index 0000000..7ec4fa2
--- /dev/null
+++ b/drm/common/ReadWriteUtils.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ReadWriteUtils"
+#include <utils/Log.h>
+
+#include <ReadWriteUtils.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <utils/String8.h>
+
+using namespace android;
+
+#define FAILURE -1
+
+String8 ReadWriteUtils::readBytes(const String8& filePath) {
+ FILE* file = NULL;
+ file = fopen(filePath.string(), "r");
+
+ String8 string("");
+ if (NULL != file) {
+ int fd = fileno(file);
+ struct stat sb;
+
+ if (fstat(fd, &sb) == 0 && sb.st_size > 0) {
+ int length = sb.st_size;
+ char* bytes = new char[length];
+ if (length == read(fd, (void*) bytes, length)) {
+ string.append(bytes, length);
+ }
+ delete bytes;
+ }
+ fclose(file);
+ }
+ return string;
+}
+
+int ReadWriteUtils::readBytes(const String8& filePath, char** buffer) {
+ FILE* file = NULL;
+ file = fopen(filePath.string(), "r");
+ int length = 0;
+
+ if (NULL != file) {
+ int fd = fileno(file);
+ struct stat sb;
+
+ if (fstat(fd, &sb) == 0 && sb.st_size > 0) {
+ length = sb.st_size;
+ *buffer = new char[length];
+ if (length != read(fd, (void*) *buffer, length)) {
+ length = FAILURE;
+ }
+ }
+ fclose(file);
+ }
+ return length;
+}
+
+void ReadWriteUtils::writeToFile(const String8& filePath, const String8& data) {
+ FILE* file = NULL;
+ file = fopen(filePath.string(), "w+");
+
+ if (NULL != file) {
+ int fd = fileno(file);
+
+ int size = data.size();
+ if (FAILURE != ftruncate(fd, size)) {
+ if (size != write(fd, data.string(), size)) {
+ LOGE("Failed to write the data to: %s", filePath.string());
+ }
+ }
+ fclose(file);
+ }
+}
+
+void ReadWriteUtils::appendToFile(const String8& filePath, const String8& data) {
+ FILE* file = NULL;
+ file = fopen(filePath.string(), "a+");
+
+ if (NULL != file) {
+ int fd = fileno(file);
+
+ int size = data.size();
+ if (size != write(fd, data.string(), size)) {
+ LOGE("Failed to write the data to: %s", filePath.string());
+ }
+ fclose(file);
+ }
+}
+
diff --git a/drm/drmioserver/Android.mk b/drm/drmioserver/Android.mk
new file mode 100644
index 0000000..11571c7
--- /dev/null
+++ b/drm/drmioserver/Android.mk
@@ -0,0 +1,43 @@
+#
+# 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:= \
+ main_drmioserver.cpp \
+ DrmIOService.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ libbinder
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_STATIC_LIBRARIES := libdrmframeworkcommon
+
+LOCAL_C_INCLUDES := \
+ $(TOP)/frameworks/base/drm/libdrmframework/include \
+ $(TOP)/frameworks/base/include
+
+LOCAL_MODULE:= drmioserver
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/drm/drmioserver/DrmIOService.cpp b/drm/drmioserver/DrmIOService.cpp
new file mode 100644
index 0000000..60e6e70
--- /dev/null
+++ b/drm/drmioserver/DrmIOService.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "DrmIOService"
+#include <utils/Log.h>
+
+#include <binder/IServiceManager.h>
+#include "DrmIOService.h"
+#include "ReadWriteUtils.h"
+
+using namespace android;
+
+void DrmIOService::instantiate() {
+ LOGV("instantiate");
+ defaultServiceManager()->addService(String16("drm.drmIOService"), new DrmIOService());
+}
+
+DrmIOService::DrmIOService() {
+ LOGV("created");
+}
+
+DrmIOService::~DrmIOService() {
+ LOGV("Destroyed");
+}
+
+void DrmIOService::writeToFile(const String8& filePath, const String8& dataBuffer) {
+ LOGV("Entering writeToFile");
+ ReadWriteUtils::writeToFile(filePath, dataBuffer);
+}
+
+String8 DrmIOService::readFromFile(const String8& filePath) {
+ LOGV("Entering readFromFile");
+ return ReadWriteUtils::readBytes(filePath);
+}
+
diff --git a/drm/drmioserver/main_drmioserver.cpp b/drm/drmioserver/main_drmioserver.cpp
new file mode 100644
index 0000000..7ed048d
--- /dev/null
+++ b/drm/drmioserver/main_drmioserver.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 <sys/types.h>
+#include <unistd.h>
+#include <grp.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+#include <private/android_filesystem_config.h>
+
+#include <DrmIOService.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+ sp<ProcessState> proc(ProcessState::self());
+ sp<IServiceManager> sm = defaultServiceManager();
+ LOGI("ServiceManager: %p", sm.get());
+ DrmIOService::instantiate();
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+}
+
diff --git a/drm/drmserver/Android.mk b/drm/drmserver/Android.mk
new file mode 100644
index 0000000..5df2ff8
--- /dev/null
+++ b/drm/drmserver/Android.mk
@@ -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.
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ main_drmserver.cpp \
+ DrmManager.cpp \
+ DrmManagerService.cpp \
+ StringTokenizer.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ libbinder
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_STATIC_LIBRARIES := libdrmframeworkcommon
+
+LOCAL_C_INCLUDES := \
+ $(TOP)/frameworks/base/include \
+ $(TOP)/frameworks/base/drm/libdrmframework/include \
+ $(TOP)/frameworks/base/drm/libdrmframework/plugins/common/include
+
+LOCAL_MODULE:= drmserver
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
new file mode 100644
index 0000000..49df1c8
--- /dev/null
+++ b/drm/drmserver/DrmManager.cpp
@@ -0,0 +1,557 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "DrmManager(Native)"
+#include "utils/Log.h"
+
+#include <utils/String8.h>
+#include <drm/DrmInfo.h>
+#include <drm/DrmInfoEvent.h>
+#include <drm/DrmRights.h>
+#include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
+#include <drm/DrmInfoStatus.h>
+#include <drm/DrmInfoRequest.h>
+#include <drm/DrmSupportInfo.h>
+#include <drm/DrmConvertedStatus.h>
+#include <IDrmEngine.h>
+
+#include "DrmManager.h"
+#include "ReadWriteUtils.h"
+
+#define DECRYPT_FILE_ERROR -1
+
+using namespace android;
+
+Vector<int> DrmManager::mUniqueIdVector;
+const String8 DrmManager::EMPTY_STRING("");
+
+DrmManager::DrmManager() :
+ mDecryptSessionId(0),
+ mConvertId(0) {
+
+}
+
+DrmManager::~DrmManager() {
+
+}
+
+int DrmManager::addUniqueId(int uniqueId) {
+ if (0 == uniqueId) {
+ int temp = 0;
+ bool foundUniqueId = false;
+ srand(time(NULL));
+
+ while (!foundUniqueId) {
+ const int size = mUniqueIdVector.size();
+ temp = rand() % 100;
+
+ int index = 0;
+ for (; index < size; ++index) {
+ if (mUniqueIdVector.itemAt(index) == temp) {
+ foundUniqueId = false;
+ break;
+ }
+ }
+ if (index == size) {
+ foundUniqueId = true;
+ }
+ }
+ uniqueId = temp;
+ }
+ mUniqueIdVector.push(uniqueId);
+ return uniqueId;
+}
+
+void DrmManager::removeUniqueId(int uniqueId) {
+ for (unsigned int i = 0; i < mUniqueIdVector.size(); i++) {
+ if (uniqueId == mUniqueIdVector.itemAt(i)) {
+ mUniqueIdVector.removeAt(i);
+ break;
+ }
+ }
+}
+
+status_t DrmManager::loadPlugIns() {
+ String8 pluginDirPath("/system/lib/drm/plugins/native");
+ return loadPlugIns(pluginDirPath);
+}
+
+status_t DrmManager::loadPlugIns(const String8& plugInDirPath) {
+ if (mSupportInfoToPlugInIdMap.isEmpty()) {
+ mPlugInManager.loadPlugIns(plugInDirPath);
+ Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();
+ for (unsigned int i = 0; i < plugInPathList.size(); ++i) {
+ String8 plugInPath = plugInPathList[i];
+ DrmSupportInfo* info = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(0);
+ if (NULL != info) {
+ mSupportInfoToPlugInIdMap.add(*info, plugInPath);
+ }
+ }
+ }
+ return DRM_NO_ERROR;
+}
+
+status_t DrmManager::unloadPlugIns() {
+ mConvertSessionMap.clear();
+ mDecryptSessionMap.clear();
+ mPlugInManager.unloadPlugIns();
+ mSupportInfoToPlugInIdMap.clear();
+ return DRM_NO_ERROR;
+}
+
+status_t DrmManager::setDrmServiceListener(
+ int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) {
+ Mutex::Autolock _l(mLock);
+ if (NULL != drmServiceListener.get()) {
+ mServiceListeners.add(uniqueId, drmServiceListener);
+ } else {
+ mServiceListeners.removeItem(uniqueId);
+ }
+ return DRM_NO_ERROR;
+}
+
+void DrmManager::addClient(int uniqueId) {
+ if (!mSupportInfoToPlugInIdMap.isEmpty()) {
+ Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
+ for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
+ rDrmEngine.initialize(uniqueId);
+ rDrmEngine.setOnInfoListener(uniqueId, this);
+ }
+ }
+}
+
+void DrmManager::removeClient(int uniqueId) {
+ Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
+ for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
+ rDrmEngine.terminate(uniqueId);
+ }
+}
+
+DrmConstraints* DrmManager::getConstraints(int uniqueId, const String8* path, const int action) {
+ const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
+ if (EMPTY_STRING != plugInId) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ return rDrmEngine.getConstraints(uniqueId, path, action);
+ }
+ return NULL;
+}
+
+DrmMetadata* DrmManager::getMetadata(int uniqueId, const String8* path) {
+ const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
+ if (EMPTY_STRING != plugInId) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ return rDrmEngine.getMetadata(uniqueId, path);
+ }
+ return NULL;
+}
+
+status_t DrmManager::installDrmEngine(int uniqueId, const String8& absolutePath) {
+ mPlugInManager.loadPlugIn(absolutePath);
+
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(absolutePath);
+ rDrmEngine.initialize(uniqueId);
+ rDrmEngine.setOnInfoListener(uniqueId, this);
+
+ DrmSupportInfo* info = rDrmEngine.getSupportInfo(0);
+ mSupportInfoToPlugInIdMap.add(*info, absolutePath);
+
+ return DRM_NO_ERROR;
+}
+
+bool DrmManager::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
+ const String8 plugInId = getSupportedPlugInId(mimeType);
+ bool result = (EMPTY_STRING != plugInId) ? true : false;
+
+ if (0 < path.length()) {
+ if (result) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ result = rDrmEngine.canHandle(uniqueId, path);
+ } else {
+ result = canHandle(uniqueId, path);
+ }
+ }
+ return result;
+}
+
+DrmInfoStatus* DrmManager::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+ const String8 plugInId = getSupportedPlugInId(drmInfo->getMimeType());
+ if (EMPTY_STRING != plugInId) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ return rDrmEngine.processDrmInfo(uniqueId, drmInfo);
+ }
+ return NULL;
+}
+
+bool DrmManager::canHandle(int uniqueId, const String8& path) {
+ bool result = false;
+ Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();
+
+ for (unsigned int i = 0; i < plugInPathList.size(); ++i) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInPathList[i]);
+ result = rDrmEngine.canHandle(uniqueId, path);
+
+ if (result) {
+ break;
+ }
+ }
+ return result;
+}
+
+DrmInfo* DrmManager::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+ const String8 plugInId = getSupportedPlugInId(drmInfoRequest->getMimeType());
+ if (EMPTY_STRING != plugInId) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ return rDrmEngine.acquireDrmInfo(uniqueId, drmInfoRequest);
+ }
+ return NULL;
+}
+
+status_t DrmManager::saveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath) {
+ const String8 plugInId = getSupportedPlugInId(drmRights.getMimeType());
+ status_t result = DRM_ERROR_UNKNOWN;
+ if (EMPTY_STRING != plugInId) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ result = rDrmEngine.saveRights(uniqueId, drmRights, rightsPath, contentPath);
+ }
+ return result;
+}
+
+String8 DrmManager::getOriginalMimeType(int uniqueId, const String8& path) {
+ const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
+ if (EMPTY_STRING != plugInId) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ return rDrmEngine.getOriginalMimeType(uniqueId, path);
+ }
+ return EMPTY_STRING;
+}
+
+int DrmManager::getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType) {
+ const String8 plugInId = getSupportedPlugInId(uniqueId, path, mimeType);
+ if (EMPTY_STRING != plugInId) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ return rDrmEngine.getDrmObjectType(uniqueId, path, mimeType);
+ }
+ return DrmObjectType::UNKNOWN;
+}
+
+int DrmManager::checkRightsStatus(int uniqueId, const String8& path, int action) {
+ const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
+ if (EMPTY_STRING != plugInId) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ return rDrmEngine.checkRightsStatus(uniqueId, path, action);
+ }
+ return RightsStatus::RIGHTS_INVALID;
+}
+
+status_t DrmManager::consumeRights(
+ int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+ status_t result = DRM_ERROR_UNKNOWN;
+ if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+ IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+ result = drmEngine->consumeRights(uniqueId, decryptHandle, action, reserve);
+ }
+ return result;
+}
+
+status_t DrmManager::setPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+ status_t result = DRM_ERROR_UNKNOWN;
+ if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+ IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+ result = drmEngine->setPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
+ }
+ return result;
+}
+
+bool DrmManager::validateAction(
+ int uniqueId, const String8& path, int action, const ActionDescription& description) {
+ const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
+ if (EMPTY_STRING != plugInId) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ return rDrmEngine.validateAction(uniqueId, path, action, description);
+ }
+ return false;
+}
+
+status_t DrmManager::removeRights(int uniqueId, const String8& path) {
+ const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
+ status_t result = DRM_ERROR_UNKNOWN;
+ if (EMPTY_STRING != plugInId) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ result = rDrmEngine.removeRights(uniqueId, path);
+ }
+ return result;
+}
+
+status_t DrmManager::removeAllRights(int uniqueId) {
+ Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
+ status_t result = DRM_ERROR_UNKNOWN;
+ for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
+ result = rDrmEngine.removeAllRights(uniqueId);
+ if (DRM_NO_ERROR != result) {
+ break;
+ }
+ }
+ return result;
+}
+
+int DrmManager::openConvertSession(int uniqueId, const String8& mimeType) {
+ int convertId = -1;
+
+ const String8 plugInId = getSupportedPlugInId(mimeType);
+ if (EMPTY_STRING != plugInId) {
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+
+ if (DRM_NO_ERROR == rDrmEngine.openConvertSession(uniqueId, mConvertId + 1)) {
+ Mutex::Autolock _l(mConvertLock);
+ ++mConvertId;
+ convertId = mConvertId;
+ mConvertSessionMap.add(convertId, &rDrmEngine);
+ }
+ }
+ return convertId;
+}
+
+DrmConvertedStatus* DrmManager::convertData(
+ int uniqueId, int convertId, const DrmBuffer* inputData) {
+ DrmConvertedStatus *drmConvertedStatus = NULL;
+
+ if (mConvertSessionMap.indexOfKey(convertId) != NAME_NOT_FOUND) {
+ IDrmEngine* drmEngine = mConvertSessionMap.valueFor(convertId);
+ drmConvertedStatus = drmEngine->convertData(uniqueId, convertId, inputData);
+ }
+ return drmConvertedStatus;
+}
+
+DrmConvertedStatus* DrmManager::closeConvertSession(int uniqueId, int convertId) {
+ DrmConvertedStatus *drmConvertedStatus = NULL;
+
+ if (mConvertSessionMap.indexOfKey(convertId) != NAME_NOT_FOUND) {
+ IDrmEngine* drmEngine = mConvertSessionMap.valueFor(convertId);
+ drmConvertedStatus = drmEngine->closeConvertSession(uniqueId, convertId);
+ mConvertSessionMap.removeItem(convertId);
+ }
+ return drmConvertedStatus;
+}
+
+status_t DrmManager::getAllSupportInfo(
+ int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) {
+ Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();
+ int size = plugInPathList.size();
+ int validPlugins = 0;
+
+ if (0 < size) {
+ Vector<DrmSupportInfo> drmSupportInfoList;
+
+ for (int i = 0; i < size; ++i) {
+ String8 plugInPath = plugInPathList[i];
+ DrmSupportInfo* drmSupportInfo
+ = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(0);
+ if (NULL != drmSupportInfo) {
+ drmSupportInfoList.add(*drmSupportInfo);
+ delete drmSupportInfo; drmSupportInfo = NULL;
+ }
+ }
+
+ validPlugins = drmSupportInfoList.size();
+ if (0 < validPlugins) {
+ *drmSupportInfoArray = new DrmSupportInfo[validPlugins];
+ for (int i = 0; i < validPlugins; ++i) {
+ (*drmSupportInfoArray)[i] = drmSupportInfoList[i];
+ }
+ }
+ }
+ *length = validPlugins;
+ return DRM_NO_ERROR;
+}
+
+DecryptHandle* DrmManager::openDecryptSession(int uniqueId, int fd, int offset, int length) {
+ Mutex::Autolock _l(mDecryptLock);
+ status_t result = DRM_ERROR_CANNOT_HANDLE;
+ Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
+
+ DecryptHandle* handle = new DecryptHandle();
+ if (NULL != handle) {
+ handle->decryptId = mDecryptSessionId + 1;
+
+ for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+ String8 plugInId = plugInIdList.itemAt(index);
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ result = rDrmEngine.openDecryptSession(uniqueId, handle, fd, offset, length);
+
+ if (DRM_NO_ERROR == result) {
+ ++mDecryptSessionId;
+ mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
+ break;
+ }
+ }
+ }
+ if (DRM_NO_ERROR != result) {
+ delete handle; handle = NULL;
+ LOGE("DrmManager::openDecryptSession: no capable plug-in found");
+ }
+ return handle;
+}
+
+DecryptHandle* DrmManager::openDecryptSession(int uniqueId, const char* uri) {
+ Mutex::Autolock _l(mDecryptLock);
+ status_t result = DRM_ERROR_CANNOT_HANDLE;
+ Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
+
+ DecryptHandle* handle = new DecryptHandle();
+ if (NULL != handle) {
+ handle->decryptId = mDecryptSessionId + 1;
+
+ for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+ String8 plugInId = plugInIdList.itemAt(index);
+ IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+ result = rDrmEngine.openDecryptSession(uniqueId, handle, uri);
+
+ if (DRM_NO_ERROR == result) {
+ ++mDecryptSessionId;
+ mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
+ break;
+ }
+ }
+ }
+ if (DRM_NO_ERROR != result) {
+ delete handle; handle = NULL;
+ LOGE("DrmManager::openDecryptSession: no capable plug-in found");
+ }
+ return handle;
+}
+
+status_t DrmManager::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+ Mutex::Autolock _l(mDecryptLock);
+ status_t result = DRM_ERROR_UNKNOWN;
+ if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+ IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+ result = drmEngine->closeDecryptSession(uniqueId, decryptHandle);
+ if (DRM_NO_ERROR == result) {
+ mDecryptSessionMap.removeItem(decryptHandle->decryptId);
+ }
+ }
+ return result;
+}
+
+status_t DrmManager::initializeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) {
+ status_t result = DRM_ERROR_UNKNOWN;
+ if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+ IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+ result = drmEngine->initializeDecryptUnit(uniqueId, decryptHandle, decryptUnitId, headerInfo);
+ }
+ return result;
+}
+
+status_t DrmManager::decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+ status_t result = DRM_ERROR_UNKNOWN;
+ if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+ IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+ result = drmEngine->decrypt(
+ uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
+ }
+ return result;
+}
+
+status_t DrmManager::finalizeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+ status_t result = DRM_ERROR_UNKNOWN;
+ if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+ IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+ result = drmEngine->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
+ }
+ return result;
+}
+
+ssize_t DrmManager::pread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off_t offset) {
+ ssize_t result = DECRYPT_FILE_ERROR;
+
+ if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+ IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+ result = drmEngine->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
+ }
+ return result;
+}
+
+String8 DrmManager::getSupportedPlugInId(
+ int uniqueId, const String8& path, const String8& mimeType) {
+ String8 plugInId("");
+
+ if (EMPTY_STRING != mimeType) {
+ plugInId = getSupportedPlugInId(mimeType);
+ } else {
+ plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
+ }
+ return plugInId;
+}
+
+String8 DrmManager::getSupportedPlugInId(const String8& mimeType) {
+ String8 plugInId("");
+
+ if (EMPTY_STRING != mimeType) {
+ for (unsigned int index = 0; index < mSupportInfoToPlugInIdMap.size(); index++) {
+ const DrmSupportInfo& drmSupportInfo = mSupportInfoToPlugInIdMap.keyAt(index);
+
+ if (drmSupportInfo.isSupportedMimeType(mimeType)) {
+ plugInId = mSupportInfoToPlugInIdMap.valueFor(drmSupportInfo);
+ break;
+ }
+ }
+ }
+ return plugInId;
+}
+
+String8 DrmManager::getSupportedPlugInIdFromPath(int uniqueId, const String8& path) {
+ String8 plugInId("");
+ const String8 fileSuffix = path.getPathExtension();
+
+ for (unsigned int index = 0; index < mSupportInfoToPlugInIdMap.size(); index++) {
+ const DrmSupportInfo& drmSupportInfo = mSupportInfoToPlugInIdMap.keyAt(index);
+
+ if (drmSupportInfo.isSupportedFileSuffix(fileSuffix)) {
+ String8 key = mSupportInfoToPlugInIdMap.valueFor(drmSupportInfo);
+ IDrmEngine& drmEngine = mPlugInManager.getPlugIn(key);
+
+ if (drmEngine.canHandle(uniqueId, path)) {
+ plugInId = key;
+ break;
+ }
+ }
+ }
+ return plugInId;
+}
+
+void DrmManager::onInfo(const DrmInfoEvent& event) {
+ Mutex::Autolock _l(mLock);
+ for (unsigned int index = 0; index < mServiceListeners.size(); index++) {
+ int uniqueId = mServiceListeners.keyAt(index);
+
+ if (uniqueId == event.getUniqueId()) {
+ sp<IDrmServiceListener> serviceListener = mServiceListeners.valueFor(uniqueId);
+ serviceListener->notify(event);
+ }
+ }
+}
+
diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp
new file mode 100644
index 0000000..4dcfa72
--- /dev/null
+++ b/drm/drmserver/DrmManagerService.cpp
@@ -0,0 +1,258 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "DrmManagerService(Native)"
+#include <utils/Log.h>
+
+#include <private/android_filesystem_config.h>
+
+#include <errno.h>
+#include <utils/threads.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <sys/stat.h>
+#include "DrmManagerService.h"
+#include "DrmManager.h"
+
+using namespace android;
+
+static Vector<uid_t> trustedUids;
+
+static bool isProtectedCallAllowed() {
+ // TODO
+ // Following implementation is just for reference.
+ // Each OEM manufacturer should implement/replace with their own solutions.
+ bool result = false;
+
+ IPCThreadState* ipcState = IPCThreadState::self();
+ uid_t uid = ipcState->getCallingUid();
+
+ for (unsigned int i = 0; i < trustedUids.size(); ++i) {
+ if (trustedUids[i] == uid) {
+ result = true;
+ break;
+ }
+ }
+ return result;
+}
+
+void DrmManagerService::instantiate() {
+ LOGV("instantiate");
+ defaultServiceManager()->addService(String16("drm.drmManager"), new DrmManagerService());
+
+ if (0 >= trustedUids.size()) {
+ // TODO
+ // Following implementation is just for reference.
+ // Each OEM manufacturer should implement/replace with their own solutions.
+
+ // Add trusted uids here
+ trustedUids.push(AID_MEDIA);
+ }
+}
+
+DrmManagerService::DrmManagerService() :
+ mDrmManager(NULL) {
+ LOGV("created");
+ mDrmManager = new DrmManager();
+ mDrmManager->loadPlugIns();
+}
+
+DrmManagerService::~DrmManagerService() {
+ LOGV("Destroyed");
+ mDrmManager->unloadPlugIns();
+ delete mDrmManager; mDrmManager = NULL;
+}
+
+int DrmManagerService::addUniqueId(int uniqueId) {
+ return mDrmManager->addUniqueId(uniqueId);
+}
+
+void DrmManagerService::removeUniqueId(int uniqueId) {
+ mDrmManager->removeUniqueId(uniqueId);
+}
+
+void DrmManagerService::addClient(int uniqueId) {
+ mDrmManager->addClient(uniqueId);
+}
+
+void DrmManagerService::removeClient(int uniqueId) {
+ mDrmManager->removeClient(uniqueId);
+}
+
+status_t DrmManagerService::setDrmServiceListener(
+ int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) {
+ LOGV("Entering setDrmServiceListener");
+ mDrmManager->setDrmServiceListener(uniqueId, drmServiceListener);
+ return DRM_NO_ERROR;
+}
+
+status_t DrmManagerService::installDrmEngine(int uniqueId, const String8& drmEngineFile) {
+ LOGV("Entering installDrmEngine");
+ return mDrmManager->installDrmEngine(uniqueId, drmEngineFile);
+}
+
+DrmConstraints* DrmManagerService::getConstraints(
+ int uniqueId, const String8* path, const int action) {
+ LOGV("Entering getConstraints from content");
+ return mDrmManager->getConstraints(uniqueId, path, action);
+}
+
+DrmMetadata* DrmManagerService::getMetadata(int uniqueId, const String8* path) {
+ LOGV("Entering getMetadata from content");
+ return mDrmManager->getMetadata(uniqueId, path);
+}
+
+bool DrmManagerService::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
+ LOGV("Entering canHandle");
+ return mDrmManager->canHandle(uniqueId, path, mimeType);
+}
+
+DrmInfoStatus* DrmManagerService::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+ LOGV("Entering processDrmInfo");
+ return mDrmManager->processDrmInfo(uniqueId, drmInfo);
+}
+
+DrmInfo* DrmManagerService::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+ LOGV("Entering acquireDrmInfo");
+ return mDrmManager->acquireDrmInfo(uniqueId, drmInfoRequest);
+}
+
+status_t DrmManagerService::saveRights(
+ int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath) {
+ LOGV("Entering saveRights");
+ return mDrmManager->saveRights(uniqueId, drmRights, rightsPath, contentPath);
+}
+
+String8 DrmManagerService::getOriginalMimeType(int uniqueId, const String8& path) {
+ LOGV("Entering getOriginalMimeType");
+ return mDrmManager->getOriginalMimeType(uniqueId, path);
+}
+
+int DrmManagerService::getDrmObjectType(
+ int uniqueId, const String8& path, const String8& mimeType) {
+ LOGV("Entering getDrmObjectType");
+ return mDrmManager->getDrmObjectType(uniqueId, path, mimeType);
+}
+
+int DrmManagerService::checkRightsStatus(
+ int uniqueId, const String8& path, int action) {
+ LOGV("Entering checkRightsStatus");
+ return mDrmManager->checkRightsStatus(uniqueId, path, action);
+}
+
+status_t DrmManagerService::consumeRights(
+ int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+ LOGV("Entering consumeRights");
+ return mDrmManager->consumeRights(uniqueId, decryptHandle, action, reserve);
+}
+
+status_t DrmManagerService::setPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+ LOGV("Entering setPlaybackStatus");
+ return mDrmManager->setPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
+}
+
+bool DrmManagerService::validateAction(
+ int uniqueId, const String8& path,
+ int action, const ActionDescription& description) {
+ LOGV("Entering validateAction");
+ return mDrmManager->validateAction(uniqueId, path, action, description);
+}
+
+status_t DrmManagerService::removeRights(int uniqueId, const String8& path) {
+ LOGV("Entering removeRights");
+ return mDrmManager->removeRights(uniqueId, path);
+}
+
+status_t DrmManagerService::removeAllRights(int uniqueId) {
+ LOGV("Entering removeAllRights");
+ return mDrmManager->removeAllRights(uniqueId);
+}
+
+int DrmManagerService::openConvertSession(int uniqueId, const String8& mimeType) {
+ LOGV("Entering openConvertSession");
+ return mDrmManager->openConvertSession(uniqueId, mimeType);
+}
+
+DrmConvertedStatus* DrmManagerService::convertData(
+ int uniqueId, int convertId, const DrmBuffer* inputData) {
+ LOGV("Entering convertData");
+ return mDrmManager->convertData(uniqueId, convertId, inputData);
+}
+
+DrmConvertedStatus* DrmManagerService::closeConvertSession(int uniqueId, int convertId) {
+ LOGV("Entering closeConvertSession");
+ return mDrmManager->closeConvertSession(uniqueId, convertId);
+}
+
+status_t DrmManagerService::getAllSupportInfo(
+ int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) {
+ LOGV("Entering getAllSupportInfo");
+ return mDrmManager->getAllSupportInfo(uniqueId, length, drmSupportInfoArray);
+}
+
+DecryptHandle* DrmManagerService::openDecryptSession(
+ int uniqueId, int fd, int offset, int length) {
+ LOGV("Entering DrmManagerService::openDecryptSession");
+ if (isProtectedCallAllowed()) {
+ return mDrmManager->openDecryptSession(uniqueId, fd, offset, length);
+ }
+
+ return NULL;
+}
+
+DecryptHandle* DrmManagerService::openDecryptSession(
+ int uniqueId, const char* uri) {
+ LOGV("Entering DrmManagerService::openDecryptSession with uri");
+ if (isProtectedCallAllowed()) {
+ return mDrmManager->openDecryptSession(uniqueId, uri);
+ }
+
+ return NULL;
+}
+
+status_t DrmManagerService::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+ LOGV("Entering closeDecryptSession");
+ return mDrmManager->closeDecryptSession(uniqueId, decryptHandle);
+}
+
+status_t DrmManagerService::initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo) {
+ LOGV("Entering initializeDecryptUnit");
+ return mDrmManager->initializeDecryptUnit(uniqueId,decryptHandle, decryptUnitId, headerInfo);
+}
+
+status_t DrmManagerService::decrypt(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+ LOGV("Entering decrypt");
+ return mDrmManager->decrypt(uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
+}
+
+status_t DrmManagerService::finalizeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+ LOGV("Entering finalizeDecryptUnit");
+ return mDrmManager->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
+}
+
+ssize_t DrmManagerService::pread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off_t offset) {
+ LOGV("Entering pread");
+ return mDrmManager->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
+}
+
diff --git a/drm/drmserver/StringTokenizer.cpp b/drm/drmserver/StringTokenizer.cpp
new file mode 100644
index 0000000..2130a00
--- /dev/null
+++ b/drm/drmserver/StringTokenizer.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 "StringTokenizer.h"
+
+using namespace android;
+
+StringTokenizer::StringTokenizer(const String8& string, const String8& delimiter) {
+ splitString(string, delimiter);
+}
+
+void StringTokenizer::splitString(const String8& string, const String8& delimiter) {
+ for (unsigned int i = 0; i < string.length(); i++) {
+ unsigned int position = string.find(delimiter.string(), i);
+ if (string.length() != position) {
+ String8 token(string.string()+i, position-i);
+ if (token.length()) {
+ mStringTokenizerVector.push(token);
+ i = position + delimiter.length() - 1;
+ }
+ } else {
+ mStringTokenizerVector.push(String8(string.string()+i, string.length()-i));
+ break;
+ }
+ }
+}
+
+StringTokenizer::Iterator StringTokenizer::iterator() {
+ return Iterator(this);
+}
+
+StringTokenizer::Iterator::Iterator(const StringTokenizer::Iterator& iterator) :
+ mStringTokenizer(iterator.mStringTokenizer),
+ mIndex(iterator.mIndex) {
+}
+
+StringTokenizer::Iterator& StringTokenizer::Iterator::operator=(
+ const StringTokenizer::Iterator& iterator) {
+ mStringTokenizer = iterator.mStringTokenizer;
+ mIndex = iterator.mIndex;
+ return *this;
+}
+
+bool StringTokenizer::Iterator::hasNext() {
+ return mIndex < mStringTokenizer->mStringTokenizerVector.size();
+}
+
+String8& StringTokenizer::Iterator::next() {
+ String8& value = mStringTokenizer->mStringTokenizerVector.editItemAt(mIndex);
+ mIndex++;
+ return value;
+}
+
diff --git a/drm/drmserver/main_drmserver.cpp b/drm/drmserver/main_drmserver.cpp
new file mode 100644
index 0000000..6d10646
--- /dev/null
+++ b/drm/drmserver/main_drmserver.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 <sys/types.h>
+#include <unistd.h>
+#include <grp.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+#include <private/android_filesystem_config.h>
+
+#include <DrmManagerService.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+ sp<ProcessState> proc(ProcessState::self());
+ sp<IServiceManager> sm = defaultServiceManager();
+ LOGI("ServiceManager: %p", sm.get());
+ DrmManagerService::instantiate();
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+}
+
diff --git a/drm/java/android/drm/DrmConvertedStatus.java b/drm/java/android/drm/DrmConvertedStatus.java
new file mode 100644
index 0000000..f200552
--- /dev/null
+++ b/drm/java/android/drm/DrmConvertedStatus.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.drm;
+
+/**
+ * This is an entity class which wraps the status of the conversion, the converted
+ * data/checksum data and the offset. Offset is going to be used in the case of close
+ * session where the agent will inform where the header and body signature should be added
+ *
+ * As a result of {@link DrmManagerClient#convertData(int, byte [])} and
+ * {@link DrmManagerClient#closeConvertSession(int)} an instance of DrmConvertedStatus
+ * would be returned.
+ *
+ */
+public class DrmConvertedStatus {
+ // Should be in sync with DrmConvertedStatus.cpp
+ public static final int STATUS_OK = 1;
+ public static final int STATUS_INPUTDATA_ERROR = 2;
+ public static final int STATUS_ERROR = 3;
+
+ public final int statusCode;
+ public final byte[] convertedData;
+ public final int offset;
+
+ /**
+ * constructor to create DrmConvertedStatus object with given parameters
+ *
+ * @param _statusCode Status of the conversion
+ * @param _convertedData Converted data/checksum data
+ * @param _offset Offset value
+ */
+ public DrmConvertedStatus(int _statusCode, byte[] _convertedData, int _offset) {
+ statusCode = _statusCode;
+ convertedData = _convertedData;
+ offset = _offset;
+ }
+}
+
diff --git a/drm/java/android/drm/DrmErrorEvent.java b/drm/java/android/drm/DrmErrorEvent.java
new file mode 100644
index 0000000..20fd8aa
--- /dev/null
+++ b/drm/java/android/drm/DrmErrorEvent.java
@@ -0,0 +1,68 @@
+/*
+ * 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.drm;
+
+/**
+ * This is an entity class which would be passed to caller in
+ * {@link DrmManagerClient.OnErrorListener#onError(DrmManagerClient, DrmErrorEvent)}
+ *
+ */
+public class DrmErrorEvent extends DrmEvent {
+ /**
+ * TYPE_RIGHTS_NOT_INSTALLED, when something went wrong installing the rights.
+ */
+ public static final int TYPE_RIGHTS_NOT_INSTALLED = 2001;
+ /**
+ * TYPE_RIGHTS_RENEWAL_NOT_ALLOWED, when the server rejects renewal of rights.
+ */
+ public static final int TYPE_RIGHTS_RENEWAL_NOT_ALLOWED = 2002;
+ /**
+ * TYPE_NOT_SUPPORTED, when answer from server can not be handled by the native agent.
+ */
+ public static final int TYPE_NOT_SUPPORTED = 2003;
+ /**
+ * TYPE_OUT_OF_MEMORY, when memory allocation fail during renewal.
+ * Can in the future perhaps be used to trigger garbage collector.
+ */
+ public static final int TYPE_OUT_OF_MEMORY = 2004;
+ /**
+ * TYPE_NO_INTERNET_CONNECTION, when the Internet connection is missing and no attempt
+ * can be made to renew rights.
+ */
+ public static final int TYPE_NO_INTERNET_CONNECTION = 2005;
+ /**
+ * TYPE_PROCESS_DRM_INFO_FAILED, when failed to process DrmInfo.
+ */
+ public static final int TYPE_PROCESS_DRM_INFO_FAILED = 2006;
+ /**
+ * TYPE_REMOVE_ALL_RIGHTS_FAILED, when failed to remove all the rights objects
+ * associated with all DRM schemes.
+ */
+ public static final int TYPE_REMOVE_ALL_RIGHTS_FAILED = 2007;
+
+ /**
+ * constructor to create DrmErrorEvent object with given parameters
+ *
+ * @param uniqueId Unique session identifier
+ * @param type Type of information
+ * @param message Message description
+ */
+ public DrmErrorEvent(int uniqueId, int type, String message) {
+ super(uniqueId, type, message);
+ }
+}
+
diff --git a/drm/java/android/drm/DrmEvent.java b/drm/java/android/drm/DrmEvent.java
new file mode 100644
index 0000000..f7bc5cd
--- /dev/null
+++ b/drm/java/android/drm/DrmEvent.java
@@ -0,0 +1,84 @@
+/*
+ * 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.drm;
+
+/**
+ * This is the base class which would be used to notify the caller
+ * about any event occurred in DRM framework.
+ *
+ */
+public class DrmEvent {
+ /**
+ * Constant field signifies that all the rights information associated with
+ * all DRM schemes are removed successfully
+ */
+ public static final int TYPE_ALL_RIGHTS_REMOVED = 1001;
+ /**
+ * Constant field signifies that given information is processed successfully
+ */
+ public static final int TYPE_DRM_INFO_PROCESSED = 1002;
+
+ public static final String DRM_INFO_STATUS_OBJECT = "drm_info_status_object";
+
+ private final int mUniqueId;
+ private final int mType;
+ private String mMessage = "";
+
+ /**
+ * constructor for DrmEvent class
+ *
+ * @param uniqueId Unique session identifier
+ * @param type Type of information
+ * @param message Message description
+ */
+ protected DrmEvent(int uniqueId, int type, String message) {
+ mUniqueId = uniqueId;
+ mType = type;
+
+ if (null != message) {
+ mMessage = message;
+ }
+ }
+
+ /**
+ * Returns the Unique Id associated with this object
+ *
+ * @return Unique Id
+ */
+ public int getUniqueId() {
+ return mUniqueId;
+ }
+
+ /**
+ * Returns the Type of information associated with this object
+ *
+ * @return Type of information
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the message description associated with this object
+ *
+ * @return message description
+ */
+ public String getMessage() {
+ return mMessage;
+ }
+}
+
diff --git a/drm/java/android/drm/DrmInfo.java b/drm/java/android/drm/DrmInfo.java
new file mode 100644
index 0000000..7d3fbf1
--- /dev/null
+++ b/drm/java/android/drm/DrmInfo.java
@@ -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.
+ */
+
+package android.drm;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * This is an entity class in which necessary information required to transact
+ * between device and online DRM server is described. DRM Framework achieves
+ * server registration, license acquisition and any other server related transaction
+ * by passing an instance of this class to {@link DrmManagerClient#processDrmInfo(DrmInfo)}.
+ *
+ * Caller can retrieve the {@link DrmInfo} instance by using
+ * {@link DrmManagerClient#acquireDrmInfo(DrmInfoRequest)}
+ * by passing {@link DrmInfoRequest} instance.
+ *
+ */
+public class DrmInfo {
+ private byte[] mData;
+ private final String mMimeType;
+ private final int mInfoType;
+ // It would be used to add attributes specific to
+ // DRM scheme such as account id, path or multiple path's
+ private final HashMap<String, Object> mAttributes = new HashMap<String, Object>();
+
+ /**
+ * constructor to create DrmInfo object with given parameters
+ *
+ * @param infoType Type of information
+ * @param data Trigger data
+ * @param mimeType MIME type
+ */
+ public DrmInfo(int infoType, byte[] data, String mimeType) {
+ mInfoType = infoType;
+ mMimeType = mimeType;
+ mData = data;
+ }
+
+ /**
+ * constructor to create DrmInfo object with given parameters
+ *
+ * @param infoType Type of information
+ * @param path Trigger data
+ * @param mimeType MIME type
+ */
+ public DrmInfo(int infoType, String path, String mimeType) {
+ mInfoType = infoType;
+ mMimeType = mimeType;
+ try {
+ mData = DrmUtils.readBytes(path);
+ } catch (IOException e) {
+ // As the given path is invalid,
+ // set mData = null, so that further processDrmInfo()
+ // call would fail with IllegalArgumentException because of mData = null
+ mData = null;
+ }
+ }
+
+ /**
+ * Adds optional information as <key, value> pair to this object
+ *
+ * @param key Key to add
+ * @param value Value to add
+ * To put custom object into DrmInfo, custom object has to
+ * override toString() implementation.
+ */
+ public void put(String key, Object value) {
+ mAttributes.put(key, value);
+ }
+
+ /**
+ * Retrieves the value of given key, if not found returns null
+ *
+ * @param key Key whose value to be retrieved
+ * @return The value or null
+ */
+ public Object get(String key) {
+ return mAttributes.get(key);
+ }
+
+ /**
+ * Returns Iterator object to walk through the keys associated with this instance
+ *
+ * @return Iterator object
+ */
+ public Iterator<String> keyIterator() {
+ return mAttributes.keySet().iterator();
+ }
+
+ /**
+ * Returns Iterator object to walk through the values associated with this instance
+ *
+ * @return Iterator object
+ */
+ public Iterator<Object> iterator() {
+ return mAttributes.values().iterator();
+ }
+
+ /**
+ * Returns the trigger data associated with this object
+ *
+ * @return Trigger data
+ */
+ public byte[] getData() {
+ return mData;
+ }
+
+ /**
+ * Returns the mimetype associated with this object
+ *
+ * @return MIME type
+ */
+ public String getMimeType() {
+ return mMimeType;
+ }
+
+ /**
+ * Returns information type associated with this instance
+ *
+ * @return Information type
+ */
+ public int getInfoType() {
+ return mInfoType;
+ }
+
+ /**
+ * Returns whether this instance is valid or not
+ *
+ * @return
+ * true if valid
+ * false if invalid
+ */
+ boolean isValid() {
+ return (null != mMimeType && !mMimeType.equals("")
+ && null != mData && mData.length > 0 && DrmInfoRequest.isValidType(mInfoType));
+ }
+}
+
diff --git a/drm/java/android/drm/DrmInfoEvent.java b/drm/java/android/drm/DrmInfoEvent.java
new file mode 100644
index 0000000..a778e06
--- /dev/null
+++ b/drm/java/android/drm/DrmInfoEvent.java
@@ -0,0 +1,60 @@
+/*
+ * 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.drm;
+
+/**
+ * This is an entity class which would be passed to caller in
+ * {@link DrmManagerClient.OnInfoListener#onInfo(DrmManagerClient, DrmInfoEvent)}
+ *
+ */
+public class DrmInfoEvent extends DrmEvent {
+ /**
+ * TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT, when registration has been already done
+ * by another account ID.
+ */
+ public static final int TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT = 1;
+ /**
+ * TYPE_REMOVE_RIGHTS, when the rights needs to be removed completely.
+ */
+ public static final int TYPE_REMOVE_RIGHTS = 2;
+ /**
+ * TYPE_RIGHTS_INSTALLED, when the rights are downloaded and installed ok.
+ */
+ public static final int TYPE_RIGHTS_INSTALLED = 3;
+ /**
+ * TYPE_WAIT_FOR_RIGHTS, rights object is on it's way to phone,
+ * wait before calling checkRights again.
+ */
+ public static final int TYPE_WAIT_FOR_RIGHTS = 4;
+ /**
+ * TYPE_ACCOUNT_ALREADY_REGISTERED, when registration has been
+ * already done for the given account.
+ */
+ public static final int TYPE_ACCOUNT_ALREADY_REGISTERED = 5;
+
+ /**
+ * constructor to create DrmInfoEvent object with given parameters
+ *
+ * @param uniqueId Unique session identifier
+ * @param type Type of information
+ * @param message Message description
+ */
+ public DrmInfoEvent(int uniqueId, int type, String message) {
+ super(uniqueId, type, message);
+ }
+}
+
diff --git a/drm/java/android/drm/DrmInfoRequest.java b/drm/java/android/drm/DrmInfoRequest.java
new file mode 100644
index 0000000..a5a799c
--- /dev/null
+++ b/drm/java/android/drm/DrmInfoRequest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.drm;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * This is an entity class used to pass required parameters to get
+ * the necessary information to communicate with online DRM server
+ *
+ * An instance of this class is passed to {@link DrmManagerClient#acquireDrmInfo(DrmInfoRequest)}
+ * to get the instance of {@link DrmInfo}
+ *
+ */
+public class DrmInfoRequest {
+ // Changes in following constants should be in sync with DrmInfoRequest.cpp
+ /**
+ * Constants defines the type of {@link DrmInfoRequest}
+ */
+ public static final int TYPE_REGISTRATION_INFO = 1;
+ public static final int TYPE_UNREGISTRATION_INFO = 2;
+ public static final int TYPE_RIGHTS_ACQUISITION_INFO = 3;
+ public static final int TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO = 4;
+
+ /**
+ * Key to pass the unique id for the account or the user
+ */
+ public static final String ACCOUNT_ID = "account_id";
+
+ /**
+ * Key to pass the unique id used for subscription
+ */
+ public static final String SUBSCRIPTION_ID = "subscription_id";
+
+ private final int mInfoType;
+ private final String mMimeType;
+ private final HashMap<String, Object> mRequestInformation = new HashMap<String, Object>();
+
+ /**
+ * constructor to create DrmInfoRequest object with type and mimetype
+ *
+ * @param infoType Type of information
+ * @param mimeType MIME type
+ */
+ public DrmInfoRequest(int infoType, String mimeType) {
+ mInfoType = infoType;
+ mMimeType = mimeType;
+ }
+
+ /**
+ * Returns the mimetype associated with this object
+ *
+ * @return MIME type
+ */
+ public String getMimeType() {
+ return mMimeType;
+ }
+
+ /**
+ * Returns Information type associated with this instance
+ *
+ * @return Information type
+ */
+ public int getInfoType() {
+ return mInfoType;
+ }
+
+ /**
+ * Adds optional information as <key, value> pair to this object.
+ *
+ * @param key Key to add
+ * @param value Value to add
+ */
+ public void put(String key, Object value) {
+ mRequestInformation.put(key, value);
+ }
+
+ /**
+ * Retrieves the value of given key, if not found returns null
+ *
+ * @param key Key whose value to be retrieved
+ * @return The value or null
+ */
+ public Object get(String key) {
+ return mRequestInformation.get(key);
+ }
+
+ /**
+ * Returns Iterator object to walk through the keys associated with this instance
+ *
+ * @return Iterator object
+ */
+ public Iterator<String> keyIterator() {
+ return mRequestInformation.keySet().iterator();
+ }
+
+ /**
+ * Returns Iterator object to walk through the values associated with this instance
+ *
+ * @return Iterator object
+ */
+ public Iterator<Object> iterator() {
+ return mRequestInformation.values().iterator();
+ }
+
+ /**
+ * Returns whether this instance is valid or not
+ *
+ * @return
+ * true if valid
+ * false if invalid
+ */
+ boolean isValid() {
+ return (null != mMimeType && !mMimeType.equals("")
+ && null != mRequestInformation && isValidType(mInfoType));
+ }
+
+ /* package */ static boolean isValidType(int infoType) {
+ boolean isValid = false;
+
+ switch (infoType) {
+ case TYPE_REGISTRATION_INFO:
+ case TYPE_UNREGISTRATION_INFO:
+ case TYPE_RIGHTS_ACQUISITION_INFO:
+ case TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO:
+ isValid = true;
+ break;
+ }
+ return isValid;
+ }
+}
+
diff --git a/drm/java/android/drm/DrmInfoStatus.java b/drm/java/android/drm/DrmInfoStatus.java
new file mode 100644
index 0000000..b37ea51
--- /dev/null
+++ b/drm/java/android/drm/DrmInfoStatus.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.drm;
+
+/**
+ * This is an entity class which wraps the result of communication between device
+ * and online DRM server.
+ *
+ * As a result of {@link DrmManagerClient#processDrmInfo(DrmInfo)} an instance of DrmInfoStatus
+ * would be returned. This class holds {@link ProcessedData}, which could be used to instantiate
+ * {@link DrmRights#DrmRights(ProcessedData, String)} in license acquisition.
+ *
+ */
+public class DrmInfoStatus {
+ // Should be in sync with DrmInfoStatus.cpp
+ public static final int STATUS_OK = 1;
+ public static final int STATUS_ERROR = 2;
+
+ public final int statusCode;
+ public final int infoType;
+ public final String mimeType;
+ public final ProcessedData data;
+
+ /**
+ * constructor to create DrmInfoStatus object with given parameters
+ *
+ * @param _statusCode Status of the communication
+ * @param _infoType Type of the DRM information processed
+ * @param _data The processed data
+ * @param _mimeType MIME type
+ */
+ public DrmInfoStatus(int _statusCode, int _infoType, ProcessedData _data, String _mimeType) {
+ statusCode = _statusCode;
+ infoType = _infoType;
+ data = _data;
+ mimeType = _mimeType;
+ }
+}
+
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
new file mode 100644
index 0000000..6caf678
--- /dev/null
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -0,0 +1,812 @@
+/*
+ * 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.drm;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Interface of DRM Framework.
+ * Java application will instantiate this class
+ * to access DRM agent through DRM Framework.
+ *
+ */
+public class DrmManagerClient {
+ /**
+ * Constant field signifies the success or no error occurred
+ */
+ public static final int ERROR_NONE = 0;
+ /**
+ * Constant field signifies that error occurred and the reason is not known
+ */
+ public static final int ERROR_UNKNOWN = -2000;
+
+ private static final String TAG = "DrmManagerClient";
+
+ static {
+ // Load the respective library
+ System.loadLibrary("drmframework_jni");
+ }
+
+ /**
+ * Interface definition of a callback to be invoked to communicate
+ * some info and/or warning about DrmManagerClient.
+ */
+ public interface OnInfoListener {
+ /**
+ * Called to indicate an info or a warning.
+ *
+ * @param client DrmManagerClient instance
+ * @param event instance which wraps reason and necessary information
+ */
+ public void onInfo(DrmManagerClient client, DrmInfoEvent event);
+ }
+
+ /**
+ * Interface definition of a callback to be invoked to communicate
+ * the result of time consuming APIs asynchronously
+ */
+ public interface OnEventListener {
+ /**
+ * Called to indicate the result of asynchronous APIs.
+ *
+ * @param client DrmManagerClient instance
+ * @param event instance which wraps type and message
+ * @param attributes resultant values in key and value pair.
+ */
+ public void onEvent(DrmManagerClient client, DrmEvent event,
+ HashMap<String, Object> attributes);
+ }
+
+ /**
+ * Interface definition of a callback to be invoked to communicate
+ * the error occurred
+ */
+ public interface OnErrorListener {
+ /**
+ * Called to indicate the error occurred.
+ *
+ * @param client DrmManagerClient instance
+ * @param event instance which wraps error type and message
+ */
+ public void onError(DrmManagerClient client, DrmErrorEvent event);
+ }
+
+ private static final int ACTION_REMOVE_ALL_RIGHTS = 1001;
+ private static final int ACTION_PROCESS_DRM_INFO = 1002;
+
+ private int mUniqueId;
+ private int mNativeContext;
+ private Context mContext;
+ private InfoHandler mInfoHandler;
+ private EventHandler mEventHandler;
+ private OnInfoListener mOnInfoListener;
+ private OnEventListener mOnEventListener;
+ private OnErrorListener mOnErrorListener;
+
+ private class EventHandler extends Handler {
+
+ public EventHandler(Looper looper) {
+ super(looper);
+ }
+
+ public void handleMessage(Message msg) {
+ DrmEvent event = null;
+ DrmErrorEvent error = null;
+ HashMap<String, Object> attributes = new HashMap<String, Object>();
+
+ switch(msg.what) {
+ case ACTION_PROCESS_DRM_INFO: {
+ final DrmInfo drmInfo = (DrmInfo) msg.obj;
+ DrmInfoStatus status = _processDrmInfo(mUniqueId, drmInfo);
+ if (null != status && DrmInfoStatus.STATUS_OK == status.statusCode) {
+ attributes.put(DrmEvent.DRM_INFO_STATUS_OBJECT, status);
+ event = new DrmEvent(mUniqueId, getEventType(status.infoType), null);
+ } else {
+ int infoType = (null != status) ? status.infoType : drmInfo.getInfoType();
+ error = new DrmErrorEvent(mUniqueId, getErrorType(infoType), null);
+ }
+ break;
+ }
+ case ACTION_REMOVE_ALL_RIGHTS: {
+ if (ERROR_NONE == _removeAllRights(mUniqueId)) {
+ event = new DrmEvent(mUniqueId, DrmEvent.TYPE_ALL_RIGHTS_REMOVED, null);
+ } else {
+ error = new DrmErrorEvent(mUniqueId,
+ DrmErrorEvent.TYPE_REMOVE_ALL_RIGHTS_FAILED, null);
+ }
+ break;
+ }
+ default:
+ Log.e(TAG, "Unknown message type " + msg.what);
+ return;
+ }
+ if (null != mOnEventListener && null != event) {
+ mOnEventListener.onEvent(DrmManagerClient.this, event, attributes);
+ }
+ if (null != mOnErrorListener && null != error) {
+ mOnErrorListener.onError(DrmManagerClient.this, error);
+ }
+ }
+ }
+
+ /**
+ * {@hide}
+ */
+ public static void notify(
+ Object thisReference, int uniqueId, int infoType, String message) {
+ DrmManagerClient instance = (DrmManagerClient)((WeakReference)thisReference).get();
+
+ if (null != instance && null != instance.mInfoHandler) {
+ Message m = instance.mInfoHandler.obtainMessage(
+ InfoHandler.INFO_EVENT_TYPE, uniqueId, infoType, message);
+ instance.mInfoHandler.sendMessage(m);
+ }
+ }
+
+ private class InfoHandler extends Handler {
+ public static final int INFO_EVENT_TYPE = 1;
+
+ public InfoHandler(Looper looper) {
+ super(looper);
+ }
+
+ public void handleMessage(Message msg) {
+ DrmInfoEvent event = null;
+ DrmErrorEvent error = null;
+
+ switch (msg.what) {
+ case InfoHandler.INFO_EVENT_TYPE:
+ int uniqueId = msg.arg1;
+ int infoType = msg.arg2;
+ String message = msg.obj.toString();
+
+ switch (infoType) {
+ case DrmInfoEvent.TYPE_REMOVE_RIGHTS: {
+ try {
+ DrmUtils.removeFile(message);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ event = new DrmInfoEvent(uniqueId, infoType, message);
+ break;
+ }
+ case DrmInfoEvent.TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT: {
+ event = new DrmInfoEvent(uniqueId, infoType, message);
+ break;
+ }
+ default:
+ error = new DrmErrorEvent(uniqueId, infoType, message);
+ break;
+ }
+
+ if (null != mOnInfoListener && null != event) {
+ mOnInfoListener.onInfo(DrmManagerClient.this, event);
+ }
+ if (null != mOnErrorListener && null != error) {
+ mOnErrorListener.onError(DrmManagerClient.this, error);
+ }
+ return;
+ default:
+ Log.e(TAG, "Unknown message type " + msg.what);
+ return;
+ }
+ }
+ }
+
+ /**
+ * To instantiate DrmManagerClient
+ *
+ * @param context context of the caller
+ */
+ public DrmManagerClient(Context context) {
+ mContext = context;
+
+ HandlerThread infoThread = new HandlerThread("DrmManagerClient.InfoHandler");
+ infoThread.start();
+ mInfoHandler = new InfoHandler(infoThread.getLooper());
+
+ HandlerThread eventThread = new HandlerThread("DrmManagerClient.EventHandler");
+ eventThread.start();
+ mEventHandler = new EventHandler(eventThread.getLooper());
+
+ // save the unique id
+ mUniqueId = hashCode();
+
+ _initialize(mUniqueId, new WeakReference<DrmManagerClient>(this));
+ }
+
+ protected void finalize() {
+ _finalize(mUniqueId);
+ }
+
+ /**
+ * Register a callback to be invoked when the caller required to receive
+ * supplementary information.
+ *
+ * @param infoListener
+ */
+ public synchronized void setOnInfoListener(OnInfoListener infoListener) {
+ if (null != infoListener) {
+ mOnInfoListener = infoListener;
+ }
+ }
+
+ /**
+ * Register a callback to be invoked when the caller required to receive
+ * the result of asynchronous APIs.
+ *
+ * @param eventListener
+ */
+ public synchronized void setOnEventListener(OnEventListener eventListener) {
+ if (null != eventListener) {
+ mOnEventListener = eventListener;
+ }
+ }
+
+ /**
+ * Register a callback to be invoked when the caller required to receive
+ * error result of asynchronous APIs.
+ *
+ * @param errorListener
+ */
+ public synchronized void setOnErrorListener(OnErrorListener errorListener) {
+ if (null != errorListener) {
+ mOnErrorListener = errorListener;
+ }
+ }
+
+ /**
+ * Retrieves informations about all the plug-ins registered with DrmFramework.
+ *
+ * @return Array of DrmEngine plug-in strings
+ */
+ public String[] getAvailableDrmEngines() {
+ DrmSupportInfo[] supportInfos = _getAllSupportInfo(mUniqueId);
+ ArrayList<String> descriptions = new ArrayList<String>();
+
+ for (int i = 0; i < supportInfos.length; i++) {
+ descriptions.add(supportInfos[i].getDescriprition());
+ }
+
+ String[] drmEngines = new String[descriptions.size()];
+ return descriptions.toArray(drmEngines);
+ }
+
+ /**
+ * Get constraints information evaluated from DRM content
+ *
+ * @param path Content path from where DRM constraints would be retrieved.
+ * @param action Actions defined in {@link DrmStore.Action}
+ * @return ContentValues instance in which constraints key-value pairs are embedded
+ * or null in case of failure
+ */
+ public ContentValues getConstraints(String path, int action) {
+ if (null == path || path.equals("") || !DrmStore.Action.isValid(action)) {
+ throw new IllegalArgumentException("Given usage or path is invalid/null");
+ }
+ return _getConstraints(mUniqueId, path, action);
+ }
+
+ /**
+ * Get metadata information from DRM content
+ *
+ * @param path Content path from where DRM metadata would be retrieved.
+ * @return ContentValues instance in which metadata key-value pairs are embedded
+ * or null in case of failure
+ */
+ public ContentValues getMetadata(String path) {
+ if (null == path || path.equals("")) {
+ throw new IllegalArgumentException("Given path is invalid/null");
+ }
+ return _getMetadata(mUniqueId, path);
+ }
+
+ /**
+ * Get constraints information evaluated from DRM content
+ *
+ * @param uri Content URI from where DRM constraints would be retrieved.
+ * @param action Actions defined in {@link DrmStore.Action}
+ * @return ContentValues instance in which constraints key-value pairs are embedded
+ * or null in case of failure
+ */
+ public ContentValues getConstraints(Uri uri, int action) {
+ if (null == uri || Uri.EMPTY == uri) {
+ throw new IllegalArgumentException("Uri should be non null");
+ }
+ return getConstraints(convertUriToPath(uri), action);
+ }
+
+ /**
+ * Get metadata information from DRM content
+ *
+ * @param uri Content URI from where DRM metadata would be retrieved.
+ * @return ContentValues instance in which metadata key-value pairs are embedded
+ * or null in case of failure
+ */
+ public ContentValues getMetadata(Uri uri) {
+ if (null == uri || Uri.EMPTY == uri) {
+ throw new IllegalArgumentException("Uri should be non null");
+ }
+ return getMetadata(convertUriToPath(uri));
+ }
+
+ /**
+ * Save DRM rights to specified rights path
+ * and make association with content path.
+ *
+ * <p class="note">In case of OMA or WM-DRM, rightsPath and contentPath could be null.</p>
+ *
+ * @param drmRights DrmRights to be saved
+ * @param rightsPath File path where rights to be saved
+ * @param contentPath File path where content was saved
+ * @return
+ * ERROR_NONE for success
+ * ERROR_UNKNOWN for failure
+ * @throws IOException if failed to save rights information in the given path
+ */
+ public int saveRights(
+ DrmRights drmRights, String rightsPath, String contentPath) throws IOException {
+ if (null == drmRights || !drmRights.isValid()) {
+ throw new IllegalArgumentException("Given drmRights or contentPath is not valid");
+ }
+ if (null != rightsPath && !rightsPath.equals("")) {
+ DrmUtils.writeToFile(rightsPath, drmRights.getData());
+ }
+ return _saveRights(mUniqueId, drmRights, rightsPath, contentPath);
+ }
+
+ /**
+ * Install new DRM Engine Plug-in at the runtime
+ *
+ * @param engineFilePath Path of the plug-in file to be installed
+ * {@hide}
+ */
+ public void installDrmEngine(String engineFilePath) {
+ if (null == engineFilePath || engineFilePath.equals("")) {
+ throw new IllegalArgumentException(
+ "Given engineFilePath: "+ engineFilePath + "is not valid");
+ }
+ _installDrmEngine(mUniqueId, engineFilePath);
+ }
+
+ /**
+ * Check whether the given mimetype or path can be handled.
+ *
+ * @param path Path of the content to be handled
+ * @param mimeType Mimetype of the object to be handled
+ * @return
+ * true - if the given mimeType or path can be handled
+ * false - cannot be handled.
+ */
+ public boolean canHandle(String path, String mimeType) {
+ if ((null == path || path.equals("")) && (null == mimeType || mimeType.equals(""))) {
+ throw new IllegalArgumentException("Path or the mimetype should be non null");
+ }
+ return _canHandle(mUniqueId, path, mimeType);
+ }
+
+ /**
+ * Check whether the given mimetype or uri can be handled.
+ *
+ * @param uri Content URI of the data to be handled.
+ * @param mimeType Mimetype of the object to be handled
+ * @return
+ * true - if the given mimeType or path can be handled
+ * false - cannot be handled.
+ */
+ public boolean canHandle(Uri uri, String mimeType) {
+ if ((null == uri || Uri.EMPTY == uri) && (null == mimeType || mimeType.equals(""))) {
+ throw new IllegalArgumentException("Uri or the mimetype should be non null");
+ }
+ return canHandle(convertUriToPath(uri), mimeType);
+ }
+
+ /**
+ * Executes given drm information based on its type
+ *
+ * @param drmInfo Information needs to be processed
+ * @return
+ * ERROR_NONE for success
+ * ERROR_UNKNOWN for failure
+ */
+ public int processDrmInfo(DrmInfo drmInfo) {
+ if (null == drmInfo || !drmInfo.isValid()) {
+ throw new IllegalArgumentException("Given drmInfo is invalid/null");
+ }
+ int result = ERROR_UNKNOWN;
+ if (null != mEventHandler) {
+ Message msg = mEventHandler.obtainMessage(ACTION_PROCESS_DRM_INFO, drmInfo);
+ result = (mEventHandler.sendMessage(msg)) ? ERROR_NONE : result;
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves necessary information for register, unregister or rights acquisition.
+ *
+ * @param drmInfoRequest Request information to retrieve drmInfo
+ * @return DrmInfo Instance as a result of processing given input
+ */
+ public DrmInfo acquireDrmInfo(DrmInfoRequest drmInfoRequest) {
+ if (null == drmInfoRequest || !drmInfoRequest.isValid()) {
+ throw new IllegalArgumentException("Given drmInfoRequest is invalid/null");
+ }
+ return _acquireDrmInfo(mUniqueId, drmInfoRequest);
+ }
+
+ /**
+ * Executes given DrmInfoRequest and returns the rights information asynchronously.
+ * This is a utility API which consists of {@link #acquireDrmInfo(DrmInfoRequest)}
+ * and {@link #processDrmInfo(DrmInfo)}.
+ * It can be used if selected DRM agent can work with this combined sequences.
+ * In case of some DRM schemes, such as OMA DRM, application needs to invoke
+ * {@link #acquireDrmInfo(DrmInfoRequest)} and {@link #processDrmInfo(DrmInfo)}, separately.
+ *
+ * @param drmInfoRequest Request information to retrieve drmInfo
+ * @return
+ * ERROR_NONE for success
+ * ERROR_UNKNOWN for failure
+ */
+ public int acquireRights(DrmInfoRequest drmInfoRequest) {
+ DrmInfo drmInfo = acquireDrmInfo(drmInfoRequest);
+ return processDrmInfo(drmInfo);
+ }
+
+ /**
+ * 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 path Path of the content or null.
+ * @param mimeType Mimetype of the content or null.
+ * @return Type of the DRM content.
+ * @see DrmStore.DrmObjectType
+ */
+ public int getDrmObjectType(String path, String mimeType) {
+ if ((null == path || path.equals("")) && (null == mimeType || mimeType.equals(""))) {
+ throw new IllegalArgumentException("Path or the mimetype should be non null");
+ }
+ return _getDrmObjectType(mUniqueId, path, mimeType);
+ }
+
+ /**
+ * Retrieves the type of the protected object (content, rights, etc..)
+ * using specified uri or mimetype. At least one parameter should be non null
+ * to retrieve DRM object type
+ *
+ * @param uri The content URI of the data
+ * @param mimeType Mimetype of the content or null.
+ * @return Type of the DRM content.
+ * @see DrmStore.DrmObjectType
+ */
+ public int getDrmObjectType(Uri uri, String mimeType) {
+ if ((null == uri || Uri.EMPTY == uri) && (null == mimeType || mimeType.equals(""))) {
+ throw new IllegalArgumentException("Uri or the mimetype should be non null");
+ }
+ String path = "";
+ try {
+ path = convertUriToPath(uri);
+ } catch (Exception e) {
+ // Even uri is invalid the mimetype shall be valid, so allow to proceed further.
+ Log.w(TAG, "Given Uri could not be found in media store");
+ }
+ return getDrmObjectType(path, mimeType);
+ }
+
+ /**
+ * Retrieves the mime type embedded inside the original content
+ *
+ * @param path Path of the protected content
+ * @return Mimetype of the original content, such as "video/mpeg"
+ */
+ public String getOriginalMimeType(String path) {
+ if (null == path || path.equals("")) {
+ throw new IllegalArgumentException("Given path should be non null");
+ }
+ return _getOriginalMimeType(mUniqueId, path);
+ }
+
+ /**
+ * Retrieves the mime type embedded inside the original content
+ *
+ * @param uri The content URI of the data
+ * @return Mimetype of the original content, such as "video/mpeg"
+ */
+ public String getOriginalMimeType(Uri uri) {
+ if (null == uri || Uri.EMPTY == uri) {
+ throw new IllegalArgumentException("Given uri is not valid");
+ }
+ return getOriginalMimeType(convertUriToPath(uri));
+ }
+
+ /**
+ * Check whether the given content has valid rights or not
+ *
+ * @param path Path of the protected content
+ * @return Status of the rights for the protected content
+ * @see DrmStore.RightsStatus
+ */
+ public int checkRightsStatus(String path) {
+ return checkRightsStatus(path, DrmStore.Action.DEFAULT);
+ }
+
+ /**
+ * Check whether the given content has valid rights or not
+ *
+ * @param uri The content URI of the data
+ * @return Status of the rights for the protected content
+ * @see DrmStore.RightsStatus
+ */
+ public int checkRightsStatus(Uri uri) {
+ if (null == uri || Uri.EMPTY == uri) {
+ throw new IllegalArgumentException("Given uri is not valid");
+ }
+ return checkRightsStatus(convertUriToPath(uri));
+ }
+
+ /**
+ * Check whether the given content has valid rights or not for specified action.
+ *
+ * @param path Path of the protected content
+ * @param action Action to perform
+ * @return Status of the rights for the protected content
+ * @see DrmStore.RightsStatus
+ */
+ public int checkRightsStatus(String path, int action) {
+ if (null == path || path.equals("") || !DrmStore.Action.isValid(action)) {
+ throw new IllegalArgumentException("Given path or action is not valid");
+ }
+ return _checkRightsStatus(mUniqueId, path, action);
+ }
+
+ /**
+ * Check whether the given content has valid rights or not for specified action.
+ *
+ * @param uri The content URI of the data
+ * @param action Action to perform
+ * @return Status of the rights for the protected content
+ * @see DrmStore.RightsStatus
+ */
+ public int checkRightsStatus(Uri uri, int action) {
+ if (null == uri || Uri.EMPTY == uri) {
+ throw new IllegalArgumentException("Given uri is not valid");
+ }
+ return checkRightsStatus(convertUriToPath(uri), action);
+ }
+
+ /**
+ * Removes the rights associated with the given protected content
+ *
+ * @param path Path of the protected content
+ * @return
+ * ERROR_NONE for success
+ * ERROR_UNKNOWN for failure
+ */
+ public int removeRights(String path) {
+ if (null == path || path.equals("")) {
+ throw new IllegalArgumentException("Given path should be non null");
+ }
+ return _removeRights(mUniqueId, path);
+ }
+
+ /**
+ * Removes the rights associated with the given protected content
+ *
+ * @param uri The content URI of the data
+ * @return
+ * ERROR_NONE for success
+ * ERROR_UNKNOWN for failure
+ */
+ public int removeRights(Uri uri) {
+ if (null == uri || Uri.EMPTY == uri) {
+ throw new IllegalArgumentException("Given uri is not valid");
+ }
+ return removeRights(convertUriToPath(uri));
+ }
+
+ /**
+ * Removes all the rights information of every plug-in associated with
+ * DRM framework. Will be used in master reset
+ *
+ * @return
+ * ERROR_NONE for success
+ * ERROR_UNKNOWN for failure
+ */
+ public int removeAllRights() {
+ int result = ERROR_UNKNOWN;
+ if (null != mEventHandler) {
+ Message msg = mEventHandler.obtainMessage(ACTION_REMOVE_ALL_RIGHTS);
+ result = (mEventHandler.sendMessage(msg)) ? ERROR_NONE : result;
+ }
+ return result;
+ }
+
+ /**
+ * This API is for Forward Lock based DRM scheme.
+ * 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.
+ *
+ * @param mimeType Description/MIME type of the input data packet
+ * @return convert ID which will be used for maintaining convert session.
+ */
+ public int openConvertSession(String mimeType) {
+ if (null == mimeType || mimeType.equals("")) {
+ throw new IllegalArgumentException("Path or the mimeType should be non null");
+ }
+ return _openConvertSession(mUniqueId, mimeType);
+ }
+
+ /**
+ * 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 are new block
+ * of data received by the application.
+ *
+ * @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.
+ */
+ public DrmConvertedStatus convertData(int convertId, byte[] inputData) {
+ if (null == inputData || 0 >= inputData.length) {
+ throw new IllegalArgumentException("Given inputData should be non null");
+ }
+ return _convertData(mUniqueId, convertId, inputData);
+ }
+
+ /**
+ * Informs the Drm Agent when there is no more data which need to be converted
+ * or when an error occurs. Upon successful conversion of the complete data,
+ * the agent will inform that where the header and body signature
+ * should be added. This signature appending is needed to integrity
+ * protect the converted file.
+ *
+ * @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 on which offset these signature data should be appended.
+ */
+ public DrmConvertedStatus closeConvertSession(int convertId) {
+ return _closeConvertSession(mUniqueId, convertId);
+ }
+
+ private int getEventType(int infoType) {
+ int eventType = -1;
+
+ switch (infoType) {
+ case DrmInfoRequest.TYPE_REGISTRATION_INFO:
+ case DrmInfoRequest.TYPE_UNREGISTRATION_INFO:
+ case DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_INFO:
+ eventType = DrmEvent.TYPE_DRM_INFO_PROCESSED;
+ break;
+ }
+ return eventType;
+ }
+
+ private int getErrorType(int infoType) {
+ int error = -1;
+
+ switch (infoType) {
+ case DrmInfoRequest.TYPE_REGISTRATION_INFO:
+ case DrmInfoRequest.TYPE_UNREGISTRATION_INFO:
+ case DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_INFO:
+ error = DrmErrorEvent.TYPE_PROCESS_DRM_INFO_FAILED;
+ break;
+ }
+ return error;
+ }
+
+ /**
+ * This method expects uri in the following format
+ * content://media/<table_name>/<row_index> (or)
+ * file://sdcard/test.mp4
+ *
+ * Here <table_name> shall be "video" or "audio" or "images"
+ * <row_index> the index of the content in given table
+ */
+ private String convertUriToPath(Uri uri) {
+ String path = null;
+ 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");
+ }
+ }
+ return path;
+ }
+
+ // private native interfaces
+ private native void _initialize(int uniqueId, Object weak_this);
+
+ private native void _finalize(int uniqueId);
+
+ private native void _installDrmEngine(int uniqueId, String engineFilepath);
+
+ private native ContentValues _getConstraints(int uniqueId, String path, int usage);
+
+ private native ContentValues _getMetadata(int uniqueId, String path);
+
+ private native boolean _canHandle(int uniqueId, String path, String mimeType);
+
+ private native DrmInfoStatus _processDrmInfo(int uniqueId, DrmInfo drmInfo);
+
+ private native DrmInfo _acquireDrmInfo(int uniqueId, DrmInfoRequest drmInfoRequest);
+
+ private native int _saveRights(
+ int uniqueId, DrmRights drmRights, String rightsPath, String contentPath);
+
+ private native int _getDrmObjectType(int uniqueId, String path, String mimeType);
+
+ private native String _getOriginalMimeType(int uniqueId, String path);
+
+ private native int _checkRightsStatus(int uniqueId, String path, int action);
+
+ private native int _removeRights(int uniqueId, String path);
+
+ private native int _removeAllRights(int uniqueId);
+
+ private native int _openConvertSession(int uniqueId, String mimeType);
+
+ private native DrmConvertedStatus _convertData(
+ int uniqueId, int convertId, byte[] inputData);
+
+ private native DrmConvertedStatus _closeConvertSession(int uniqueId, int convertId);
+
+ private native DrmSupportInfo[] _getAllSupportInfo(int uniqueId);
+}
+
diff --git a/drm/java/android/drm/DrmRights.java b/drm/java/android/drm/DrmRights.java
new file mode 100644
index 0000000..103af07
--- /dev/null
+++ b/drm/java/android/drm/DrmRights.java
@@ -0,0 +1,182 @@
+/*
+ * 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.drm;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * This is an entity class which wraps the license information which was
+ * retrieved from the online DRM server.
+ *
+ * Caller can instantiate {@link DrmRights} by
+ * invoking {@link DrmRights#DrmRights(ProcessedData, String)}
+ * constructor by using the result of {@link DrmManagerClient#processDrmInfo(DrmInfo)} interface.
+ * Caller can also instantiate {@link DrmRights} using the file path
+ * which contains rights information.
+ *
+ */
+public class DrmRights {
+ private byte[] mData;
+ private String mMimeType;
+ private String mAccountId = "_NO_USER";
+ private String mSubscriptionId = "";
+
+ /**
+ * constructor to create DrmRights object with given parameters
+ *
+ * @param rightsFilePath Path of the file containing rights data
+ * @param mimeType MIME type
+ */
+ public DrmRights(String rightsFilePath, String mimeType) {
+ File file = new File(rightsFilePath);
+ instantiate(file, mimeType);
+ }
+
+ /**
+ * constructor to create DrmRights object with given parameters
+ *
+ * @param rightsFilePath Path of the file containing rights data
+ * @param mimeType MIME type
+ * @param accountId Account Id of the user
+ */
+ public DrmRights(String rightsFilePath, String mimeType, String accountId) {
+ this(rightsFilePath, mimeType);
+
+ if (null != accountId && !accountId.equals("")) {
+ mAccountId = accountId;
+ }
+ }
+
+ /**
+ * constructor to create DrmRights object with given parameters
+ *
+ * @param rightsFilePath Path of the file containing rights data
+ * @param mimeType MIME type
+ * @param accountId Account Id of the user
+ * @param subscriptionId Subscription Id of the user
+ */
+ public DrmRights(
+ String rightsFilePath, String mimeType, String accountId, String subscriptionId) {
+ this(rightsFilePath, mimeType);
+
+ if (null != accountId && !accountId.equals("")) {
+ mAccountId = accountId;
+ }
+
+ if (null != subscriptionId && !subscriptionId.equals("")) {
+ mSubscriptionId = subscriptionId;
+ }
+ }
+
+ /**
+ * constructor to create DrmRights object with given parameters
+ *
+ * @param rightsFile File containing rights data
+ * @param mimeType MIME type
+ */
+ public DrmRights(File rightsFile, String mimeType) {
+ instantiate(rightsFile, mimeType);
+ }
+
+ private void instantiate(File rightsFile, String mimeType) {
+ try {
+ mData = DrmUtils.readBytes(rightsFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ mMimeType = mimeType;
+ }
+
+ /**
+ * constructor to create DrmRights object with given parameters
+ * The user can pass String or binary data<p>
+ * Usage:<p>
+ * i) String(e.g. data is instance of String):<br>
+ * - new DrmRights(data.getBytes(), mimeType)<p>
+ * ii) Binary data<br>
+ * - new DrmRights(binaryData[], mimeType)<br>
+ *
+ * @param data Processed data
+ * @param mimeType MIME type
+ */
+ public DrmRights(ProcessedData data, String mimeType) {
+ mData = data.getData();
+
+ String accountId = data.getAccountId();
+ if (null != accountId && !accountId.equals("")) {
+ mAccountId = accountId;
+ }
+
+ String subscriptionId = data.getSubscriptionId();
+ if (null != subscriptionId && !subscriptionId.equals("")) {
+ mSubscriptionId = subscriptionId;
+ }
+
+ mMimeType = mimeType;
+ }
+
+ /**
+ * Returns the rights data associated with this object
+ *
+ * @return Rights data
+ */
+ public byte[] getData() {
+ return mData;
+ }
+
+ /**
+ * Returns the mimetype associated with this object
+ *
+ * @return MIME type
+ */
+ public String getMimeType() {
+ return mMimeType;
+ }
+
+ /**
+ * Returns the account-id associated with this object
+ *
+ * @return Account Id
+ */
+ public String getAccountId() {
+ return mAccountId;
+ }
+
+ /**
+ * Returns the subscription-id associated with this object
+ *
+ * @return Subscription Id
+ */
+ public String getSubscriptionId() {
+ return mSubscriptionId;
+ }
+
+ /**
+ * Returns whether this instance is valid or not
+ *
+ * @return
+ * true if valid
+ * false if invalid
+ */
+ /*package*/ boolean isValid() {
+ return (null != mMimeType && !mMimeType.equals("")
+ && null != mData && mData.length > 0);
+ }
+}
+
diff --git a/drm/java/android/drm/DrmStore.java b/drm/java/android/drm/DrmStore.java
new file mode 100644
index 0000000..44df90c
--- /dev/null
+++ b/drm/java/android/drm/DrmStore.java
@@ -0,0 +1,199 @@
+/*
+ * 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.drm;
+
+/**
+ * This class defines all the constants used by DRM framework
+ *
+ */
+public class DrmStore {
+ /**
+ * Columns representing drm constraints
+ */
+ public interface ConstraintsColumns {
+ /**
+ * The max repeat count
+ * <P>Type: INTEGER</P>
+ */
+ public static final String MAX_REPEAT_COUNT = "max_repeat_count";
+
+ /**
+ * The remaining repeat count
+ * <P>Type: INTEGER</P>
+ */
+ public static final String REMAINING_REPEAT_COUNT = "remaining_repeat_count";
+
+ /**
+ * The time before which the protected file can not be played/viewed
+ * <P>Type: TEXT</P>
+ */
+ public static final String LICENSE_START_TIME = "license_start_time";
+
+ /**
+ * The time after which the protected file can not be played/viewed
+ * <P>Type: TEXT</P>
+ */
+ public static final String LICENSE_EXPIRY_TIME = "license_expiry_time";
+
+ /**
+ * The available time for license
+ * <P>Type: TEXT</P>
+ */
+ public static final String LICENSE_AVAILABLE_TIME = "license_available_time";
+
+ /**
+ * The data stream for extended metadata
+ * <P>Type: TEXT</P>
+ */
+ public static final String EXTENDED_METADATA = "extended_metadata";
+ }
+
+ /**
+ * Defines constants related to DRM types
+ */
+ public static class DrmObjectType {
+ /**
+ * Field specifies the unknown type
+ */
+ public static final int UNKNOWN = 0x00;
+ /**
+ * Field specifies the protected content type
+ */
+ public static final int CONTENT = 0x01;
+ /**
+ * Field specifies the rights information
+ */
+ public static final int RIGHTS_OBJECT = 0x02;
+ /**
+ * Field specifies the trigger information
+ */
+ public static final int TRIGGER_OBJECT = 0x03;
+ }
+
+ /**
+ * Defines constants related to playback
+ */
+ public static class Playback {
+ /**
+ * Constant field signifies playback start
+ */
+ public static final int START = 0x00;
+ /**
+ * Constant field signifies playback stop
+ */
+ public static final int STOP = 0x01;
+ /**
+ * Constant field signifies playback paused
+ */
+ public static final int PAUSE = 0x02;
+ /**
+ * Constant field signifies playback resumed
+ */
+ public static final int RESUME = 0x03;
+
+ /* package */ static boolean isValid(int playbackStatus) {
+ boolean isValid = false;
+
+ switch (playbackStatus) {
+ case START:
+ case STOP:
+ case PAUSE:
+ case RESUME:
+ isValid = true;
+ }
+ return isValid;
+ }
+ }
+
+ /**
+ * Defines actions that can be performed on protected content
+ */
+ public static class Action {
+ /**
+ * Constant field signifies that the default action
+ */
+ public static final int DEFAULT = 0x00;
+ /**
+ * Constant field signifies that the content can be played
+ */
+ public static final int PLAY = 0x01;
+ /**
+ * Constant field signifies that the content can be set as ring tone
+ */
+ public static final int RINGTONE = 0x02;
+ /**
+ * Constant field signifies that the content can be transfered
+ */
+ public static final int TRANSFER = 0x03;
+ /**
+ * Constant field signifies that the content can be set as output
+ */
+ public static final int OUTPUT = 0x04;
+ /**
+ * Constant field signifies that preview is allowed
+ */
+ public static final int PREVIEW = 0x05;
+ /**
+ * Constant field signifies that the content can be executed
+ */
+ public static final int EXECUTE = 0x06;
+ /**
+ * Constant field signifies that the content can displayed
+ */
+ public static final int DISPLAY = 0x07;
+
+ /* package */ static boolean isValid(int action) {
+ boolean isValid = false;
+
+ switch (action) {
+ case DEFAULT:
+ case PLAY:
+ case RINGTONE:
+ case TRANSFER:
+ case OUTPUT:
+ case PREVIEW:
+ case EXECUTE:
+ case DISPLAY:
+ isValid = true;
+ }
+ return isValid;
+ }
+ }
+
+ /**
+ * Defines constants related to status of the rights
+ */
+ public static class RightsStatus {
+ /**
+ * Constant field signifies that the rights are valid
+ */
+ public static final int RIGHTS_VALID = 0x00;
+ /**
+ * Constant field signifies that the rights are invalid
+ */
+ public static final int RIGHTS_INVALID = 0x01;
+ /**
+ * Constant field signifies that the rights are expired for the content
+ */
+ public static final int RIGHTS_EXPIRED = 0x02;
+ /**
+ * Constant field signifies that the rights are not acquired for the content
+ */
+ public static final int RIGHTS_NOT_ACQUIRED = 0x03;
+ }
+}
+
diff --git a/drm/java/android/drm/DrmSupportInfo.java b/drm/java/android/drm/DrmSupportInfo.java
new file mode 100644
index 0000000..0886af8
--- /dev/null
+++ b/drm/java/android/drm/DrmSupportInfo.java
@@ -0,0 +1,153 @@
+/*
+ * 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.drm;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * This is an entity class which wraps the capability of each plug-in,
+ * such as mimetype's and file suffixes it could handle.
+ *
+ * Plug-in developer could return the capability of the plugin by passing
+ * {@link DrmSupportInfo} instance.
+ *
+ */
+public class DrmSupportInfo {
+ private final ArrayList<String> mFileSuffixList = new ArrayList<String>();
+ private final ArrayList<String> mMimeTypeList = new ArrayList<String>();
+ private String mDescription = "";
+
+ /**
+ * Add the mime-type to the support info such that respective plug-in is
+ * capable of handling the given mime-type.
+ *
+ * @param mimeType MIME type
+ */
+ public void addMimeType(String mimeType) {
+ mMimeTypeList.add(mimeType);
+ }
+
+ /**
+ * Add the file suffix to the support info such that respective plug-in is
+ * capable of handling the given file suffix.
+ *
+ * @param fileSuffix File suffix which can be handled
+ */
+ public void addFileSuffix(String fileSuffix) {
+ mFileSuffixList.add(fileSuffix);
+ }
+
+ /**
+ * Returns the iterator to walk to through mime types of this object
+ *
+ * @return Iterator object
+ */
+ public Iterator<String> getMimeTypeIterator() {
+ return mMimeTypeList.iterator();
+ }
+
+ /**
+ * Returns the iterator to walk to through file suffixes of this object
+ *
+ * @return Iterator object
+ */
+ public Iterator<String> getFileSuffixIterator() {
+ return mFileSuffixList.iterator();
+ }
+
+ /**
+ * Set the unique description about the plugin
+ *
+ * @param description Unique description
+ */
+ public void setDescription(String description) {
+ if (null != description) {
+ mDescription = description;
+ }
+ }
+
+ /**
+ * Returns the unique description associated with the plugin
+ *
+ * @return Unique description
+ */
+ public String getDescriprition() {
+ return mDescription;
+ }
+
+ /**
+ * Overridden hash code implementation
+ *
+ * @return Hash code value
+ */
+ public int hashCode() {
+ return mFileSuffixList.hashCode() + mMimeTypeList.hashCode() + mDescription.hashCode();
+ }
+
+ /**
+ * Overridden equals implementation
+ *
+ * @param object The object to be compared
+ * @return
+ * true if equal
+ * false if not equal
+ */
+ public boolean equals(Object object) {
+ boolean result = false;
+
+ if (object instanceof DrmSupportInfo) {
+ result = mFileSuffixList.equals(((DrmSupportInfo) object).mFileSuffixList) &&
+ mMimeTypeList.equals(((DrmSupportInfo) object).mMimeTypeList) &&
+ mDescription.equals(((DrmSupportInfo) object).mDescription);
+ }
+ return result;
+ }
+
+ /**
+ * Returns whether given mime-type is supported or not
+ *
+ * @param mimeType MIME type
+ * @return
+ * true if mime type is supported
+ * false if mime type is not supported
+ */
+ /* package */ boolean isSupportedMimeType(String mimeType) {
+ if (null != mimeType && !mimeType.equals("")) {
+ for (int i = 0; i < mMimeTypeList.size(); i++) {
+ String completeMimeType = mMimeTypeList.get(i);
+ if (completeMimeType.startsWith(mimeType)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether given file suffix is supported or not
+ *
+ * @param fileSuffix File suffix
+ * @return
+ * true - if file suffix is supported
+ * false - if file suffix is not supported
+ */
+ /* package */ boolean isSupportedFileSuffix(String fileSuffix) {
+ return mFileSuffixList.contains(fileSuffix);
+ }
+}
+
diff --git a/drm/java/android/drm/DrmUtils.java b/drm/java/android/drm/DrmUtils.java
new file mode 100644
index 0000000..8903485
--- /dev/null
+++ b/drm/java/android/drm/DrmUtils.java
@@ -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.
+ */
+
+package android.drm;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * The utility class used in the DRM Framework. This inclueds APIs for file operations
+ * and ExtendedMetadataParser for parsing extended metadata BLOB in DRM constraints.
+ *
+ */
+public class DrmUtils {
+ /* Should be used when we need to read from local file */
+ /* package */ static byte[] readBytes(String path) throws IOException {
+ File file = new File(path);
+ return readBytes(file);
+ }
+
+ /* Should be used when we need to read from local file */
+ /* package */ static byte[] readBytes(File file) throws IOException {
+ FileInputStream inputStream = new FileInputStream(file);
+ BufferedInputStream bufferedStream = new BufferedInputStream(inputStream);
+ byte[] data = null;
+
+ try {
+ int length = bufferedStream.available();
+ if (length > 0) {
+ data = new byte[length];
+ // read the entire data
+ bufferedStream.read(data);
+ }
+ } finally {
+ quiteDispose(bufferedStream);
+ quiteDispose(inputStream);
+ }
+ return data;
+ }
+
+ /* package */ static void writeToFile(final String path, byte[] data) throws IOException {
+ /* check for invalid inputs */
+ FileOutputStream outputStream = null;
+
+ if (null != path && null != data) {
+ try {
+ outputStream = new FileOutputStream(path);
+ outputStream.write(data);
+ } finally {
+ quiteDispose(outputStream);
+ }
+ }
+ }
+
+ /* package */ static void removeFile(String path) throws IOException {
+ File file = new File(path);
+ file.delete();
+ }
+
+ private static void quiteDispose(InputStream stream) {
+ try {
+ if (null != stream) {
+ stream.close();
+ }
+ } catch (IOException e) {
+ // no need to care, at least as of now
+ }
+ }
+
+ private static void quiteDispose(OutputStream stream) {
+ try {
+ if (null != stream) {
+ stream.close();
+ }
+ } catch (IOException e) {
+ // no need to care
+ }
+ }
+
+ /**
+ * Get an instance of ExtendedMetadataParser to be used for parsing
+ * extended metadata BLOB in DRM constraints. <br>
+ *
+ * extendedMetadata BLOB is retrieved by specifing
+ * key DrmStore.ConstraintsColumns.EXTENDED_METADATA.
+ *
+ * @param extendedMetadata BLOB in which key-value pairs of extended metadata are embedded.
+ *
+ */
+ public static ExtendedMetadataParser getExtendedMetadataParser(byte[] extendedMetadata) {
+ return new ExtendedMetadataParser(extendedMetadata);
+ }
+
+ /**
+ * Utility parser to parse the extended meta-data embedded inside DRM constraints<br><br>
+ *
+ * Usage example<br>
+ * byte[] extendedMetadata<br>
+ * =
+ * constraints.getAsByteArray(DrmStore.ConstraintsColumns.EXTENDED_METADATA);<br>
+ * ExtendedMetadataParser parser = getExtendedMetadataParser(extendedMetadata);<br>
+ * Iterator keyIterator = parser.keyIterator();<br>
+ * while (keyIterator.hasNext()) {<br>
+ * String extendedMetadataKey = keyIterator.next();<br>
+ * String extendedMetadataValue =
+ * parser.get(extendedMetadataKey);<br>
+ * }
+ */
+ public static class ExtendedMetadataParser {
+ HashMap<String, String> mMap = new HashMap<String, String>();
+
+ private int readByte(byte[] constraintData, int arrayIndex) {
+ //Convert byte[] into int.
+ return (int)constraintData[arrayIndex];
+ }
+
+ private String readMultipleBytes(
+ byte[] constraintData, int numberOfBytes, int arrayIndex) {
+ byte[] returnBytes = new byte[numberOfBytes];
+ for (int j = arrayIndex, i = 0; j < arrayIndex + numberOfBytes; j++,i++) {
+ returnBytes[i] = constraintData[j];
+ }
+ return new String(returnBytes);
+ }
+
+ /*
+ * This will parse the following format
+ * KeyLengthValueLengthKeyValueKeyLength1ValueLength1Key1Value1..\0
+ */
+ private ExtendedMetadataParser(byte[] constraintData) {
+ //Extract KeyValue Pair Info, till terminator occurs.
+ int index = 0;
+
+ while (index < constraintData.length) {
+ //Parse Key Length
+ int keyLength = readByte(constraintData, index);
+ index++;
+
+ //Parse Value Length
+ int valueLength = readByte(constraintData, index);
+ index++;
+
+ //Fetch key
+ String strKey = readMultipleBytes(constraintData, keyLength, index);
+ index += keyLength;
+
+ //Fetch Value
+ String strValue = readMultipleBytes(constraintData, valueLength, index);
+ if (strValue.equals(" ")) {
+ strValue = "";
+ }
+ index += valueLength;
+ mMap.put(strKey, strValue);
+ }
+ }
+
+ public Iterator<String> iterator() {
+ return mMap.values().iterator();
+ }
+
+ public Iterator<String> keyIterator() {
+ return mMap.keySet().iterator();
+ }
+
+ public String get(String key) {
+ return mMap.get(key);
+ }
+ }
+}
+
diff --git a/drm/java/android/drm/ProcessedData.java b/drm/java/android/drm/ProcessedData.java
new file mode 100644
index 0000000..579264f
--- /dev/null
+++ b/drm/java/android/drm/ProcessedData.java
@@ -0,0 +1,83 @@
+/*
+ * 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.drm;
+
+/**
+ * This is an entity class which wraps the result of transaction between
+ * device and online DRM server by using {@link DrmManagerClient#processDrmInfo(DrmInfo)}
+ *
+ * In license acquisition scenario this class would hold the binary data
+ * of rights information.
+ *
+ */
+public class ProcessedData {
+ private final byte[] mData;
+ private String mAccountId = "_NO_USER";
+ private String mSubscriptionId = "";
+
+ /**
+ * constructor to create ProcessedData object with given parameters
+ *
+ * @param data Rights data
+ * @param accountId Account Id of the user
+ */
+ /* package */ ProcessedData(byte[] data, String accountId) {
+ mData = data;
+ mAccountId = accountId;
+ }
+
+ /**
+ * constructor to create ProcessedData object with given parameters
+ *
+ * @param data Rights data
+ * @param accountId Account Id of the user
+ * @param subscriptionId Subscription Id of the user
+ */
+ /* package */ ProcessedData(byte[] data, String accountId, String subscriptionId) {
+ mData = data;
+ mAccountId = accountId;
+ mSubscriptionId = subscriptionId;
+ }
+
+ /**
+ * Returns the processed data as a result.
+ *
+ * @return Rights data associated
+ */
+ public byte[] getData() {
+ return mData;
+ }
+
+ /**
+ * Returns the account-id associated with this object
+ *
+ * @return Account Id associated
+ */
+ public String getAccountId() {
+ return mAccountId;
+ }
+
+ /**
+ * Returns the subscription-id associated with this object
+ *
+ * @return Subscription Id associated
+ */
+ public String getSubscriptionId() {
+ return mSubscriptionId;
+ }
+}
+
diff --git a/drm/jni/Android.mk b/drm/jni/Android.mk
new file mode 100644
index 0000000..b65e4da
--- /dev/null
+++ b/drm/jni/Android.mk
@@ -0,0 +1,50 @@
+#
+# 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:= \
+ android_drm_DrmManagerClient.cpp
+
+LOCAL_MODULE:= libdrmframework_jni
+
+LOCAL_SHARED_LIBRARIES := \
+ libdrmframework \
+ libutils \
+ libandroid_runtime \
+ libnativehelper \
+ libbinder
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_STATIC_LIBRARIES :=
+
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE) \
+ $(TOP)/frameworks/base/drm/libdrmframework/include \
+ $(TOP)/frameworks/base/drm/libdrmframework/plugins/common/include \
+ $(TOP)/frameworks/base/include
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
new file mode 100644
index 0000000..e131839
--- /dev/null
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -0,0 +1,795 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android_drm_DrmManagerClient"
+#include <utils/Log.h>
+
+#include <jni.h>
+#include <JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <drm/DrmInfo.h>
+#include <drm/DrmRights.h>
+#include <drm/DrmInfoEvent.h>
+#include <drm/DrmInfoStatus.h>
+#include <drm/DrmInfoRequest.h>
+#include <drm/DrmSupportInfo.h>
+#include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
+#include <drm/DrmConvertedStatus.h>
+#include <drm/drm_framework_common.h>
+
+#include <DrmManagerClientImpl.h>
+
+using namespace android;
+
+/**
+ * Utility class used to extract the value from the provided java object.
+ * May need to add some utility function to create java object.
+ */
+class Utility {
+public:
+ static String8 getStringValue(JNIEnv* env, jobject object, const char* fieldName);
+
+ static char* getByteArrayValue(
+ JNIEnv* env, jobject object, const char* fieldName, int* dataLength);
+
+ static char* getByteArrayValue(
+ JNIEnv* env, jbyteArray byteArray, int* dataLength);
+
+ static String8 getStringValue(JNIEnv* env, jstring string);
+
+ static int getIntValue(JNIEnv* env, jobject object, const char* fieldName);
+};
+
+String8 Utility::getStringValue(JNIEnv* env, jobject object, const char* fieldName) {
+ String8 dataString("");
+
+ /* Look for the instance field with the name fieldName */
+ jfieldID fieldID
+ = env->GetFieldID(env->GetObjectClass(object), fieldName , "Ljava/lang/String;");
+
+ if (NULL != fieldID) {
+ jstring valueString = (jstring) env->GetObjectField(object, fieldID);
+
+ if (NULL != valueString && valueString != env->NewStringUTF("")) {
+ char* bytes = const_cast< char* > (env->GetStringUTFChars(valueString, NULL));
+
+ const int length = strlen(bytes) + 1;
+ char *data = new char[length];
+ strncpy(data, bytes, length);
+ dataString = String8(data);
+
+ env->ReleaseStringUTFChars(valueString, bytes);
+ delete [] data; data = NULL;
+ } else {
+ LOGV("Failed to retrieve the data from the field %s", fieldName);
+ }
+ }
+ return dataString;
+}
+
+String8 Utility::getStringValue(JNIEnv* env, jstring string) {
+ String8 dataString("");
+
+ if (NULL != string && string != env->NewStringUTF("")) {
+ char* bytes = const_cast< char* > (env->GetStringUTFChars(string, NULL));
+
+ const int length = strlen(bytes) + 1;
+ char *data = new char[length];
+ strncpy(data, bytes, length);
+ dataString = String8(data);
+
+ env->ReleaseStringUTFChars(string, bytes);
+ delete [] data; data = NULL;
+ }
+ return dataString;
+}
+
+char* Utility::getByteArrayValue(
+ JNIEnv* env, jobject object, const char* fieldName, int* dataLength) {
+ char* data = NULL;
+ *dataLength = 0;
+
+ jfieldID fieldID = env->GetFieldID(env->GetObjectClass(object), fieldName , "[B");
+
+ if (NULL != fieldID) {
+ jbyteArray byteArray = (jbyteArray) env->GetObjectField(object, fieldID);
+ if (NULL != byteArray) {
+ jint length = env->GetArrayLength(byteArray);
+
+ *dataLength = length;
+ if (0 < *dataLength) {
+ data = new char[length];
+ env->GetByteArrayRegion(byteArray, (jint)0, length, (jbyte *) data);
+ }
+ }
+ }
+ return data;
+}
+
+char* Utility::getByteArrayValue(JNIEnv* env, jbyteArray byteArray, int* dataLength) {
+ char* data = NULL;
+ if (NULL != byteArray) {
+ jint length = env->GetArrayLength(byteArray);
+
+ *dataLength = length;
+ if (0 < *dataLength) {
+ data = new char[length];
+ env->GetByteArrayRegion(byteArray, (jint)0, length, (jbyte *) data);
+ }
+ }
+ return data;
+}
+
+int Utility::getIntValue(JNIEnv* env, jobject object, const char* fieldName) {
+ jfieldID fieldID;
+ int intValue = -1;
+
+ /* Get a reference to obj’s class */
+ jclass clazz = env->GetObjectClass(object);
+ /* Look for the instance field with the name fieldName */
+ fieldID = env->GetFieldID(clazz, fieldName , "I");
+
+ if (NULL != fieldID) {
+ intValue = (int) env->GetIntField(object, fieldID);
+ }
+
+ return intValue;
+}
+
+class JNIOnInfoListener : public DrmManagerClient::OnInfoListener {
+public:
+ JNIOnInfoListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
+
+ virtual ~JNIOnInfoListener();
+ void onInfo(const DrmInfoEvent& event);
+
+private:
+ JNIOnInfoListener();
+ jclass mClass;
+ jobject mObject;
+};
+
+JNIOnInfoListener::JNIOnInfoListener(JNIEnv* env, jobject thiz, jobject weak_thiz) {
+ jclass clazz = env->GetObjectClass(thiz);
+
+ if (clazz == NULL) {
+ LOGE("Can't find android/drm/DrmManagerClient");
+ jniThrowException(env, "java/lang/Exception", NULL);
+ return;
+ }
+ mClass = (jclass)env->NewGlobalRef(clazz);
+ mObject = env->NewGlobalRef(weak_thiz);
+}
+
+JNIOnInfoListener::~JNIOnInfoListener() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->DeleteGlobalRef(mObject);
+ env->DeleteGlobalRef(mClass);
+}
+
+void JNIOnInfoListener::onInfo(const DrmInfoEvent& event) {
+ jint uniqueId = event.getUniqueId();
+ jint type = event.getType();
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jstring message = env->NewStringUTF(event.getMessage().string());
+ LOGV("JNIOnInfoListener::onInfo => %d | %d | %s", uniqueId, type, event.getMessage().string());
+
+ env->CallStaticVoidMethod(
+ mClass,
+ env->GetStaticMethodID(mClass, "notify", "(Ljava/lang/Object;IILjava/lang/String;)V"),
+ mObject, uniqueId, type, message);
+}
+
+static Mutex sLock;
+
+static sp<DrmManagerClientImpl> setDrmManagerClientImpl(
+ JNIEnv* env, jobject thiz, const sp<DrmManagerClientImpl>& client) {
+ Mutex::Autolock l(sLock);
+ jclass clazz = env->FindClass("android/drm/DrmManagerClient");
+ jfieldID fieldId = env->GetFieldID(clazz, "mNativeContext", "I");
+
+ sp<DrmManagerClientImpl> old = (DrmManagerClientImpl*)env->GetIntField(thiz, fieldId);
+ if (client.get()) {
+ client->incStrong(thiz);
+ }
+ if (old != 0) {
+ old->decStrong(thiz);
+ }
+ env->SetIntField(thiz, fieldId, (int)client.get());
+ return old;
+}
+
+static sp<DrmManagerClientImpl> getDrmManagerClientImpl(JNIEnv* env, jobject thiz) {
+ Mutex::Autolock l(sLock);
+ jclass clazz = env->FindClass("android/drm/DrmManagerClient");
+ jfieldID fieldId = env->GetFieldID(clazz, "mNativeContext", "I");
+
+ DrmManagerClientImpl* const client = (DrmManagerClientImpl*)env->GetIntField(thiz, fieldId);
+ return sp<DrmManagerClientImpl>(client);
+}
+
+static void android_drm_DrmManagerClient_initialize(
+ JNIEnv* env, jobject thiz, jint uniqueId, jobject weak_thiz) {
+ LOGV("initialize - Enter");
+
+ sp<DrmManagerClientImpl> drmManager = DrmManagerClientImpl::create(&uniqueId);
+ drmManager->addClient(uniqueId);
+
+ // Set the listener to DrmManager
+ sp<DrmManagerClient::OnInfoListener> listener = new JNIOnInfoListener(env, thiz, weak_thiz);
+ drmManager->setOnInfoListener(uniqueId, listener);
+
+ setDrmManagerClientImpl(env, thiz, drmManager);
+ LOGV("initialize - Exit");
+}
+
+static void android_drm_DrmManagerClient_finalize(JNIEnv* env, jobject thiz, jint uniqueId) {
+ LOGV("finalize - Enter");
+ DrmManagerClientImpl::remove(uniqueId);
+ getDrmManagerClientImpl(env, thiz)->setOnInfoListener(uniqueId, NULL);
+
+ sp<DrmManagerClientImpl> oldClient = setDrmManagerClientImpl(env, thiz, NULL);
+ if (oldClient != NULL) {
+ oldClient->setOnInfoListener(uniqueId, NULL);
+ oldClient->removeClient(uniqueId);
+ }
+ LOGV("finalize - Exit");
+}
+
+static jobject android_drm_DrmManagerClient_getConstraintsFromContent(
+ JNIEnv* env, jobject thiz, jint uniqueId, jstring jpath, jint usage) {
+ LOGV("GetConstraints - Enter");
+
+ const String8 pathString = Utility::getStringValue(env, jpath);
+ DrmConstraints* pConstraints
+ = getDrmManagerClientImpl(env, thiz)->getConstraints(uniqueId, &pathString, usage);
+
+ jclass localRef = env->FindClass("android/content/ContentValues");
+ jobject constraints = NULL;
+
+ if (NULL != localRef && NULL != pConstraints) {
+ // Get the constructor id
+ jmethodID constructorId = env->GetMethodID(localRef, "<init>", "()V");
+ // create the java DrmConstraints object
+ constraints = env->NewObject(localRef, constructorId);
+
+ DrmConstraints::KeyIterator keyIt = pConstraints->keyIterator();
+
+ while (keyIt.hasNext()) {
+ String8 key = keyIt.next();
+
+ // insert the entry<constraintKey, constraintValue> to newly created java object
+ if (DrmConstraints::EXTENDED_METADATA == key) {
+ const char* value = pConstraints->getAsByteArray(&key);
+ if (NULL != value) {
+ jbyteArray dataArray = env->NewByteArray(strlen(value));
+ env->SetByteArrayRegion(dataArray, 0, strlen(value), (jbyte*)value);
+ env->CallVoidMethod(
+ constraints, env->GetMethodID(localRef, "put", "(Ljava/lang/String;[B)V"),
+ env->NewStringUTF(key.string()), dataArray);
+ }
+ } else {
+ String8 value = pConstraints->get(key);
+ env->CallVoidMethod(
+ constraints,
+ env->GetMethodID(localRef, "put", "(Ljava/lang/String;Ljava/lang/String;)V"),
+ env->NewStringUTF(key.string()), env->NewStringUTF(value.string()));
+ }
+ }
+ }
+
+ delete pConstraints; pConstraints = NULL;
+ LOGV("GetConstraints - Exit");
+ return constraints;
+}
+
+static jobject android_drm_DrmManagerClient_getMetadataFromContent(
+ JNIEnv* env, jobject thiz, jint uniqueId, jstring jpath) {
+ LOGV("GetMetadata - Enter");
+ const String8 pathString = Utility::getStringValue(env, jpath);
+ DrmMetadata* pMetadata =
+ getDrmManagerClientImpl(env, thiz)->getMetadata(uniqueId, &pathString);
+
+ jobject metadata = NULL;
+
+ jclass localRef = NULL;
+ localRef = env->FindClass("android/content/ContentValues");
+ if (NULL != localRef && NULL != pMetadata) {
+ // Get the constructor id
+ jmethodID constructorId = NULL;
+ constructorId = env->GetMethodID(localRef, "<init>", "()V");
+ if (NULL != constructorId) {
+ // create the java DrmMetadata object
+ metadata = env->NewObject(localRef, constructorId);
+ if (NULL != metadata) {
+ DrmMetadata::KeyIterator keyIt = pMetadata->keyIterator();
+ while (keyIt.hasNext()) {
+ String8 key = keyIt.next();
+ // insert the entry<constraintKey, constraintValue>
+ // to newly created java object
+ String8 value = pMetadata->get(key);
+ env->CallVoidMethod(metadata, env->GetMethodID(localRef, "put",
+ "(Ljava/lang/String;Ljava/lang/String;)V"),
+ env->NewStringUTF(key.string()), env->NewStringUTF(value.string()));
+ }
+ }
+ }
+ }
+ delete pMetadata; pMetadata = NULL;
+ LOGV("GetMetadata - Exit");
+ return metadata;
+}
+
+static jobjectArray android_drm_DrmManagerClient_getAllSupportInfo(
+ JNIEnv* env, jobject thiz, jint uniqueId) {
+ LOGV("GetAllSupportInfo - Enter");
+ DrmSupportInfo* drmSupportInfoArray = NULL;
+
+ int length = 0;
+ getDrmManagerClientImpl(env, thiz)->getAllSupportInfo(uniqueId, &length, &drmSupportInfoArray);
+
+ jclass clazz = env->FindClass("android/drm/DrmSupportInfo");
+
+ jobjectArray array = (jobjectArray)env->NewObjectArray(length, clazz, NULL);
+
+ for (int i = 0; i < length; i++) {
+ DrmSupportInfo info = drmSupportInfoArray[i];
+
+ jobject drmSupportInfo = env->NewObject(clazz, env->GetMethodID(clazz, "<init>", "()V"));
+
+ jmethodID addMimeTypeId
+ = env->GetMethodID(clazz, "addMimeType", "(Ljava/lang/String;)V");
+ jmethodID addFileSuffixId
+ = env->GetMethodID(clazz, "addFileSuffix", "(Ljava/lang/String;)V");
+
+ env->CallVoidMethod(
+ drmSupportInfo, env->GetMethodID(clazz, "setDescription", "(Ljava/lang/String;)V"),
+ env->NewStringUTF(info.getDescription().string()));
+
+ DrmSupportInfo::MimeTypeIterator iterator = info.getMimeTypeIterator();
+ while (iterator.hasNext()) {
+ String8 value = iterator.next();
+ env->CallVoidMethod(drmSupportInfo, addMimeTypeId, env->NewStringUTF(value.string()));
+ }
+
+ DrmSupportInfo::FileSuffixIterator it = info.getFileSuffixIterator();
+ while (it.hasNext()) {
+ String8 value = it.next();
+ env->CallVoidMethod(
+ drmSupportInfo, addFileSuffixId, env->NewStringUTF(value.string()));
+ }
+
+ env->SetObjectArrayElement(array, i, drmSupportInfo);
+ }
+
+ delete [] drmSupportInfoArray; drmSupportInfoArray = NULL;
+ LOGV("GetAllSupportInfo - Exit");
+ return array;
+}
+
+static void android_drm_DrmManagerClient_installDrmEngine(
+ JNIEnv* env, jobject thiz, jint uniqueId, jstring engineFilePath) {
+ LOGV("installDrmEngine - Enter");
+ //getDrmManagerClient(env, thiz)
+ // ->installDrmEngine(uniqueId, Utility::getStringValue(env, engineFilePath));
+ LOGV("installDrmEngine - Exit");
+}
+
+static jint android_drm_DrmManagerClient_saveRights(
+ JNIEnv* env, jobject thiz, jint uniqueId,
+ jobject drmRights, jstring rightsPath, jstring contentPath) {
+ LOGV("saveRights - Enter");
+ int result = DRM_ERROR_UNKNOWN;
+ int dataLength = 0;
+ char* mData = Utility::getByteArrayValue(env, drmRights, "mData", &dataLength);
+
+ if (NULL != mData) {
+ DrmRights rights(DrmBuffer(mData, dataLength),
+ Utility::getStringValue(env, drmRights, "mMimeType"),
+ Utility::getStringValue(env, drmRights, "mAccountId"),
+ Utility::getStringValue(env, drmRights, "mSubscriptionId"));
+ result = getDrmManagerClientImpl(env, thiz)
+ ->saveRights(uniqueId, rights, Utility::getStringValue(env, rightsPath),
+ Utility::getStringValue(env, contentPath));
+ }
+
+ delete mData; mData = NULL;
+ LOGV("saveRights - Exit");
+ return result;
+}
+
+static jboolean android_drm_DrmManagerClient_canHandle(
+ JNIEnv* env, jobject thiz, jint uniqueId, jstring path, jstring mimeType) {
+ LOGV("canHandle - Enter");
+ jboolean result
+ = getDrmManagerClientImpl(env, thiz)
+ ->canHandle(uniqueId, Utility::getStringValue(env, path),
+ Utility::getStringValue(env, mimeType));
+ LOGV("canHandle - Exit");
+ return result;
+}
+
+static jobject android_drm_DrmManagerClient_processDrmInfo(
+ JNIEnv* env, jobject thiz, jint uniqueId, jobject drmInfoObject) {
+ LOGV("processDrmInfo - Enter");
+ int dataLength = 0;
+ const String8 mMimeType = Utility::getStringValue(env, drmInfoObject, "mMimeType");
+ char* mData = Utility::getByteArrayValue(env, drmInfoObject, "mData", &dataLength);
+ int mInfoType = Utility::getIntValue(env, drmInfoObject, "mInfoType");
+
+ const DrmBuffer buffer(mData, dataLength);
+ DrmInfo drmInfo(mInfoType, buffer, mMimeType);
+
+ jclass clazz = env->FindClass("android/drm/DrmInfo");
+ jobject keyIterator
+ = env->CallObjectMethod(drmInfoObject,
+ env->GetMethodID(clazz, "keyIterator", "()Ljava/util/Iterator;"));
+
+ jmethodID hasNextId = env->GetMethodID(env->FindClass("java/util/Iterator"), "hasNext", "()Z");
+
+ while (env->CallBooleanMethod(keyIterator, hasNextId)) {
+ jstring key = (jstring) env->CallObjectMethod(keyIterator,
+ env->GetMethodID(env->FindClass("java/util/Iterator"),
+ "next", "()Ljava/lang/Object;"));
+
+ jobject valueObject = env->CallObjectMethod(drmInfoObject,
+ env->GetMethodID(clazz, "get", "(Ljava/lang/String;)Ljava/lang/Object;"), key);
+
+ jstring valString = NULL;
+ if (NULL != valueObject) {
+ valString = (jstring) env->CallObjectMethod(valueObject,
+ env->GetMethodID(env->FindClass("java/lang/Object"),
+ "toString", "()Ljava/lang/String;"));
+ }
+
+ String8 keyString = Utility::getStringValue(env, key);
+ String8 valueString = Utility::getStringValue(env, valString);
+ LOGV("Key: %s | Value: %s", keyString.string(), valueString.string());
+
+ drmInfo.put(keyString, valueString);
+ }
+
+ DrmInfoStatus* pDrmInfoStatus
+ = getDrmManagerClientImpl(env, thiz)->processDrmInfo(uniqueId, &drmInfo);
+
+ jclass localRef = env->FindClass("android/drm/DrmInfoStatus");
+ jobject drmInfoStatus = NULL;
+
+ if (NULL != localRef && NULL != pDrmInfoStatus) {
+ int statusCode = pDrmInfoStatus->statusCode;
+ int infoType = pDrmInfoStatus->infoType;
+
+ jbyteArray dataArray = NULL;
+ if (NULL != pDrmInfoStatus->drmBuffer) {
+ int length = pDrmInfoStatus->drmBuffer->length;
+ dataArray = env->NewByteArray(length);
+ env->SetByteArrayRegion(
+ dataArray, 0, length, (jbyte*) pDrmInfoStatus->drmBuffer->data);
+
+ delete [] pDrmInfoStatus->drmBuffer->data;
+ delete pDrmInfoStatus->drmBuffer; pDrmInfoStatus->drmBuffer = NULL;
+ }
+ jclass clazz = env->FindClass("android/drm/ProcessedData");
+ jmethodID constructorId
+ = env->GetMethodID(clazz, "<init>", "([BLjava/lang/String;Ljava/lang/String;)V");
+ jobject processedData = env->NewObject(clazz, constructorId, dataArray,
+ env->NewStringUTF((drmInfo.get(DrmInfoRequest::ACCOUNT_ID)).string()),
+ env->NewStringUTF((drmInfo.get(DrmInfoRequest::SUBSCRIPTION_ID)).string()));
+
+ constructorId
+ = env->GetMethodID(localRef,
+ "<init>", "(IILandroid/drm/ProcessedData;Ljava/lang/String;)V");
+
+ drmInfoStatus = env->NewObject(localRef, constructorId, statusCode, infoType,
+ processedData, env->NewStringUTF(pDrmInfoStatus->mimeType.string()));
+ }
+
+ delete mData; mData = NULL;
+ delete pDrmInfoStatus; pDrmInfoStatus = NULL;
+
+ LOGV("processDrmInfo - Exit");
+ return drmInfoStatus;
+}
+
+static jobject android_drm_DrmManagerClient_acquireDrmInfo(
+ JNIEnv* env, jobject thiz, jint uniqueId, jobject drmInfoRequest) {
+ LOGV("acquireDrmInfo Enter");
+ const String8 mMimeType = Utility::getStringValue(env, drmInfoRequest, "mMimeType");
+ int mInfoType = Utility::getIntValue(env, drmInfoRequest, "mInfoType");
+
+ DrmInfoRequest drmInfoReq(mInfoType, mMimeType);
+
+ jclass clazz = env->FindClass("android/drm/DrmInfoRequest");
+ jobject keyIterator
+ = env->CallObjectMethod(drmInfoRequest,
+ env->GetMethodID(clazz, "keyIterator", "()Ljava/util/Iterator;"));
+
+ jmethodID hasNextId = env->GetMethodID(env->FindClass("java/util/Iterator"), "hasNext", "()Z");
+
+ while (env->CallBooleanMethod(keyIterator, hasNextId)) {
+ jstring key
+ = (jstring) env->CallObjectMethod(keyIterator,
+ env->GetMethodID(env->FindClass("java/util/Iterator"),
+ "next", "()Ljava/lang/Object;"));
+
+ jstring value = (jstring) env->CallObjectMethod(drmInfoRequest,
+ env->GetMethodID(clazz, "get", "(Ljava/lang/String;)Ljava/lang/Object;"), key);
+
+ String8 keyString = Utility::getStringValue(env, key);
+ String8 valueString = Utility::getStringValue(env, value);
+ LOGV("Key: %s | Value: %s", keyString.string(), valueString.string());
+
+ drmInfoReq.put(keyString, valueString);
+ }
+
+ DrmInfo* pDrmInfo = getDrmManagerClientImpl(env, thiz)->acquireDrmInfo(uniqueId, &drmInfoReq);
+
+ jobject drmInfoObject = NULL;
+
+ if (NULL != pDrmInfo) {
+ jclass localRef = env->FindClass("android/drm/DrmInfo");
+
+ if (NULL != localRef) {
+ int length = pDrmInfo->getData().length;
+
+ jbyteArray dataArray = env->NewByteArray(length);
+ env->SetByteArrayRegion(dataArray, 0, length, (jbyte*)pDrmInfo->getData().data);
+
+ drmInfoObject
+ = env->NewObject(localRef,
+ env->GetMethodID(localRef, "<init>", "(I[BLjava/lang/String;)V"),
+ mInfoType, dataArray, env->NewStringUTF(pDrmInfo->getMimeType().string()));
+
+ DrmInfo::KeyIterator it = pDrmInfo->keyIterator();
+ jmethodID putMethodId
+ = env->GetMethodID(localRef, "put", "(Ljava/lang/String;Ljava/lang/Object;)V");
+
+ while (it.hasNext()) {
+ String8 key = it.next();
+ String8 value = pDrmInfo->get(key);
+
+ env->CallVoidMethod(drmInfoObject, putMethodId,
+ env->NewStringUTF(key.string()), env->NewStringUTF(value.string()));
+ }
+ }
+ delete [] pDrmInfo->getData().data;
+ }
+
+ delete pDrmInfo; pDrmInfo = NULL;
+
+ LOGV("acquireDrmInfo Exit");
+ return drmInfoObject;
+}
+
+static jint android_drm_DrmManagerClient_getDrmObjectType(
+ JNIEnv* env, jobject thiz, jint uniqueId, jstring path, jstring mimeType) {
+ LOGV("getDrmObjectType Enter");
+ int drmObjectType
+ = getDrmManagerClientImpl(env, thiz)
+ ->getDrmObjectType(uniqueId, Utility::getStringValue(env, path),
+ Utility::getStringValue(env, mimeType));
+ LOGV("getDrmObjectType Exit");
+ return drmObjectType;
+}
+
+static jstring android_drm_DrmManagerClient_getOriginalMimeType(
+ JNIEnv* env, jobject thiz, jint uniqueId, jstring path) {
+ LOGV("getOriginalMimeType Enter");
+ String8 mimeType
+ = getDrmManagerClientImpl(env, thiz)
+ ->getOriginalMimeType(uniqueId, Utility::getStringValue(env, path));
+ LOGV("getOriginalMimeType Exit");
+ return env->NewStringUTF(mimeType.string());
+}
+
+static jint android_drm_DrmManagerClient_checkRightsStatus(
+ JNIEnv* env, jobject thiz, jint uniqueId, jstring path, int action) {
+ LOGV("getOriginalMimeType Enter");
+ int rightsStatus
+ = getDrmManagerClientImpl(env, thiz)
+ ->checkRightsStatus(uniqueId, Utility::getStringValue(env, path), action);
+ LOGV("getOriginalMimeType Exit");
+ return rightsStatus;
+}
+
+static jint android_drm_DrmManagerClient_removeRights(
+ JNIEnv* env, jobject thiz, jint uniqueId, jstring path) {
+ LOGV("removeRights");
+ return getDrmManagerClientImpl(env, thiz)
+ ->removeRights(uniqueId, Utility::getStringValue(env, path));
+}
+
+static jint android_drm_DrmManagerClient_removeAllRights(
+ JNIEnv* env, jobject thiz, jint uniqueId) {
+ LOGV("removeAllRights");
+ return getDrmManagerClientImpl(env, thiz)->removeAllRights(uniqueId);
+}
+
+static jint android_drm_DrmManagerClient_openConvertSession(
+ JNIEnv* env, jobject thiz, jint uniqueId, jstring mimeType) {
+ LOGV("openConvertSession Enter");
+ int convertId
+ = getDrmManagerClientImpl(env, thiz)
+ ->openConvertSession(uniqueId, Utility::getStringValue(env, mimeType));
+ LOGV("openConvertSession Exit");
+ return convertId;
+}
+
+static jobject android_drm_DrmManagerClient_convertData(
+ JNIEnv* env, jobject thiz, jint uniqueId, jint convertId, jbyteArray inputData) {
+ LOGV("convertData Enter");
+
+ int dataLength = 0;
+ char* mData = Utility::getByteArrayValue(env, inputData, &dataLength);
+ const DrmBuffer buffer(mData, dataLength);
+
+ DrmConvertedStatus* pDrmConvertedStatus
+ = getDrmManagerClientImpl(env, thiz)->convertData(uniqueId, convertId, &buffer);
+
+ jclass localRef = env->FindClass("android/drm/DrmConvertedStatus");
+
+ jobject drmConvertedStatus = NULL;
+
+ if (NULL != localRef && NULL != pDrmConvertedStatus) {
+ int statusCode = pDrmConvertedStatus->statusCode;
+
+ jbyteArray dataArray = NULL;
+ if (NULL != pDrmConvertedStatus->convertedData) {
+ int length = pDrmConvertedStatus->convertedData->length;
+ dataArray = env->NewByteArray(length);
+ env->SetByteArrayRegion(dataArray, 0, length,
+ (jbyte*) pDrmConvertedStatus->convertedData->data);
+
+ delete [] pDrmConvertedStatus->convertedData->data;
+ delete pDrmConvertedStatus->convertedData; pDrmConvertedStatus->convertedData = NULL;
+ }
+ jmethodID constructorId = env->GetMethodID(localRef, "<init>", "(I[BI)V");
+ drmConvertedStatus
+ = env->NewObject(localRef, constructorId,
+ statusCode, dataArray, pDrmConvertedStatus->offset);
+ }
+
+ delete mData; mData = NULL;
+ delete pDrmConvertedStatus; pDrmConvertedStatus = NULL;
+
+ LOGV("convertData - Exit");
+ return drmConvertedStatus;
+}
+
+static jobject android_drm_DrmManagerClient_closeConvertSession(
+ JNIEnv* env, jobject thiz, int uniqueId, jint convertId) {
+
+ LOGV("closeConvertSession Enter");
+
+ DrmConvertedStatus* pDrmConvertedStatus
+ = getDrmManagerClientImpl(env, thiz)->closeConvertSession(uniqueId, convertId);
+
+ jclass localRef = env->FindClass("android/drm/DrmConvertedStatus");
+
+ jobject drmConvertedStatus = NULL;
+
+ if (NULL != localRef && NULL != pDrmConvertedStatus) {
+ int statusCode = pDrmConvertedStatus->statusCode;
+
+ jbyteArray dataArray = NULL;
+ if (NULL != pDrmConvertedStatus->convertedData) {
+ int length = pDrmConvertedStatus->convertedData->length;
+ dataArray = env->NewByteArray(length);
+ env->SetByteArrayRegion(
+ dataArray, 0, length, (jbyte*) pDrmConvertedStatus->convertedData->data);
+
+ delete [] pDrmConvertedStatus->convertedData->data;
+ delete pDrmConvertedStatus->convertedData; pDrmConvertedStatus->convertedData = NULL;
+ }
+ jmethodID constructorId = env->GetMethodID(localRef, "<init>", "(I[BI)V");
+ drmConvertedStatus
+ = env->NewObject(localRef, constructorId,
+ statusCode, dataArray, pDrmConvertedStatus->offset);
+ }
+
+ delete pDrmConvertedStatus; pDrmConvertedStatus = NULL;
+
+ LOGV("closeConvertSession - Exit");
+ return drmConvertedStatus;
+}
+
+static JNINativeMethod nativeMethods[] = {
+
+ {"_initialize", "(ILjava/lang/Object;)V",
+ (void*)android_drm_DrmManagerClient_initialize},
+
+ {"_finalize", "(I)V",
+ (void*)android_drm_DrmManagerClient_finalize},
+
+ {"_getConstraints", "(ILjava/lang/String;I)Landroid/content/ContentValues;",
+ (void*)android_drm_DrmManagerClient_getConstraintsFromContent},
+
+ {"_getMetadata", "(ILjava/lang/String;)Landroid/content/ContentValues;",
+ (void*)android_drm_DrmManagerClient_getMetadataFromContent},
+
+ {"_getAllSupportInfo", "(I)[Landroid/drm/DrmSupportInfo;",
+ (void*)android_drm_DrmManagerClient_getAllSupportInfo},
+
+ {"_installDrmEngine", "(ILjava/lang/String;)V",
+ (void*)android_drm_DrmManagerClient_installDrmEngine},
+
+ {"_canHandle", "(ILjava/lang/String;Ljava/lang/String;)Z",
+ (void*)android_drm_DrmManagerClient_canHandle},
+
+ {"_processDrmInfo", "(ILandroid/drm/DrmInfo;)Landroid/drm/DrmInfoStatus;",
+ (void*)android_drm_DrmManagerClient_processDrmInfo},
+
+ {"_acquireDrmInfo", "(ILandroid/drm/DrmInfoRequest;)Landroid/drm/DrmInfo;",
+ (void*)android_drm_DrmManagerClient_acquireDrmInfo},
+
+ {"_saveRights", "(ILandroid/drm/DrmRights;Ljava/lang/String;Ljava/lang/String;)I",
+ (void*)android_drm_DrmManagerClient_saveRights},
+
+ {"_getDrmObjectType", "(ILjava/lang/String;Ljava/lang/String;)I",
+ (void*)android_drm_DrmManagerClient_getDrmObjectType},
+
+ {"_getOriginalMimeType", "(ILjava/lang/String;)Ljava/lang/String;",
+ (void*)android_drm_DrmManagerClient_getOriginalMimeType},
+
+ {"_checkRightsStatus", "(ILjava/lang/String;I)I",
+ (void*)android_drm_DrmManagerClient_checkRightsStatus},
+
+ {"_removeRights", "(ILjava/lang/String;)I",
+ (void*)android_drm_DrmManagerClient_removeRights},
+
+ {"_removeAllRights", "(I)I",
+ (void*)android_drm_DrmManagerClient_removeAllRights},
+
+ {"_openConvertSession", "(ILjava/lang/String;)I",
+ (void*)android_drm_DrmManagerClient_openConvertSession},
+
+ {"_convertData", "(II[B)Landroid/drm/DrmConvertedStatus;",
+ (void*)android_drm_DrmManagerClient_convertData},
+
+ {"_closeConvertSession", "(II)Landroid/drm/DrmConvertedStatus;",
+ (void*)android_drm_DrmManagerClient_closeConvertSession},
+};
+
+static int registerNativeMethods(JNIEnv* env) {
+ int result = -1;
+
+ /* look up the class */
+ jclass clazz = env->FindClass("android/drm/DrmManagerClient");
+
+ if (NULL != clazz) {
+ if (env->RegisterNatives(clazz, nativeMethods, sizeof(nativeMethods)
+ / sizeof(nativeMethods[0])) == JNI_OK) {
+ result = 0;
+ }
+ }
+ return result;
+}
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+ JNIEnv* env = NULL;
+ jint result = -1;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) {
+ if (NULL != env && registerNativeMethods(env) == 0) {
+ result = JNI_VERSION_1_4;
+ }
+ }
+ return result;
+}
+
diff --git a/drm/libdrmframework/Android.mk b/drm/libdrmframework/Android.mk
new file mode 100644
index 0000000..99133ba
--- /dev/null
+++ b/drm/libdrmframework/Android.mk
@@ -0,0 +1,49 @@
+#
+# 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:= \
+ DrmManagerClientImpl.cpp \
+ DrmManagerClient.cpp
+
+LOCAL_MODULE:= libdrmframework
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ libbinder
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_STATIC_LIBRARIES := \
+ libdrmframeworkcommon
+
+LOCAL_C_INCLUDES += \
+ $(TOP)/frameworks/base/drm/libdrmframework/include \
+ $(TOP)/frameworks/base/include
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/drm/libdrmframework/DrmManagerClient.cpp b/drm/libdrmframework/DrmManagerClient.cpp
new file mode 100644
index 0000000..8bb00c3
--- /dev/null
+++ b/drm/libdrmframework/DrmManagerClient.cpp
@@ -0,0 +1,157 @@
+/*
+ * 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 <utils/String8.h>
+#include <binder/IServiceManager.h>
+#include <drm/DrmManagerClient.h>
+
+#include "DrmManagerClientImpl.h"
+
+using namespace android;
+
+DrmManagerClient::DrmManagerClient():
+ mUniqueId(0), mDrmManagerClientImpl(NULL) {
+ mDrmManagerClientImpl = DrmManagerClientImpl::create(&mUniqueId);
+ mDrmManagerClientImpl->addClient(mUniqueId);
+}
+
+DrmManagerClient::~DrmManagerClient() {
+ DrmManagerClientImpl::remove(mUniqueId);
+ mDrmManagerClientImpl->removeClient(mUniqueId);
+ mDrmManagerClientImpl->setOnInfoListener(mUniqueId, NULL);
+ delete mDrmManagerClientImpl; mDrmManagerClientImpl = NULL;
+}
+
+status_t DrmManagerClient::setOnInfoListener(
+ const sp<DrmManagerClient::OnInfoListener>& infoListener) {
+ return mDrmManagerClientImpl->setOnInfoListener(mUniqueId, infoListener);
+}
+
+DrmConstraints* DrmManagerClient::getConstraints(const String8* path, const int action) {
+ return mDrmManagerClientImpl->getConstraints(mUniqueId, path, action);
+}
+
+DrmMetadata* DrmManagerClient::getMetadata(const String8* path) {
+ return mDrmManagerClientImpl->getMetadata(mUniqueId, path);
+}
+
+bool DrmManagerClient::canHandle(const String8& path, const String8& mimeType) {
+ return mDrmManagerClientImpl->canHandle(mUniqueId, path, mimeType);
+}
+
+DrmInfoStatus* DrmManagerClient::processDrmInfo(const DrmInfo* drmInfo) {
+ return mDrmManagerClientImpl->processDrmInfo(mUniqueId, drmInfo);
+}
+
+DrmInfo* DrmManagerClient::acquireDrmInfo(const DrmInfoRequest* drmInfoRequest) {
+ return mDrmManagerClientImpl->acquireDrmInfo(mUniqueId, drmInfoRequest);
+}
+
+status_t DrmManagerClient::saveRights(
+ const DrmRights& drmRights, const String8& rightsPath, const String8& contentPath) {
+ return mDrmManagerClientImpl->saveRights(mUniqueId, drmRights, rightsPath, contentPath);
+}
+
+String8 DrmManagerClient::getOriginalMimeType(const String8& path) {
+ return mDrmManagerClientImpl->getOriginalMimeType(mUniqueId, path);
+}
+
+int DrmManagerClient::getDrmObjectType(const String8& path, const String8& mimeType) {
+ return mDrmManagerClientImpl->getDrmObjectType( mUniqueId, path, mimeType);
+}
+
+int DrmManagerClient::checkRightsStatus(const String8& path, int action) {
+ return mDrmManagerClientImpl->checkRightsStatus(mUniqueId, path, action);
+}
+
+status_t DrmManagerClient::consumeRights(DecryptHandle* decryptHandle, int action, bool reserve) {
+ Mutex::Autolock _l(mDecryptLock);
+ return mDrmManagerClientImpl->consumeRights(mUniqueId, decryptHandle, action, reserve);
+}
+
+status_t DrmManagerClient::setPlaybackStatus(
+ DecryptHandle* decryptHandle, int playbackStatus, int position) {
+ return mDrmManagerClientImpl
+ ->setPlaybackStatus(mUniqueId, decryptHandle, playbackStatus, position);
+}
+
+bool DrmManagerClient::validateAction(
+ const String8& path, int action, const ActionDescription& description) {
+ return mDrmManagerClientImpl->validateAction(mUniqueId, path, action, description);
+}
+
+status_t DrmManagerClient::removeRights(const String8& path) {
+ return mDrmManagerClientImpl->removeRights(mUniqueId, path);
+}
+
+status_t DrmManagerClient::removeAllRights() {
+ return mDrmManagerClientImpl->removeAllRights(mUniqueId);
+}
+
+int DrmManagerClient::openConvertSession(const String8& mimeType) {
+ return mDrmManagerClientImpl->openConvertSession(mUniqueId, mimeType);
+}
+
+DrmConvertedStatus* DrmManagerClient::convertData(int convertId, const DrmBuffer* inputData) {
+ return mDrmManagerClientImpl->convertData(mUniqueId, convertId, inputData);
+}
+
+DrmConvertedStatus* DrmManagerClient::closeConvertSession(int convertId) {
+ return mDrmManagerClientImpl->closeConvertSession(mUniqueId, convertId);
+}
+
+status_t DrmManagerClient::getAllSupportInfo(int* length, DrmSupportInfo** drmSupportInfoArray) {
+ return mDrmManagerClientImpl->getAllSupportInfo(mUniqueId, length, drmSupportInfoArray);
+}
+
+DecryptHandle* DrmManagerClient::openDecryptSession(int fd, int offset, int length) {
+ return mDrmManagerClientImpl->openDecryptSession(mUniqueId, fd, offset, length);
+}
+
+DecryptHandle* DrmManagerClient::openDecryptSession(const char* uri) {
+ return mDrmManagerClientImpl->openDecryptSession(mUniqueId, uri);
+}
+
+status_t DrmManagerClient::closeDecryptSession(DecryptHandle* decryptHandle) {
+ return mDrmManagerClientImpl->closeDecryptSession(mUniqueId, decryptHandle);
+}
+
+status_t DrmManagerClient::initializeDecryptUnit(
+ DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) {
+ Mutex::Autolock _l(mDecryptLock);
+ return mDrmManagerClientImpl->initializeDecryptUnit(
+ mUniqueId, decryptHandle, decryptUnitId, headerInfo);
+}
+
+status_t DrmManagerClient::decrypt(
+ DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+ Mutex::Autolock _l(mDecryptLock);
+ return mDrmManagerClientImpl->decrypt(
+ mUniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
+}
+
+status_t DrmManagerClient::finalizeDecryptUnit(DecryptHandle* decryptHandle, int decryptUnitId) {
+ Mutex::Autolock _l(mDecryptLock);
+ return mDrmManagerClientImpl->finalizeDecryptUnit(mUniqueId, decryptHandle, decryptUnitId);
+}
+
+ssize_t DrmManagerClient::pread(
+ DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset) {
+ Mutex::Autolock _l(mDecryptLock);
+ return mDrmManagerClientImpl->pread(mUniqueId, decryptHandle, buffer, numBytes, offset);
+}
+
diff --git a/drm/libdrmframework/DrmManagerClientImpl.cpp b/drm/libdrmframework/DrmManagerClientImpl.cpp
new file mode 100644
index 0000000..eea312b
--- /dev/null
+++ b/drm/libdrmframework/DrmManagerClientImpl.cpp
@@ -0,0 +1,311 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "DrmManagerClientImpl(Native)"
+#include <utils/Log.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <binder/IServiceManager.h>
+
+#include "DrmManagerClientImpl.h"
+
+using namespace android;
+
+#define INVALID_VALUE -1
+
+Mutex DrmManagerClientImpl::mMutex;
+sp<IDrmManagerService> DrmManagerClientImpl::mDrmManagerService;
+const String8 DrmManagerClientImpl::EMPTY_STRING("");
+
+DrmManagerClientImpl* DrmManagerClientImpl::create(int* pUniqueId) {
+ if (0 == *pUniqueId) {
+ int uniqueId = getDrmManagerService()->addUniqueId(*pUniqueId);
+ *pUniqueId = uniqueId;
+ } else {
+ getDrmManagerService()->addUniqueId(*pUniqueId);
+ }
+ return new DrmManagerClientImpl();
+}
+
+void DrmManagerClientImpl::remove(int uniqueId) {
+ getDrmManagerService()->removeUniqueId(uniqueId);
+}
+
+const sp<IDrmManagerService>& DrmManagerClientImpl::getDrmManagerService() {
+ mMutex.lock();
+ if (NULL == mDrmManagerService.get()) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder;
+ do {
+ binder = sm->getService(String16("drm.drmManager"));
+ if (binder != 0) {
+ break;
+ }
+ LOGW("DrmManagerService not published, waiting...");
+ struct timespec reqt;
+ reqt.tv_sec = 0;
+ reqt.tv_nsec = 500000000; //0.5 sec
+ nanosleep(&reqt, NULL);
+ } while (true);
+
+ mDrmManagerService = interface_cast<IDrmManagerService>(binder);
+ }
+ mMutex.unlock();
+ return mDrmManagerService;
+}
+
+void DrmManagerClientImpl::addClient(int uniqueId) {
+ getDrmManagerService()->addClient(uniqueId);
+}
+
+void DrmManagerClientImpl::removeClient(int uniqueId) {
+ getDrmManagerService()->removeClient(uniqueId);
+}
+
+status_t DrmManagerClientImpl::setOnInfoListener(
+ int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener) {
+ Mutex::Autolock _l(mLock);
+ mOnInfoListener = infoListener;
+ return getDrmManagerService()->setDrmServiceListener(uniqueId,
+ (NULL != infoListener.get()) ? this : NULL);
+}
+
+status_t DrmManagerClientImpl::installDrmEngine(int uniqueId, const String8& drmEngineFile) {
+ status_t status = DRM_ERROR_UNKNOWN;
+ if (EMPTY_STRING != drmEngineFile) {
+ status = getDrmManagerService()->installDrmEngine(uniqueId, drmEngineFile);
+ }
+ return status;
+}
+
+DrmConstraints* DrmManagerClientImpl::getConstraints(
+ int uniqueId, const String8* path, const int action) {
+ DrmConstraints *drmConstraints = NULL;
+ if ((NULL != path) && (EMPTY_STRING != *path)) {
+ drmConstraints = getDrmManagerService()->getConstraints(uniqueId, path, action);
+ }
+ return drmConstraints;
+}
+
+DrmMetadata* DrmManagerClientImpl::getMetadata(int uniqueId, const String8* path) {
+ DrmMetadata *drmMetadata = NULL;
+ if ((NULL != path) && (EMPTY_STRING != *path)) {
+ drmMetadata = getDrmManagerService()->getMetadata(uniqueId, path);
+ }
+ return drmMetadata;
+}
+
+bool DrmManagerClientImpl::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
+ bool retCode = false;
+ if ((EMPTY_STRING != path) || (EMPTY_STRING != mimeType)) {
+ retCode = getDrmManagerService()->canHandle(uniqueId, path, mimeType);
+ }
+ return retCode;
+}
+
+DrmInfoStatus* DrmManagerClientImpl::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+ DrmInfoStatus *drmInfoStatus = NULL;
+ if (NULL != drmInfo) {
+ drmInfoStatus = getDrmManagerService()->processDrmInfo(uniqueId, drmInfo);
+ }
+ return drmInfoStatus;
+}
+
+DrmInfo* DrmManagerClientImpl::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+ DrmInfo* drmInfo = NULL;
+ if (NULL != drmInfoRequest) {
+ drmInfo = getDrmManagerService()->acquireDrmInfo(uniqueId, drmInfoRequest);
+ }
+ return drmInfo;
+}
+
+status_t DrmManagerClientImpl::saveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath) {
+ status_t status = DRM_ERROR_UNKNOWN;
+ if (EMPTY_STRING != contentPath) {
+ status = getDrmManagerService()->saveRights(uniqueId, drmRights, rightsPath, contentPath);
+ }
+ return status;
+}
+
+String8 DrmManagerClientImpl::getOriginalMimeType(int uniqueId, const String8& path) {
+ String8 mimeType = EMPTY_STRING;
+ if (EMPTY_STRING != path) {
+ mimeType = getDrmManagerService()->getOriginalMimeType(uniqueId, path);
+ }
+ return mimeType;
+}
+
+int DrmManagerClientImpl::getDrmObjectType(
+ int uniqueId, const String8& path, const String8& mimeType) {
+ int drmOjectType = DrmObjectType::UNKNOWN;
+ if ((EMPTY_STRING != path) || (EMPTY_STRING != mimeType)) {
+ drmOjectType = getDrmManagerService()->getDrmObjectType(uniqueId, path, mimeType);
+ }
+ return drmOjectType;
+}
+
+int DrmManagerClientImpl::checkRightsStatus(
+ int uniqueId, const String8& path, int action) {
+ int rightsStatus = RightsStatus::RIGHTS_INVALID;
+ if (EMPTY_STRING != path) {
+ rightsStatus = getDrmManagerService()->checkRightsStatus(uniqueId, path, action);
+ }
+ return rightsStatus;
+}
+
+status_t DrmManagerClientImpl::consumeRights(
+ int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+ status_t status = DRM_ERROR_UNKNOWN;
+ if (NULL != decryptHandle) {
+ status = getDrmManagerService()->consumeRights(uniqueId, decryptHandle, action, reserve);
+ }
+ return status;
+}
+
+status_t DrmManagerClientImpl::setPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+ status_t status = DRM_ERROR_UNKNOWN;
+ if (NULL != decryptHandle) {
+ status = getDrmManagerService()->setPlaybackStatus(
+ uniqueId, decryptHandle, playbackStatus, position);
+ }
+ return status;
+}
+
+bool DrmManagerClientImpl::validateAction(
+ int uniqueId, const String8& path, int action, const ActionDescription& description) {
+ bool retCode = false;
+ if (EMPTY_STRING != path) {
+ retCode = getDrmManagerService()->validateAction(uniqueId, path, action, description);
+ }
+ return retCode;
+}
+
+status_t DrmManagerClientImpl::removeRights(int uniqueId, const String8& path) {
+ status_t status = DRM_ERROR_UNKNOWN;
+ if (EMPTY_STRING != path) {
+ status = getDrmManagerService()->removeRights(uniqueId, path);
+ }
+ return status;
+}
+
+status_t DrmManagerClientImpl::removeAllRights(int uniqueId) {
+ return getDrmManagerService()->removeAllRights(uniqueId);
+}
+
+int DrmManagerClientImpl::openConvertSession(int uniqueId, const String8& mimeType) {
+ int retCode = INVALID_VALUE;
+ if (EMPTY_STRING != mimeType) {
+ retCode = getDrmManagerService()->openConvertSession(uniqueId, mimeType);
+ }
+ return retCode;
+}
+
+DrmConvertedStatus* DrmManagerClientImpl::convertData(
+ int uniqueId, int convertId, const DrmBuffer* inputData) {
+ DrmConvertedStatus* drmConvertedStatus = NULL;
+ if (NULL != inputData) {
+ drmConvertedStatus = getDrmManagerService()->convertData(uniqueId, convertId, inputData);
+ }
+ return drmConvertedStatus;
+}
+
+DrmConvertedStatus* DrmManagerClientImpl::closeConvertSession(int uniqueId, int convertId) {
+ return getDrmManagerService()->closeConvertSession(uniqueId, convertId);
+}
+
+status_t DrmManagerClientImpl::getAllSupportInfo(
+ int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) {
+ status_t status = DRM_ERROR_UNKNOWN;
+ if ((NULL != drmSupportInfoArray) && (NULL != length)) {
+ status = getDrmManagerService()->getAllSupportInfo(uniqueId, length, drmSupportInfoArray);
+ }
+ return status;
+}
+
+DecryptHandle* DrmManagerClientImpl::openDecryptSession(
+ int uniqueId, int fd, int offset, int length) {
+ return getDrmManagerService()->openDecryptSession(uniqueId, fd, offset, length);
+}
+
+DecryptHandle* DrmManagerClientImpl::openDecryptSession(int uniqueId, const char* uri) {
+ DecryptHandle* handle = NULL;
+ if (NULL != uri) {
+ handle = getDrmManagerService()->openDecryptSession(uniqueId, uri);
+ }
+ return handle;
+}
+
+status_t DrmManagerClientImpl::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+ status_t status = DRM_ERROR_UNKNOWN;
+ if (NULL != decryptHandle) {
+ status = getDrmManagerService()->closeDecryptSession( uniqueId, decryptHandle);
+ }
+ return status;
+}
+
+status_t DrmManagerClientImpl::initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo) {
+ status_t status = DRM_ERROR_UNKNOWN;
+ if ((NULL != decryptHandle) && (NULL != headerInfo)) {
+ status = getDrmManagerService()->initializeDecryptUnit(
+ uniqueId, decryptHandle, decryptUnitId, headerInfo);
+ }
+ return status;
+}
+
+status_t DrmManagerClientImpl::decrypt(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+ status_t status = DRM_ERROR_UNKNOWN;
+ if ((NULL != decryptHandle) && (NULL != encBuffer)
+ && (NULL != decBuffer) && (NULL != *decBuffer)) {
+ status = getDrmManagerService()->decrypt(
+ uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
+ }
+ return status;
+}
+
+status_t DrmManagerClientImpl::finalizeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+ status_t status = DRM_ERROR_UNKNOWN;
+ if (NULL != decryptHandle) {
+ status
+ = getDrmManagerService()->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
+ }
+ return status;
+}
+
+ssize_t DrmManagerClientImpl::pread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off_t offset) {
+ ssize_t retCode = INVALID_VALUE;
+ if ((NULL != decryptHandle) && (NULL != buffer) && (0 < numBytes)) {
+ retCode = getDrmManagerService()->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
+ }
+ return retCode;
+}
+
+status_t DrmManagerClientImpl::notify(const DrmInfoEvent& event) {
+ if (NULL != mOnInfoListener.get()) {
+ Mutex::Autolock _l(mLock);
+ sp<DrmManagerClient::OnInfoListener> listener = mOnInfoListener;
+ listener->onInfo(event);
+ }
+ return DRM_NO_ERROR;
+}
+
diff --git a/drm/libdrmframework/include/DrmIOService.h b/drm/libdrmframework/include/DrmIOService.h
new file mode 100644
index 0000000..244124e
--- /dev/null
+++ b/drm/libdrmframework/include/DrmIOService.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 __DRM_IO_SERVICE_H__
+#define __DRM_IO_SERVICE_H__
+
+#include "IDrmIOService.h"
+
+namespace android {
+
+/**
+ * This is the implementation class for DRM IO service.
+ *
+ * The instance of this class is created while starting the DRM IO service.
+ *
+ */
+class DrmIOService : public BnDrmIOService {
+public:
+ static void instantiate();
+
+private:
+ DrmIOService();
+ virtual ~DrmIOService();
+
+public:
+ void writeToFile(const String8& filePath, const String8& dataBuffer);
+ String8 readFromFile(const String8& filePath);
+};
+
+};
+
+#endif /* __DRM_IO_SERVICE_H__ */
+
diff --git a/drm/libdrmframework/include/DrmManager.h b/drm/libdrmframework/include/DrmManager.h
new file mode 100644
index 0000000..bc462c2
--- /dev/null
+++ b/drm/libdrmframework/include/DrmManager.h
@@ -0,0 +1,162 @@
+/*
+ * 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 __DRM_MANAGER_H__
+#define __DRM_MANAGER_H__
+
+#include <utils/Errors.h>
+#include <utils/threads.h>
+#include <drm/drm_framework_common.h>
+#include "IDrmEngine.h"
+#include "PlugInManager.h"
+#include "IDrmServiceListener.h"
+
+namespace android {
+
+class IDrmManager;
+class DrmRegistrationInfo;
+class DrmUnregistrationInfo;
+class DrmRightsAcquisitionInfo;
+class DrmContentIds;
+class DrmConstraints;
+class DrmMetadata;
+class DrmRights;
+class DrmInfo;
+class DrmInfoStatus;
+class DrmConvertedStatus;
+class DrmInfoRequest;
+class DrmSupportInfo;
+class ActionDescription;
+
+/**
+ * This is implementation class for DRM Manager. This class delegates the
+ * functionality to corresponding DRM Engine.
+ *
+ * The DrmManagerService class creates an instance of this class.
+ *
+ */
+class DrmManager : public IDrmEngine::OnInfoListener {
+public:
+ DrmManager();
+ virtual ~DrmManager();
+
+public:
+ int addUniqueId(int uniqueId);
+
+ void removeUniqueId(int uniqueId);
+
+ void addClient(int uniqueId);
+
+ void removeClient(int uniqueId);
+
+ status_t loadPlugIns();
+
+ status_t loadPlugIns(const String8& plugInDirPath);
+
+ status_t unloadPlugIns();
+
+ status_t setDrmServiceListener(
+ int uniqueId, const sp<IDrmServiceListener>& drmServiceListener);
+
+ status_t installDrmEngine(int uniqueId, const String8& drmEngineFile);
+
+ DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
+
+ DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
+ bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
+
+ DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+ DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest);
+
+ status_t saveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath);
+
+ String8 getOriginalMimeType(int uniqueId, const String8& path);
+
+ int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+
+ int checkRightsStatus(int uniqueId, const String8& path, int action);
+
+ status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+
+ status_t setPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+ bool validateAction(
+ int uniqueId, const String8& path, int action, const ActionDescription& description);
+
+ status_t removeRights(int uniqueId, const String8& path);
+
+ status_t removeAllRights(int uniqueId);
+
+ int openConvertSession(int uniqueId, const String8& mimeType);
+
+ DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData);
+
+ DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId);
+
+ status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
+
+ DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+
+ DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
+
+ status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+
+ status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo);
+
+ status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
+
+ status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+
+ ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off_t offset);
+
+ void onInfo(const DrmInfoEvent& event);
+
+private:
+ String8 getSupportedPlugInId(int uniqueId, const String8& path, const String8& mimeType);
+
+ String8 getSupportedPlugInId(const String8& mimeType);
+
+ String8 getSupportedPlugInIdFromPath(int uniqueId, const String8& path);
+
+ bool canHandle(int uniqueId, const String8& path);
+
+private:
+ static Vector<int> mUniqueIdVector;
+ static const String8 EMPTY_STRING;
+
+ int mDecryptSessionId;
+ int mConvertId;
+ Mutex mLock;
+ Mutex mDecryptLock;
+ Mutex mConvertLock;
+ TPlugInManager<IDrmEngine> mPlugInManager;
+ KeyedVector< DrmSupportInfo, String8 > mSupportInfoToPlugInIdMap;
+ KeyedVector< int, IDrmEngine*> mConvertSessionMap;
+ KeyedVector< int, sp<IDrmServiceListener> > mServiceListeners;
+ KeyedVector< int, IDrmEngine*> mDecryptSessionMap;
+};
+
+};
+
+#endif /* __DRM_MANAGER_H__ */
+
diff --git a/drm/libdrmframework/include/DrmManagerClientImpl.h b/drm/libdrmframework/include/DrmManagerClientImpl.h
new file mode 100644
index 0000000..ff84fc7
--- /dev/null
+++ b/drm/libdrmframework/include/DrmManagerClientImpl.h
@@ -0,0 +1,420 @@
+/*
+ * 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 __DRM_MANAGER_CLIENT_IMPL_H__
+#define __DRM_MANAGER_CLIENT_IMPL_H__
+
+#include <binder/IMemory.h>
+#include <utils/threads.h>
+#include <drm/DrmManagerClient.h>
+
+#include "IDrmManagerService.h"
+
+namespace android {
+
+class DrmInfoEvent;
+/**
+ * This is implementation class for DrmManagerClient class.
+ *
+ * Only the JNI layer creates an instance of this class to delegate
+ * functionality to Native later.
+ *
+ */
+class DrmManagerClientImpl : public BnDrmServiceListener {
+private:
+ DrmManagerClientImpl() { }
+
+public:
+ static DrmManagerClientImpl* create(int* pUniqueId);
+
+ static void remove(int uniqueId);
+
+ virtual ~DrmManagerClientImpl() { }
+
+public:
+ /**
+ * Adds the client respective to given unique id.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ */
+ void addClient(int uniqueId);
+
+ /**
+ * Removes the client respective to given unique id.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ */
+ void removeClient(int uniqueId);
+
+ /**
+ * Register a callback to be invoked when the caller required to
+ * receive necessary information
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] infoListener Listener
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t setOnInfoListener(
+ int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener);
+
+ /**
+ * Get constraint information associated with input content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @param[in] 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* getConstraints(int uniqueId, const String8* path, const int action);
+
+ /**
+ * Get metadata information associated with input content.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @return DrmMetadata
+ * key-value pairs of metadata are embedded in it
+ * @note
+ * In case of error, return NULL
+ */
+ DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
+ /**
+ * Check whether the given mimetype or path can be handled
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the content needs to be handled
+ * @param[in] mimetype Mimetype of the content needs to be handled
+ * @return
+ * True if DrmManager can handle given path or mime type.
+ */
+ bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
+
+ /**
+ * Executes given drm information based on its type
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] drmInfo Information needs to be processed
+ * @return DrmInfoStatus
+ * instance as a result of processing given input
+ */
+ DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+ /**
+ * Retrieves necessary information for registration, unregistration or rights
+ * acquisition information.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] drmInfoRequest Request information to retrieve drmInfo
+ * @return DrmInfo
+ * instance as a result of processing given input
+ */
+ DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest);
+
+ /**
+ * Save DRM rights to specified rights path
+ * and make association with content path
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] drmRights DrmRights to be saved
+ * @param[in] rightsPath File path where rights to be saved
+ * @param[in] contentPath File path where content was saved
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t saveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath);
+
+ /**
+ * Retrieves the mime type embedded inside the original content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path the path of the protected content
+ * @return String8
+ * Returns mime-type of the original content, such as "video/mpeg"
+ */
+ String8 getOriginalMimeType(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[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the content or null.
+ * @param[in] mimeType Mime type of the content or null.
+ * @return type of the DRM content,
+ * such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
+ */
+ int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+
+ /**
+ * Check whether the given content has valid rights or not
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @param[in] 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 checkRightsStatus(int uniqueId, const String8& path, int action);
+
+ /**
+ * Consumes the rights for a content.
+ * If the reserve parameter is true the rights is reserved until the same
+ * application calls this api again with the reserve parameter set to false.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] action Action to perform. (Action::DEFAULT, Action::PLAY, etc)
+ * @param[in] reserve True if the rights should be reserved.
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+
+ /**
+ * Informs the DRM engine about the playback actions performed on the DRM files.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE)
+ * @param[in] 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
+ */
+ status_t setPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+ /**
+ * Validates whether an action on the DRM content is allowed or not.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @param[in] action Action to validate (Action::PLAY, Action::TRANSFER, etc)
+ * @param[in] description Detailed description of the action
+ * @return true if the action is allowed.
+ */
+ bool validateAction(
+ int uniqueId, const String8& path, int action, const ActionDescription& description);
+
+ /**
+ * Removes the rights associated with the given protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t removeRights(int uniqueId, const String8& path);
+
+ /**
+ * Removes all the rights information of each plug-in associated with
+ * DRM framework. Will be used in master reset
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t removeAllRights(int uniqueId);
+
+ /**
+ * This API is for Forward Lock based DRM scheme.
+ * 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.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] mimeType Description/MIME type of the input data packet
+ * @return Return handle for the convert session
+ */
+ int openConvertSession(int uniqueId, const String8& mimeType);
+
+ /**
+ * 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 are new block
+ * of data received by the application.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] convertId Handle for the convert session
+ * @param[in] 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* convertData(int uniqueId, int convertId, const DrmBuffer* inputData);
+
+ /**
+ * Informs the Drm Agent when there is no more data which need to be converted
+ * or when an error occurs. Upon successful conversion of the complete data,
+ * the agent will inform that where the header and body signature
+ * should be added. This signature appending is needed to integrity
+ * protect the converted file.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] 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 on which offset these signature data
+ * should be appended.
+ */
+ DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId);
+
+ /**
+ * Retrieves all DrmSupportInfo instance that native DRM framework can handle.
+ * This interface is meant to be used by JNI layer
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[out] length Number of elements in drmSupportInfoArray
+ * @param[out] drmSupportInfoArray Array contains all DrmSupportInfo
+ * that native DRM framework can handle
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
+
+ /**
+ * Open the decrypt session to decrypt the given protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] fd File descriptor of the protected content to be decrypted
+ * @param[in] offset Start position of the content
+ * @param[in] length The length of the protected content
+ * @return
+ * Handle for the decryption session
+ */
+ DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+
+ /**
+ * Open the decrypt session to decrypt the given protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] uri Path of the protected content to be decrypted
+ * @return
+ * Handle for the decryption session
+ */
+ DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
+
+ /**
+ * Close the decrypt session for the given handle
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+
+ /**
+ * Initialize decryption for the given unit of the protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param[in] headerInfo Information for initializing decryption of this decrypUnit
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t initializeDecryptUnit(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[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param[in] encBuffer Encrypted data block
+ * @param[out] decBuffer Decrypted data block
+ * @param[in] 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 decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
+
+ /**
+ * Finalize decryption for the given unit of the protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+
+ /**
+ * Reads the specified number of bytes from an open DRM file.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[out] buffer Reference to the buffer that should receive the read data.
+ * @param[in] numBytes Number of bytes to read.
+ * @param[in] offset Offset with which to update the file position.
+ *
+ * @return Number of bytes read. Returns -1 for Failure.
+ */
+ ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off_t offset);
+
+ /**
+ * Notify the event to the registered listener
+ *
+ * @param[in] event The event to be notified
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t notify(const DrmInfoEvent& event);
+
+private:
+ /**
+ * Install new DRM Engine Plug-in at the runtime
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] drmEngine Shared Object(so) File in which DRM Engine defined
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t installDrmEngine(int uniqueId, const String8& drmEngineFile);
+
+private:
+ Mutex mLock;
+ sp<DrmManagerClient::OnInfoListener> mOnInfoListener;
+
+private:
+ static Mutex mMutex;
+ static sp<IDrmManagerService> mDrmManagerService;
+ static const sp<IDrmManagerService>& getDrmManagerService();
+ static const String8 EMPTY_STRING;
+};
+
+};
+
+#endif /* __DRM_MANAGER_CLIENT_IMPL_H__ */
+
diff --git a/drm/libdrmframework/include/DrmManagerService.h b/drm/libdrmframework/include/DrmManagerService.h
new file mode 100644
index 0000000..f346356
--- /dev/null
+++ b/drm/libdrmframework/include/DrmManagerService.h
@@ -0,0 +1,125 @@
+/*
+ * 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 __DRM_MANAGER_SERVICE_H__
+#define __DRM_MANAGER_SERVICE_H__
+
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include "IDrmManagerService.h"
+#include "IDrmServiceListener.h"
+
+namespace android {
+
+class DrmManager;
+class String8;
+class Mutex;
+
+/**
+ * This is the implementation class for DRM manager service. This delegates
+ * the responsibility to DrmManager.
+ *
+ * The instance of this class is created while starting the DRM manager service.
+ *
+ */
+class DrmManagerService : public BnDrmManagerService {
+public:
+ static void instantiate();
+
+private:
+ DrmManagerService();
+ virtual ~DrmManagerService();
+
+public:
+ int addUniqueId(int uniqueId);
+
+ void removeUniqueId(int uniqueId);
+
+ void addClient(int uniqueId);
+
+ void removeClient(int uniqueId);
+
+ status_t setDrmServiceListener(
+ int uniqueId, const sp<IDrmServiceListener>& drmServiceListener);
+
+ status_t installDrmEngine(int uniqueId, const String8& drmEngineFile);
+
+ DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
+
+ DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
+ bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
+
+ DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+ DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest);
+
+ status_t saveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath);
+
+ String8 getOriginalMimeType(int uniqueId, const String8& path);
+
+ int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+
+ int checkRightsStatus(int uniqueId, const String8& path,int action);
+
+ status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+
+ status_t setPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+ bool validateAction(int uniqueId, const String8& path,
+ int action, const ActionDescription& description);
+
+ status_t removeRights(int uniqueId, const String8& path);
+
+ status_t removeAllRights(int uniqueId);
+
+ int openConvertSession(int uniqueId, const String8& mimeType);
+
+ DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData);
+
+ DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId);
+
+ status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
+
+ DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+
+ DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
+
+ status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+
+ status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo);
+
+ status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
+
+ status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+
+ ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off_t offset);
+
+private:
+ DrmManager* mDrmManager;
+};
+
+};
+
+#endif /* __DRM_MANAGER_SERVICE_H__ */
+
diff --git a/drm/libdrmframework/include/IDrmIOService.h b/drm/libdrmframework/include/IDrmIOService.h
new file mode 100644
index 0000000..5e0d907
--- /dev/null
+++ b/drm/libdrmframework/include/IDrmIOService.h
@@ -0,0 +1,86 @@
+/*
+ * 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 __IDRM_IO_SERVICE_H__
+#define __IDRM_IO_SERVICE_H__
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+/**
+ * This is the interface class for DRM IO service.
+ *
+ */
+class IDrmIOService : public IInterface
+{
+public:
+ enum {
+ WRITE_TO_FILE = IBinder::FIRST_CALL_TRANSACTION,
+ READ_FROM_FILE
+ };
+
+public:
+ DECLARE_META_INTERFACE(DrmIOService);
+
+public:
+ /**
+ * Writes the data into the file path provided
+ *
+ * @param[in] filePath Path of the file
+ * @param[in] dataBuffer Data to write
+ */
+ virtual void writeToFile(const String8& filePath, const String8& dataBuffer) = 0;
+
+ /**
+ * Reads the data from the file path provided
+ *
+ * @param[in] filePath Path of the file
+ * @return Data read from the file
+ */
+ virtual String8 readFromFile(const String8& filePath) = 0;
+};
+
+/**
+ * This is the Binder implementation class for DRM IO service.
+ */
+class BpDrmIOService: public BpInterface<IDrmIOService>
+{
+public:
+ BpDrmIOService(const sp<IBinder>& impl)
+ : BpInterface<IDrmIOService>(impl) {}
+
+ virtual void writeToFile(const String8& filePath, const String8& dataBuffer);
+
+ virtual String8 readFromFile(const String8& filePath);
+};
+
+/**
+ * This is the Binder implementation class for DRM IO service.
+ */
+class BnDrmIOService: public BnInterface<IDrmIOService>
+{
+public:
+ virtual status_t onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
+};
+
+};
+
+#endif /* __IDRM_IO_SERVICE_H__ */
+
diff --git a/drm/libdrmframework/include/IDrmManagerService.h b/drm/libdrmframework/include/IDrmManagerService.h
new file mode 100644
index 0000000..f1dabd3
--- /dev/null
+++ b/drm/libdrmframework/include/IDrmManagerService.h
@@ -0,0 +1,258 @@
+/*
+ * 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 __IDRM_MANAGER_SERVICE_H__
+#define __IDRM_MANAGER_SERVICE_H__
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <drm/drm_framework_common.h>
+#include "IDrmServiceListener.h"
+
+namespace android {
+
+class DrmContentIds;
+class DrmConstraints;
+class DrmMetadata;
+class DrmRights;
+class DrmInfo;
+class DrmInfoStatus;
+class DrmInfoRequest;
+class DrmSupportInfo;
+class DrmConvertedStatus;
+class String8;
+class ActionDescription;
+
+/**
+ * This is the interface class for DRM Manager service.
+ *
+ */
+class IDrmManagerService : public IInterface
+{
+public:
+ enum {
+ ADD_UNIQUEID = IBinder::FIRST_CALL_TRANSACTION,
+ REMOVE_UNIQUEID,
+ ADD_CLIENT,
+ REMOVE_CLIENT,
+ SET_DRM_SERVICE_LISTENER,
+ INSTALL_DRM_ENGINE,
+ GET_CONSTRAINTS_FROM_CONTENT,
+ GET_METADATA_FROM_CONTENT,
+ CAN_HANDLE,
+ PROCESS_DRM_INFO,
+ ACQUIRE_DRM_INFO,
+ SAVE_RIGHTS,
+ GET_ORIGINAL_MIMETYPE,
+ GET_DRM_OBJECT_TYPE,
+ CHECK_RIGHTS_STATUS,
+ CONSUME_RIGHTS,
+ SET_PLAYBACK_STATUS,
+ VALIDATE_ACTION,
+ REMOVE_RIGHTS,
+ REMOVE_ALL_RIGHTS,
+ OPEN_CONVERT_SESSION,
+ CONVERT_DATA,
+ CLOSE_CONVERT_SESSION,
+ GET_ALL_SUPPORT_INFO,
+ OPEN_DECRYPT_SESSION,
+ OPEN_DECRYPT_SESSION_FROM_URI,
+ CLOSE_DECRYPT_SESSION,
+ INITIALIZE_DECRYPT_UNIT,
+ DECRYPT,
+ FINALIZE_DECRYPT_UNIT,
+ PREAD
+ };
+
+public:
+ DECLARE_META_INTERFACE(DrmManagerService);
+
+public:
+ virtual int addUniqueId(int uniqueId) = 0;
+
+ virtual void removeUniqueId(int uniqueId) = 0;
+
+ virtual void addClient(int uniqueId) = 0;
+
+ virtual void removeClient(int uniqueId) = 0;
+
+ virtual status_t setDrmServiceListener(
+ int uniqueId, const sp<IDrmServiceListener>& infoListener) = 0;
+
+ virtual status_t installDrmEngine(int uniqueId, const String8& drmEngineFile) = 0;
+
+ virtual DrmConstraints* getConstraints(
+ int uniqueId, const String8* path, const int action) = 0;
+
+ virtual DrmMetadata* getMetadata(int uniqueId, const String8* path) = 0;
+
+ virtual bool canHandle(int uniqueId, const String8& path, const String8& mimeType) = 0;
+
+ virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo) = 0;
+
+ virtual DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest) = 0;
+
+ virtual status_t saveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath) = 0;
+
+ virtual String8 getOriginalMimeType(int uniqueId, const String8& path) = 0;
+
+ virtual int getDrmObjectType(
+ int uniqueId, const String8& path, const String8& mimeType) = 0;
+
+ virtual int checkRightsStatus(int uniqueId, const String8& path, int action) = 0;
+
+ virtual status_t consumeRights(
+ int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) = 0;
+
+ virtual status_t setPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) = 0;
+
+ virtual bool validateAction(
+ int uniqueId, const String8& path,
+ int action, const ActionDescription& description) = 0;
+
+ virtual status_t removeRights(int uniqueId, const String8& path) = 0;
+
+ virtual status_t removeAllRights(int uniqueId) = 0;
+
+ virtual int openConvertSession(int uniqueId, const String8& mimeType) = 0;
+
+ virtual DrmConvertedStatus* convertData(
+ int uniqueId, int convertId, const DrmBuffer* inputData) = 0;
+
+ virtual DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId) = 0;
+
+ virtual status_t getAllSupportInfo(
+ int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) = 0;
+
+ virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length) = 0;
+
+ virtual DecryptHandle* openDecryptSession(int uniqueId, const char* uri) = 0;
+
+ virtual status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) = 0;
+
+ virtual status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo) = 0;
+
+ virtual status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) = 0;
+
+ virtual status_t finalizeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0;
+
+ virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes,off_t offset) = 0;
+};
+
+/**
+ * This is the Binder implementation class for DRM Manager service.
+ */
+class BpDrmManagerService: public BpInterface<IDrmManagerService>
+{
+public:
+ BpDrmManagerService(const sp<IBinder>& impl)
+ : BpInterface<IDrmManagerService>(impl) {}
+
+ virtual int addUniqueId(int uniqueId);
+
+ virtual void removeUniqueId(int uniqueId);
+
+ virtual void addClient(int uniqueId);
+
+ virtual void removeClient(int uniqueId);
+
+ virtual status_t setDrmServiceListener(
+ int uniqueId, const sp<IDrmServiceListener>& infoListener);
+
+ virtual status_t installDrmEngine(int uniqueId, const String8& drmEngineFile);
+
+ virtual DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
+
+ virtual DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
+ virtual bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
+
+ virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+ virtual DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest);
+
+ virtual status_t saveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath);
+
+ virtual String8 getOriginalMimeType(int uniqueId, const String8& path);
+
+ virtual int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+
+ virtual int checkRightsStatus(int uniqueId, const String8& path, int action);
+
+ virtual status_t consumeRights(
+ int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+
+ virtual status_t setPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+ virtual bool validateAction(
+ int uniqueId, const String8& path, int action, const ActionDescription& description);
+
+ virtual status_t removeRights(int uniqueId, const String8& path);
+
+ virtual status_t removeAllRights(int uniqueId);
+
+ virtual int openConvertSession(int uniqueId, const String8& mimeType);
+
+ virtual DrmConvertedStatus* convertData(
+ int uniqueId, int convertId, const DrmBuffer* inputData);
+
+ virtual DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId);
+
+ virtual status_t getAllSupportInfo(
+ int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
+
+ virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+
+ virtual DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
+
+ virtual status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+
+ virtual status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo);
+
+ virtual status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
+
+ virtual status_t finalizeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+
+ virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off_t offset);
+};
+
+/**
+ * This is the Binder implementation class for DRM Manager service.
+ */
+class BnDrmManagerService: public BnInterface<IDrmManagerService>
+{
+public:
+ virtual status_t onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
+};
+
+};
+
+#endif /* __IDRM_MANAGER_SERVICE_H__ */
+
diff --git a/drm/libdrmframework/include/IDrmServiceListener.h b/drm/libdrmframework/include/IDrmServiceListener.h
new file mode 100644
index 0000000..7f7109f
--- /dev/null
+++ b/drm/libdrmframework/include/IDrmServiceListener.h
@@ -0,0 +1,71 @@
+/*
+ * 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 __IDRM_SERVICE_LISTENER_H__
+#define __IDRM_SERVICE_LISTENER_H__
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class DrmInfoEvent;
+
+/**
+ * This is the interface class for DRM service listener.
+ *
+ */
+class IDrmServiceListener : public IInterface
+{
+public:
+ enum {
+ NOTIFY = IBinder::FIRST_CALL_TRANSACTION,
+ };
+
+public:
+ DECLARE_META_INTERFACE(DrmServiceListener);
+
+public:
+ virtual status_t notify(const DrmInfoEvent& event) = 0;
+};
+
+/**
+ * This is the Binder implementation class for DRM service listener.
+ */
+class BpDrmServiceListener: public BpInterface<IDrmServiceListener>
+{
+public:
+ BpDrmServiceListener(const sp<IBinder>& impl)
+ : BpInterface<IDrmServiceListener>(impl) {}
+
+ virtual status_t notify(const DrmInfoEvent& event);
+};
+
+/**
+ * This is the Binder implementation class for DRM service listener.
+ */
+class BnDrmServiceListener: public BnInterface<IDrmServiceListener>
+{
+public:
+ virtual status_t onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
+};
+
+};
+
+#endif /* __IDRM_SERVICE_LISTENER_H__ */
+
diff --git a/drm/libdrmframework/include/PlugInManager.h b/drm/libdrmframework/include/PlugInManager.h
new file mode 100644
index 0000000..9ad195f
--- /dev/null
+++ b/drm/libdrmframework/include/PlugInManager.h
@@ -0,0 +1,273 @@
+/*
+ * 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 __PLUGIN_MANAGER_H__
+#define __PLUGIN_MANAGER_H__
+
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+const char* const PLUGIN_MANAGER_CREATE = "create";
+const char* const PLUGIN_MANAGER_DESTROY = "destroy";
+const char* const PLUGIN_EXTENSION = ".so";
+
+/**
+ * This is the template class for Plugin manager.
+ *
+ * The DrmManager uses this class to handle the plugins.
+ *
+ */
+template<typename Type>
+class TPlugInManager {
+private:
+ typedef void* HANDLE;
+ typedef Type* create_t(void);
+ typedef void destroy_t(Type*);
+ typedef create_t* FPCREATE;
+ typedef destroy_t* FPDESTORY;
+
+ typedef struct _PlugInContainer {
+ String8 sPath;
+ HANDLE hHandle;
+ FPCREATE fpCreate;
+ FPDESTORY fpDestory;
+ Type* pInstance;
+
+ _PlugInContainer():
+ sPath("")
+ ,hHandle(NULL)
+ ,fpCreate(NULL)
+ ,fpDestory(NULL)
+ ,pInstance(NULL)
+ {}
+ } PlugInContainer;
+
+ typedef KeyedVector<String8, PlugInContainer*> PlugInMap;
+ PlugInMap m_plugInMap;
+
+ typedef Vector<String8> PlugInIdList;
+ PlugInIdList m_plugInIdList;
+
+public:
+ /**
+ * Load all the plug-ins in the specified directory
+ *
+ * @param[in] rsPlugInDirPath
+ * Directory path which plug-ins (dynamic library) are stored
+ * @note Plug-ins should be implemented according to the specification
+ */
+ void loadPlugIns(const String8& rsPlugInDirPath) {
+ Vector<String8> plugInFileList = getPlugInPathList(rsPlugInDirPath);
+
+ if (!plugInFileList.isEmpty()) {
+ for (unsigned int i = 0; i < plugInFileList.size(); ++i) {
+ loadPlugIn(plugInFileList[i]);
+ }
+ }
+ }
+
+ /**
+ * Unload all the plug-ins
+ *
+ */
+ void unloadPlugIns() {
+ for (unsigned int i = 0; i < m_plugInIdList.size(); ++i) {
+ unloadPlugIn(m_plugInIdList[i]);
+ }
+ m_plugInIdList.clear();
+ }
+
+ /**
+ * Get all the IDs of available plug-ins
+ *
+ * @return[in] plugInIdList
+ * String type Vector in which all plug-in IDs are stored
+ */
+ Vector<String8> getPlugInIdList() const {
+ return m_plugInIdList;
+ }
+
+ /**
+ * Get a plug-in reference of specified ID
+ *
+ * @param[in] rsPlugInId
+ * Plug-in ID to be used
+ * @return plugIn
+ * Reference of specified plug-in instance
+ */
+ Type& getPlugIn(const String8& rsPlugInId) {
+ if (!contains(rsPlugInId)) {
+ // This error case never happens
+ }
+ return *(m_plugInMap.valueFor(rsPlugInId)->pInstance);
+ }
+
+public:
+ /**
+ * Load a plug-in stored in the specified path
+ *
+ * @param[in] rsPlugInPath
+ * Plug-in (dynamic library) file path
+ * @note Plug-in should be implemented according to the specification
+ */
+ void loadPlugIn(const String8& rsPlugInPath) {
+ if (contains(rsPlugInPath)) {
+ return;
+ }
+
+ PlugInContainer* pPlugInContainer = new PlugInContainer();
+
+ pPlugInContainer->hHandle = dlopen(rsPlugInPath.string(), RTLD_LAZY);
+
+ if (NULL == pPlugInContainer->hHandle) {
+ delete pPlugInContainer;
+ pPlugInContainer = NULL;
+ return;
+ }
+
+ pPlugInContainer->sPath = rsPlugInPath;
+ pPlugInContainer->fpCreate
+ = (FPCREATE)dlsym(pPlugInContainer->hHandle, PLUGIN_MANAGER_CREATE);
+ pPlugInContainer->fpDestory
+ = (FPDESTORY)dlsym(pPlugInContainer->hHandle, PLUGIN_MANAGER_DESTROY);
+
+ if (NULL != pPlugInContainer->fpCreate && NULL != pPlugInContainer->fpDestory) {
+ pPlugInContainer->pInstance = (Type*)pPlugInContainer->fpCreate();
+ m_plugInIdList.add(rsPlugInPath);
+ m_plugInMap.add(rsPlugInPath, pPlugInContainer);
+ } else {
+ dlclose(pPlugInContainer->hHandle);
+ delete pPlugInContainer;
+ pPlugInContainer = NULL;
+ return;
+ }
+ }
+
+ /**
+ * Unload a plug-in stored in the specified path
+ *
+ * @param[in] rsPlugInPath
+ * Plug-in (dynamic library) file path
+ */
+ void unloadPlugIn(const String8& rsPlugInPath) {
+ if (!contains(rsPlugInPath)) {
+ return;
+ }
+
+ PlugInContainer* pPlugInContainer = m_plugInMap.valueFor(rsPlugInPath);
+ pPlugInContainer->fpDestory(pPlugInContainer->pInstance);
+ dlclose(pPlugInContainer->hHandle);
+
+ m_plugInMap.removeItem(rsPlugInPath);
+ delete pPlugInContainer;
+ pPlugInContainer = NULL;
+ }
+
+private:
+ /**
+ * True if TPlugInManager contains rsPlugInId
+ */
+ bool contains(const String8& rsPlugInId) {
+ return m_plugInMap.indexOfKey(rsPlugInId) != NAME_NOT_FOUND;
+ }
+
+ /**
+ * Return file path list of plug-ins stored in the specified directory
+ *
+ * @param[in] rsDirPath
+ * Directory path in which plug-ins are stored
+ * @return plugInFileList
+ * String type Vector in which file path of plug-ins are stored
+ */
+ Vector<String8> getPlugInPathList(const String8& rsDirPath) {
+ Vector<String8> fileList;
+ DIR* pDir = opendir(rsDirPath.string());
+ struct dirent* pEntry = new dirent();
+
+ while (NULL != pDir && NULL != (pEntry = readdir(pDir))) {
+ if (!isPlugIn(pEntry)) {
+ continue;
+ }
+ String8 plugInPath;
+ plugInPath += rsDirPath;
+ plugInPath += "/";
+ plugInPath += pEntry->d_name;
+
+ fileList.add(plugInPath);
+ }
+
+ if (NULL != pDir) {
+ closedir(pDir);
+ }
+ delete pEntry;
+ pEntry = NULL;
+
+ return fileList;
+ }
+
+ /**
+ * True if the input name denotes plug-in
+ */
+ bool isPlugIn(const struct dirent* pEntry) const {
+ String8 sName(pEntry->d_name);
+ int extentionPos = sName.size() - String8(PLUGIN_EXTENSION).size();
+ if (extentionPos < 0) {
+ return false;
+ }
+ return extentionPos == (int)sName.find(PLUGIN_EXTENSION);
+ }
+
+ /**
+ * True if the input entry is "." or ".."
+ */
+ bool isDotOrDDot(const struct dirent* pEntry) const {
+ String8 sName(pEntry->d_name);
+ return "." == sName || ".." == sName;
+ }
+
+ /**
+ * True if input entry is directory
+ */
+ bool isDirectory(const struct dirent* pEntry) const {
+ return DT_DIR == pEntry->d_type;
+ }
+
+ /**
+ * True if input entry is regular file
+ */
+ bool isRegularFile(const struct dirent* pEntry) const {
+ return DT_REG == pEntry->d_type;
+ }
+
+ /**
+ * True if input entry is link
+ */
+ bool isLink(const struct dirent* pEntry) const {
+ return DT_LNK == pEntry->d_type;
+ }
+};
+
+};
+
+#endif /* __PLUGIN_MANAGER_H__ */
+
diff --git a/drm/libdrmframework/include/ReadWriteUtils.h b/drm/libdrmframework/include/ReadWriteUtils.h
new file mode 100644
index 0000000..529b342
--- /dev/null
+++ b/drm/libdrmframework/include/ReadWriteUtils.h
@@ -0,0 +1,79 @@
+/*
+ * 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 __READ_WRITE_UTILS_H__
+#define __READ_WRITE_UTILS_H__
+
+#include <utils/FileMap.h>
+#include <drm/drm_framework_common.h>
+
+namespace android {
+
+/**
+ * This is an utility class which performs IO operations.
+ *
+ */
+class ReadWriteUtils {
+public:
+ /**
+ * Constructor for ReadWriteUtils
+ */
+ ReadWriteUtils() {}
+
+ /**
+ * Destructor for ReadWriteUtils
+ */
+ virtual ~ReadWriteUtils();
+
+public:
+ /**
+ * Reads the data from the file path provided
+ *
+ * @param[in] filePath Path of the file
+ * @return Data read from the file
+ */
+ static String8 readBytes(const String8& filePath);
+ /**
+ * Reads the data into the given buffer from the file path provided
+ *
+ * @param[in] filePath Path of the file
+ * @param[out] buffer Data read from the file
+ * @return Length of the data read from the file
+ */
+ static int readBytes(const String8& filePath, char** buffer);
+ /**
+ * Writes the data into the file path provided
+ *
+ * @param[in] filePath Path of the file
+ * @param[in] dataBuffer Data to write
+ */
+ static void writeToFile(const String8& filePath, const String8& data);
+ /**
+ * Appends the data into the file path provided
+ *
+ * @param[in] filePath Path of the file
+ * @param[in] dataBuffer Data to append
+ */
+ static void appendToFile(const String8& filePath, const String8& data);
+
+private:
+ FileMap* mFileMap;
+};
+
+};
+
+#endif /* __READ_WRITE_UTILS_H__ */
+
diff --git a/drm/libdrmframework/include/StringTokenizer.h b/drm/libdrmframework/include/StringTokenizer.h
new file mode 100644
index 0000000..70e7558
--- /dev/null
+++ b/drm/libdrmframework/include/StringTokenizer.h
@@ -0,0 +1,87 @@
+/*
+ * 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 __STRING_TOKENIZER_H__
+#define __STRING_TOKENIZER_H__
+
+#include <drm/drm_framework_common.h>
+
+namespace android {
+
+/**
+ * This is an utility class for String manipulation.
+ *
+ */
+class StringTokenizer {
+public:
+ /**
+ * Iterator for string tokens
+ */
+ class Iterator {
+ friend class StringTokenizer;
+ private:
+ Iterator(StringTokenizer* StringTokenizer)
+ : mStringTokenizer(StringTokenizer), mIndex(0) {}
+
+ public:
+ Iterator(const Iterator& iterator);
+ Iterator& operator=(const Iterator& iterator);
+ virtual ~Iterator() {}
+
+ public:
+ bool hasNext();
+ String8& next();
+
+ private:
+ StringTokenizer* mStringTokenizer;
+ unsigned int mIndex;
+ };
+
+public:
+ /**
+ * Constructor for StringTokenizer
+ *
+ * @param[in] string Complete string data
+ * @param[in] delimeter Delimeter used to split the string
+ */
+ StringTokenizer(const String8& string, const String8& delimeter);
+
+ /**
+ * Destructor for StringTokenizer
+ */
+ ~StringTokenizer() {}
+
+private:
+ /**
+ * Splits the string according to the delimeter
+ */
+ void splitString(const String8& string, const String8& delimeter);
+
+public:
+ /**
+ * Returns Iterator object to walk through the split string values
+ *
+ * @return Iterator object
+ */
+ Iterator iterator();
+
+private:
+ Vector<String8> mStringTokenizerVector;
+};
+
+};
+#endif /* __STRING_TOKENIZER_H__ */
+
diff --git a/drm/libdrmframework/plugins/Android.mk b/drm/libdrmframework/plugins/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/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/Android.mk b/drm/libdrmframework/plugins/common/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/Android.mk
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+include $(call all-subdir-makefiles)
diff --git a/drm/libdrmframework/plugins/common/include/DrmEngineBase.h b/drm/libdrmframework/plugins/common/include/DrmEngineBase.h
new file mode 100644
index 0000000..67b6355
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/include/DrmEngineBase.h
@@ -0,0 +1,459 @@
+/*
+ * 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 __DRM_ENGINE_BASE_H__
+#define __DRM_ENGINE_BASE_H__
+
+#include <drm/drm_framework_common.h>
+#include "IDrmEngine.h"
+
+namespace android {
+
+/**
+ * This class is an interface for plug-in developers
+ *
+ * Responsibility of this class is control the sequence of actual plug-in.
+ * All each plug-in developer has to do is implement onXXX() type virtual interfaces.
+ */
+class DrmEngineBase : public IDrmEngine {
+public:
+ DrmEngineBase();
+ virtual ~DrmEngineBase();
+
+public:
+ DrmConstraints* getConstraints(int uniqueId, const String8* path, int action);
+
+ DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
+ status_t initialize(int uniqueId);
+
+ status_t setOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener);
+
+ status_t terminate(int uniqueId);
+
+ bool canHandle(int uniqueId, const String8& path);
+
+ DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+ status_t saveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath);
+
+ DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest);
+
+ String8 getOriginalMimeType(int uniqueId, const String8& path);
+
+ int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+
+ int checkRightsStatus(int uniqueId, const String8& path, int action);
+
+ status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+
+ status_t setPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+ bool validateAction(
+ int uniqueId, const String8& path, int action, const ActionDescription& description);
+
+ status_t removeRights(int uniqueId, const String8& path);
+
+ status_t removeAllRights(int uniqueId);
+
+ status_t openConvertSession(int uniqueId, int convertId);
+
+ DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData);
+
+ DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId);
+
+ DrmSupportInfo* getSupportInfo(int uniqueId);
+
+ status_t openDecryptSession(
+ int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length);
+
+ status_t openDecryptSession(
+ int uniqueId, DecryptHandle* decryptHandle, const char* uri);
+
+ status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+
+ status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo);
+
+ status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
+
+ status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+
+ ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off_t offset);
+
+protected:
+ /////////////////////////////////////////////////////
+ // Interface for plug-in developers //
+ // each plug-in has to implement following method //
+ /////////////////////////////////////////////////////
+ /**
+ * Get constraint information associated with input content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @param[in] 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
+ */
+ virtual DrmConstraints* onGetConstraints(
+ int uniqueId, const String8* path, int action) = 0;
+
+ /**
+ * Get metadata information associated with input content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @return DrmMetadata
+ * key-value pairs of metadata
+ * @note
+ * In case of error, return NULL
+ */
+ virtual DrmMetadata* onGetMetadata(int uniqueId, const String8* path) = 0;
+
+ /**
+ * Initialize plug-in
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t onInitialize(int uniqueId) = 0;
+
+ /**
+ * Register a callback to be invoked when the caller required to
+ * receive necessary information
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] infoListener Listener
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t onSetOnInfoListener(
+ int uniqueId, const IDrmEngine::OnInfoListener* infoListener) = 0;
+
+ /**
+ * Terminate the plug-in
+ * and release resource bound to plug-in
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t onTerminate(int uniqueId) = 0;
+
+ /**
+ * Get whether the given content can be handled by this plugin or not
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path the protected object
+ * @return bool
+ * Returns true if this plugin can handle , false in case of not able to handle
+ */
+ virtual bool onCanHandle(int uniqueId, const String8& path) = 0;
+
+ /**
+ * Executes given drm information based on its type
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] drmInfo Information needs to be processed
+ * @return DrmInfoStatus
+ * instance as a result of processing given input
+ */
+ virtual DrmInfoStatus* onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo) = 0;
+
+ /**
+ * Save DRM rights to specified rights path
+ * and make association with content path
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] drmRights DrmRights to be saved
+ * @param[in] rightsPath File path where rights to be saved
+ * @param[in] contentPath File path where content was saved
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t onSaveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightspath, const String8& contentPath) = 0;
+
+ /**
+ * Retrieves necessary information for registration, unregistration or rights
+ * acquisition information.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] drmInfoRequest Request information to retrieve drmInfo
+ * @return DrmInfo
+ * instance as a result of processing given input
+ */
+ virtual DrmInfo* onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest) = 0;
+
+ /**
+ * Retrieves the mime type embedded inside the original content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @return String8
+ * Returns mime-type of the original content, such as "video/mpeg"
+ */
+ virtual String8 onGetOriginalMimeType(int uniqueId, const String8& path) = 0;
+
+ /**
+ * 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[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the content or null.
+ * @param[in] mimeType Mime type of the content or null.
+ * @return type of the DRM content,
+ * such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
+ */
+ virtual int onGetDrmObjectType(
+ int uniqueId, const String8& path, const String8& mimeType) = 0;
+
+ /**
+ * Check whether the given content has valid rights or not
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @param[in] 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.
+ */
+ virtual int onCheckRightsStatus(int uniqueId, const String8& path, int action) = 0;
+
+ /**
+ * Consumes the rights for a content.
+ * If the reserve parameter is true the rights is reserved until the same
+ * application calls this api again with the reserve parameter set to false.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] action Action to perform. (Action::DEFAULT, Action::PLAY, etc)
+ * @param[in] reserve True if the rights should be reserved.
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t onConsumeRights(int uniqueId, DecryptHandle* decryptHandle,
+ int action, bool reserve) = 0;
+
+ /**
+ * Informs the DRM Engine about the playback actions performed on the DRM files.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE)
+ * @param[in] 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
+ */
+ virtual status_t onSetPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) = 0;
+
+ /**
+ * Validates whether an action on the DRM content is allowed or not.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @param[in] action Action to validate (Action::PLAY, Action::TRANSFER, etc)
+ * @param[in] description Detailed description of the action
+ * @return true if the action is allowed.
+ */
+ virtual bool onValidateAction(int uniqueId, const String8& path,
+ int action, const ActionDescription& description) = 0;
+
+ /**
+ * Removes the rights associated with the given protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t onRemoveRights(int uniqueId, const String8& path) = 0;
+
+ /**
+ * Removes all the rights information of each plug-in associated with
+ * DRM framework. Will be used in master reset
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t onRemoveAllRights(int uniqueId) = 0;
+
+ /**
+ * This API is for Forward Lock based DRM scheme.
+ * 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.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] convertId Handle for the convert session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t onOpenConvertSession(int uniqueId, int convertId) = 0;
+
+ /**
+ * 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 are new block
+ * of data received by the application.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] convertId Handle for the convert session
+ * @param[in] 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.
+ */
+ virtual DrmConvertedStatus* onConvertData(
+ int uniqueId, int convertId, const DrmBuffer* inputData) = 0;
+
+ /**
+ * Informs the Drm Agent when there is no more data which need to be converted
+ * or when an error occurs. Upon successful conversion of the complete data,
+ * the agent will inform that where the header and body signature
+ * should be added. This signature appending is needed to integrity
+ * protect the converted file.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] 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 on which offset these signature data
+ * should be appended.
+ */
+ virtual DrmConvertedStatus* onCloseConvertSession(int uniqueId, int convertId) = 0;
+
+ /**
+ * Returns the information about the Drm Engine capabilities which includes
+ * supported MimeTypes and file suffixes.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @return DrmSupportInfo
+ * instance which holds the capabilities of a plug-in
+ */
+ virtual DrmSupportInfo* onGetSupportInfo(int uniqueId) = 0;
+
+ /**
+ * Open the decrypt session to decrypt the given protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the current decryption session
+ * @param[in] fd File descriptor of the protected content to be decrypted
+ * @param[in] offset Start position of the content
+ * @param[in] length The length of the protected content
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+ virtual status_t onOpenDecryptSession(
+ int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) = 0;
+
+ /**
+ * Open the decrypt session to decrypt the given protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the current decryption session
+ * @param[in] uri Path of the protected content to be decrypted
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+ virtual status_t onOpenDecryptSession(
+ int uniqueId, DecryptHandle* decryptHandle, const char* uri) = 0;
+
+ /**
+ * Close the decrypt session for the given handle
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle) = 0;
+
+ /**
+ * Initialize decryption for the given unit of the protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptId Handle for the decryption session
+ * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID
+ * @param[in] headerInfo Information for initializing decryption of this decrypUnit
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo) = 0;
+
+ /**
+ * 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[in] uniqueId Unique identifier for a session
+ * @param[in] decryptId Handle for the decryption session
+ * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID
+ * @param[in] encBuffer Encrypted data block
+ * @param[out] decBuffer Decrypted data block
+ * @param[in] 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.
+ */
+ virtual status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) = 0;
+
+ /**
+ * Finalize decryption for the given unit of the protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t onFinalizeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0;
+
+ /**
+ * Reads the specified number of bytes from an open DRM file.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[out] buffer Reference to the buffer that should receive the read data.
+ * @param[in] numBytes Number of bytes to read.
+ * @param[in] offset Offset with which to update the file position.
+ *
+ * @return Number of bytes read. Returns -1 for Failure.
+ */
+ virtual ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off_t offset) = 0;
+};
+
+};
+
+#endif /* __DRM_ENGINE_BASE_H__ */
+
diff --git a/drm/libdrmframework/plugins/common/include/IDrmEngine.h b/drm/libdrmframework/plugins/common/include/IDrmEngine.h
new file mode 100644
index 0000000..f839070
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/include/IDrmEngine.h
@@ -0,0 +1,415 @@
+/*
+ * 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 __IDRM_ENGINE_H__
+#define __IDRM_ENGINE_H__
+
+#include <drm/drm_framework_common.h>
+
+namespace android {
+
+class DrmContentIds;
+class DrmConstraints;
+class DrmMetadata;
+class DrmRights;
+class DrmInfo;
+class DrmInfoStatus;
+class DrmConvertedStatus;
+class DrmInfoRequest;
+class DrmSupportInfo;
+class DrmInfoEvent;
+
+/**
+ * This class is an interface for plug-in user
+ *
+ * Responsibility of this class is provide generic interface to DRM Engine Manager.
+ * Each interface need to be as abstract as possible.
+ */
+class IDrmEngine {
+public:
+ virtual ~IDrmEngine() {
+ }
+
+public:
+ class OnInfoListener {
+
+ public:
+ virtual void onInfo(const DrmInfoEvent& event) = 0;
+
+ virtual ~OnInfoListener() { }
+ };
+
+public:
+
+ //////////////////////////////////
+ // Implementation of IDrmEngine //
+ //////////////////////////////////
+
+ /**
+ * Initialize plug-in
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t initialize(int uniqueId) = 0;
+
+ /**
+ * Register a callback to be invoked when the caller required to
+ * receive necessary information
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] infoListener Listener
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t setOnInfoListener(
+ int uniqueId, const IDrmEngine::OnInfoListener* infoListener) = 0;
+
+ /**
+ * Terminate the plug-in
+ * and release resource bound to plug-in
+ * e.g.) release native resource
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t terminate(int uniqueId) = 0;
+
+ /**
+ * Get constraint information associated with input content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @param[in] 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
+ */
+ virtual DrmConstraints* getConstraints(
+ int uniqueId, const String8* path, int action) = 0;
+
+ /**
+ * Get metadata information associated with input content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @return DrmMetadata
+ * key-value pairs of metadata
+ * @note
+ * In case of error, return NULL
+ */
+ virtual DrmMetadata* getMetadata(int uniqueId, const String8* path) = 0;
+
+ /**
+ * Get whether the given content can be handled by this plugin or not
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path the protected object
+ * @return bool
+ * true if this plugin can handle , false in case of not able to handle
+ */
+ virtual bool canHandle(int uniqueId, const String8& path) = 0;
+
+ /**
+ * Executes given drm information based on its type
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] drmInfo Information needs to be processed
+ * @return DrmInfoStatus
+ * instance as a result of processing given input
+ */
+ virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo) = 0;
+
+ /**
+ * Retrieves necessary information for registration, unregistration or rights
+ * acquisition information.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] drmInfoRequest Request information to retrieve drmInfo
+ * @return DrmInfo
+ * instance as a result of processing given input
+ */
+ virtual DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) = 0;
+
+ /**
+ * Save DRM rights to specified rights path
+ * and make association with content path
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] drmRights DrmRights to be saved
+ * @param[in] rightsPath File path where rights to be saved
+ * @param[in] contentPath File path where content was saved
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t saveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath) = 0;
+
+ /**
+ * Retrieves the mime type embedded inside the original content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @return String8
+ * Returns mime-type of the original content, such as "video/mpeg"
+ */
+ virtual String8 getOriginalMimeType(int uniqueId, const String8& path) = 0;
+
+ /**
+ * 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[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the content or null.
+ * @param[in] mimeType Mime type of the content or null.
+ * @return type of the DRM content,
+ * such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
+ */
+ virtual int getDrmObjectType(
+ int uniqueId, const String8& path, const String8& mimeType) = 0;
+
+ /**
+ * Check whether the given content has valid rights or not
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @param[in] 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.
+ */
+ virtual int checkRightsStatus(int uniqueId, const String8& path, int action) = 0;
+
+ /**
+ * Consumes the rights for a content.
+ * If the reserve parameter is true the rights is reserved until the same
+ * application calls this api again with the reserve parameter set to false.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] action Action to perform. (Action::DEFAULT, Action::PLAY, etc)
+ * @param[in] reserve True if the rights should be reserved.
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t consumeRights(
+ int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) = 0;
+
+ /**
+ * Informs the DRM Engine about the playback actions performed on the DRM files.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE)
+ * @param[in] 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
+ */
+ virtual status_t setPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+ int playbackStatus, int position) = 0;
+
+ /**
+ * Validates whether an action on the DRM content is allowed or not.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @param[in] action Action to validate (Action::PLAY, Action::TRANSFER, etc)
+ * @param[in] description Detailed description of the action
+ * @return true if the action is allowed.
+ */
+ virtual bool validateAction(int uniqueId, const String8& path,
+ int action, const ActionDescription& description) = 0;
+
+ /**
+ * Removes the rights associated with the given protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] path Path of the protected content
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t removeRights(int uniqueId, const String8& path) = 0;
+
+ /**
+ * Removes all the rights information of each plug-in associated with
+ * DRM framework. Will be used in master reset
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t removeAllRights(int uniqueId) = 0;
+
+ /**
+ * This API is for Forward Lock based DRM scheme.
+ * 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.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] convertId Handle for the convert session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t openConvertSession(int uniqueId, int convertId) = 0;
+
+ /**
+ * 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 are new block
+ * of data received by the application.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] convertId Handle for the convert session
+ * @param[in] 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.
+ */
+ virtual DrmConvertedStatus* convertData(
+ int uniqueId, int convertId, const DrmBuffer* inputData) = 0;
+
+ /**
+ * Informs the Drm Agent when there is no more data which need to be converted
+ * or when an error occurs. Upon successful conversion of the complete data,
+ * the agent will inform that where the header and body signature
+ * should be added. This signature appending is needed to integrity
+ * protect the converted file.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] 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 on which offset these signature data
+ * should be appended.
+ */
+ virtual DrmConvertedStatus* closeConvertSession( int uniqueId, int convertId) = 0;
+
+ /**
+ * Returns the information about the Drm Engine capabilities which includes
+ * supported MimeTypes and file suffixes.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @return DrmSupportInfo
+ * instance which holds the capabilities of a plug-in
+ */
+ virtual DrmSupportInfo* getSupportInfo(int uniqueId) = 0;
+
+ /**
+ * Open the decrypt session to decrypt the given protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the current decryption session
+ * @param[in] fd File descriptor of the protected content to be decrypted
+ * @param[in] offset Start position of the content
+ * @param[in] length The length of the protected content
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+ virtual status_t openDecryptSession(
+ int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) = 0;
+
+ /**
+ * Open the decrypt session to decrypt the given protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the current decryption session
+ * @param[in] uri Path of the protected content to be decrypted
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+ virtual status_t openDecryptSession(
+ int uniqueId, DecryptHandle* decryptHandle, const char* uri) = 0;
+
+ /**
+ * Close the decrypt session for the given handle
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) = 0;
+
+ /**
+ * Initialize decryption for the given unit of the protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param[in] headerInfo Information for initializing decryption of this decrypUnit
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo) = 0;
+
+ /**
+ * 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[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param[in] encBuffer Encrypted data block
+ * @param[out] decBuffer Decrypted data block
+ * @param[in] 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.
+ */
+ virtual status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) = 0;
+
+ /**
+ * Finalize decryption for the given unit of the protected content
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ virtual status_t finalizeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0;
+
+ /**
+ * Reads the specified number of bytes from an open DRM file.
+ *
+ * @param[in] uniqueId Unique identifier for a session
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[out] buffer Reference to the buffer that should receive the read data.
+ * @param[in] numBytes Number of bytes to read.
+ * @param[in] offset Offset with which to update the file position.
+ *
+ * @return Number of bytes read. Returns -1 for Failure.
+ */
+ virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off_t offset) = 0;
+};
+
+};
+
+#endif /* __IDRM_ENGINE_H__ */
+
diff --git a/drm/libdrmframework/plugins/common/util/Android.mk b/drm/libdrmframework/plugins/common/util/Android.mk
new file mode 100644
index 0000000..15dda80
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/Android.mk
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ src/MimeTypeUtil.cpp
+
+LOCAL_MODULE := libdrmutility
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ libdl \
+ libdvm \
+ libandroid_runtime \
+ libnativehelper \
+ liblog
+
+
+base := frameworks/base
+
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE) \
+ $(base)/include \
+ $(base)/include/drm \
+ $(base)/include/drm/plugins \
+ $(LOCAL_PATH)/include
+
+
+ifneq ($(TARGET_BUILD_VARIANT),user)
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/tools
+
+endif
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h b/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h
new file mode 100644
index 0000000..4d12a61
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MIMETYPEUTIL_H__
+#define __MIMETYPEUTIL_H__
+
+#include <utils/String8.h>
+
+namespace android {
+
+class MimeTypeUtil {
+
+public:
+
+ MimeTypeUtil() {}
+
+ virtual ~MimeTypeUtil() {}
+
+/**
+ * May convert the mimetype if there is a well known
+ * replacement mimetype otherwise the original mimetype
+ * is returned.
+ *
+ * @param mimeType - mimetype in lower case to convert.
+ *
+ * @return mimetype or null.
+ */
+static String8 convertMimeType(String8& mimeType);
+
+};
+};
+
+#endif /* __MIMETYPEUTIL_H__ */
diff --git a/drm/libdrmframework/plugins/common/util/include/SessionMap.h b/drm/libdrmframework/plugins/common/util/include/SessionMap.h
new file mode 100644
index 0000000..3dff58c
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/include/SessionMap.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SESSIONMAP_H__
+#define __SESSIONMAP_H__
+
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+/**
+ * A wrapper template class for handling DRM Engine sessions.
+ */
+template <typename NODE>
+class SessionMap {
+
+public:
+ KeyedVector<int, NODE> map;
+
+ SessionMap() {}
+
+ virtual ~SessionMap() {
+ destroyMap();
+ }
+
+/**
+ * Adds a new value in the session map table. It expects memory to be allocated already
+ * for the session object
+ *
+ * @param key - key or Session ID
+ * @param value - session object to add
+ *
+ * @return boolean result of adding value. returns false if key is already exist.
+ */
+bool addValue(int key, NODE value) {
+ bool result = false;
+
+ if (!isCreated(key)) {
+ map.add(key, value);
+ result = true;
+ }
+
+ return result;
+}
+
+
+/**
+ * returns the session object by the key
+ *
+ * @param key - key or Session ID
+ *
+ * @return session object as per the key
+ */
+NODE getValue(int key) {
+ NODE value = NULL;
+
+ if (isCreated(key)) {
+ value = (NODE) map.valueFor(key);
+ }
+
+ return value;
+}
+
+/**
+ * returns the number of objects in the session map table
+ *
+ * @return count of number of session objects.
+ */
+int getSize() {
+ return map.size();
+}
+
+/**
+ * returns the session object by the index in the session map table
+ *
+ * @param index - index of the value required
+ *
+ * @return session object as per the index
+ */
+NODE getValueAt(unsigned int index) {
+ NODE value = NULL;
+
+ if (map.size() > index) {
+ value = map.valueAt(index);
+ }
+
+ return value;
+}
+
+/**
+ * deletes the object from session map. It also frees up memory for the session object.
+ *
+ * @param key - key of the value to be deleted
+ *
+ */
+void removeValue(int key) {
+ deleteValue(getValue(key));
+ map.removeItem(key);
+}
+
+/**
+ * decides if session is already created.
+ *
+ * @param key - key of the value for the session
+ *
+ * @return boolean result of whether session is created
+ */
+bool isCreated(int key) {
+ return (0 <= map.indexOfKey(key));
+}
+
+/**
+ * empty the entire session table. It releases all the memory for session objects.
+ */
+void destroyMap() {
+ int size = map.size();
+ int i = 0;
+
+ for (i = 0; i < size; i++) {
+ deleteValue(map.valueAt(i));
+ }
+
+ map.clear();
+}
+
+/**
+ * free up the memory for the session object.
+ * Make sure if any reference to the session object anywhere, otherwise it will be a
+ * dangle pointer after this call.
+ *
+ * @param value - session object to free
+ *
+ */
+void deleteValue(NODE value) {
+ delete value;
+}
+
+};
+
+};
+
+#endif /* __SESSIONMAP_H__ */
diff --git a/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp b/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp
new file mode 100644
index 0000000..4ee903e
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <MimeTypeUtil.h>
+#include <utils/Log.h>
+
+namespace android {
+
+#undef LOG_TAG
+#define LOG_TAG "MimeTypeUtil"
+
+enum {
+ MIMETYPE_AUDIO = 0,
+ MIMETYPE_APPLICATION = 1,
+ MIMETYPE_IMAGE = 2,
+ MIMETYPE_VIDEO = 3,
+ MIMETYPE_LAST = -1,
+};
+
+struct MimeGroup{
+ int type; // Audio, video,.. use the enum values
+ const char* pGroup; // "audio/", "video/",.. should contain the last "/"
+ int size; // Number of bytes. e.g. "audio/" = 6 bytes
+};
+
+struct MimeTypeList{
+ int type;
+ const char* pMimeExt; // Everything after the '/' e.g. audio/x-mpeg -> "x-mpeg"
+ int size; // Number of bytes. e.g. "x-mpeg" = 6 bytes
+ const char* pMimeType; // Mimetype that should be returned
+};
+
+
+// Known mimetypes by android
+static const char mime_type_audio_mpeg[] = "audio/mpeg";
+static const char mime_type_audio_3gpp[] = "audio/3gpp";
+static const char mime_type_audio_amr[] = "audio/amr-wb";
+static const char mime_type_audio_aac[] = "audio/mp4a-latm";
+static const char mime_type_audio_wav[] = "audio/wav";
+
+static const char mime_type_video_mpeg4[] = "video/mpeg4";
+static const char mime_type_video_3gpp[] = "video/3gpp";
+
+// Known mimetype groups
+static const char mime_group_audio[] = "audio/";
+static const char mime_group_application[] = "application/";
+static const char mime_group_image[] = "image/";
+static const char mime_group_video[] = "video/";
+
+static struct MimeGroup mimeGroup[] = {
+ {MIMETYPE_AUDIO, mime_group_audio, sizeof(mime_group_audio)-1},
+ {MIMETYPE_APPLICATION, mime_group_application, sizeof(mime_group_application)-1},
+ {MIMETYPE_IMAGE, mime_group_image, sizeof(mime_group_image)-1},
+ {MIMETYPE_VIDEO, mime_group_video, sizeof(mime_group_video)-1},
+ {MIMETYPE_LAST, NULL, 0} // Must be last entry
+};
+
+// List of all mimetypes that should be converted.
+static struct MimeTypeList mimeTypeList[] = {
+ // Mp3 mime types
+ {MIMETYPE_AUDIO, "mp3", sizeof("mp3")-1, mime_type_audio_mpeg},
+ {MIMETYPE_AUDIO, "x-mpeg", sizeof("x-mpeg")-1, mime_type_audio_mpeg},
+ {MIMETYPE_AUDIO, "x-mp3", sizeof("x-mp3")-1, mime_type_audio_mpeg},
+ {MIMETYPE_AUDIO, "mpg", sizeof("mpg")-1, mime_type_audio_mpeg},
+ {MIMETYPE_AUDIO, "mpg3", sizeof("mpg")-1, mime_type_audio_mpeg},
+ {MIMETYPE_AUDIO, "x-mpg", sizeof("x-mpg")-1, mime_type_audio_mpeg},
+ {MIMETYPE_AUDIO, "x-mpegaudio", sizeof("x-mpegaudio")-1, mime_type_audio_mpeg},
+
+ // 3gpp audio mime types
+ {MIMETYPE_AUDIO, "3gp", sizeof("3gp")-1, mime_type_audio_3gpp},
+
+ // Amr audio mime types
+ {MIMETYPE_AUDIO, "amr", sizeof("amr")-1, mime_type_audio_amr},
+
+ // Aac audio mime types
+ {MIMETYPE_AUDIO, "aac", sizeof("aac")-1, mime_type_audio_aac},
+
+ // Wav audio mime types
+ {MIMETYPE_AUDIO, "x-wav", sizeof("x-wav")-1, mime_type_audio_wav},
+
+ // Mpeg4 video mime types
+ {MIMETYPE_VIDEO, "mpg4", sizeof("mpg4")-1, mime_type_video_mpeg4},
+ {MIMETYPE_VIDEO, "mp4v-es", sizeof("mp4v-es")-1, mime_type_video_mpeg4},
+
+ // 3gpp video mime types
+ {MIMETYPE_VIDEO, "3gp", sizeof("3gp")-1, mime_type_video_3gpp},
+
+ // Must be last entry
+ {MIMETYPE_LAST, NULL, 0, NULL}
+};
+
+/**
+ * May convert the mimetype if there is a well known
+ * replacement mimetype otherwise the original mimetype
+ * is returned.
+ *
+ * @param mimeType - mimetype in lower case to convert.
+ *
+ * @return mimetype or null.
+ */
+String8 MimeTypeUtil::convertMimeType(String8& mimeType) {
+ String8 result = mimeType;
+ const char* pTmp;
+ const char* pMimeType;
+ struct MimeGroup* pGroup;
+ struct MimeTypeList* pMimeItem;
+ int len;
+
+ pMimeType = mimeType.string();
+ if (NULL != pMimeType) {
+ /* Check which group the mimetype is */
+ pGroup = mimeGroup;
+
+ while (MIMETYPE_LAST != pGroup->type) {
+ if (0 == strncmp(pMimeType, pGroup->pGroup, pGroup->size)) {
+ break;
+ }
+ pGroup++;
+ }
+
+ /* Go through the mimetype list. Only check items of the correct group */
+ if (MIMETYPE_LAST != pGroup->type) {
+ pMimeItem = mimeTypeList;
+ len = strlen (pMimeType+pGroup->size);
+
+ while (MIMETYPE_LAST != pMimeItem->type) {
+ if ((len == pMimeItem->size) &&
+ (0 == strcmp(pMimeType+pGroup->size, pMimeItem->pMimeExt))) {
+ result = String8(pMimeItem->pMimeType);
+ break;
+ }
+ pMimeItem++;
+ }
+ }
+ LOGI("convertMimeType got mimetype %s, converted into mimetype %s",
+ pMimeType, result.string());
+ }
+
+ return result;
+}
+};
diff --git a/drm/libdrmframework/plugins/forward-lock/Android.mk b/drm/libdrmframework/plugins/forward-lock/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/Android.mk
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+include $(call all-subdir-makefiles)
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
new file mode 100644
index 0000000..d4a6f18
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
@@ -0,0 +1,67 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+base := frameworks/base
+
+# Determine whether the DRM framework uses 64-bit data types for file offsets and do the same.
+ifneq ($(shell grep -c 'off64_t offset' $(base)/drm/libdrmframework/plugins/common/include/IDrmEngine.h), 0)
+LOCAL_CFLAGS += -DUSE_64BIT_DRM_API
+endif
+
+LOCAL_SRC_FILES:= \
+ src/FwdLockEngine.cpp
+
+LOCAL_MODULE := libfwdlockengine
+
+LOCAL_SHARED_LIBRARIES := \
+ libicui18n \
+ libicuuc \
+ libutils \
+ libdl \
+ libandroid_runtime \
+ libnativehelper \
+ libcrypto \
+ libssl \
+ libdrmframework
+
+LOCAL_STATIC_LIBRARIES := \
+ libdrmutility \
+ libdrmframeworkcommon \
+ libfwdlock-common \
+ libfwdlock-converter \
+ libfwdlock-decoder
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE) \
+ $(base)/include/drm \
+ $(base)/drm/libdrmframework/plugins/common/include \
+ $(base)/drm/libdrmframework/plugins/common/util/include \
+ $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/common \
+ $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/converter \
+ $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/decoder \
+ $(LOCAL_PATH)/include \
+ external/openssl/include
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/drm/plugins/native
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
new file mode 100644
index 0000000..34804cf
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FWDLOCKENGINE_H__
+#define __FWDLOCKENGINE_H__
+
+#include <DrmEngineBase.h>
+#include <DrmConstraints.h>
+#include <DrmRights.h>
+#include <DrmInfo.h>
+#include <DrmInfoStatus.h>
+#include <DrmConvertedStatus.h>
+#include <DrmInfoRequest.h>
+#include <DrmSupportInfo.h>
+#include <DrmInfoEvent.h>
+
+#include "SessionMap.h"
+#include "FwdLockConv.h"
+
+namespace android {
+
+/**
+ * Forward Lock Engine class.
+ */
+class FwdLockEngine : public android::DrmEngineBase {
+
+public:
+ FwdLockEngine();
+ virtual ~FwdLockEngine();
+
+protected:
+/**
+ * Get constraint information associated with input content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @param action Actions defined such as,
+ * Action::DEFAULT, Action::PLAY, etc
+ * @return DrmConstraints
+ * key-value pairs of constraint are embedded in it
+ * @note
+ * In case of error, return NULL
+ */
+DrmConstraints* onGetConstraints(int uniqueId, const String8* path, int action);
+
+/**
+ * Get metadata information associated with input content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @return DrmMetadata
+ * For Forward Lock engine, it returns an empty object
+ * @note
+ * In case of error, returns NULL
+ */
+DrmMetadata* onGetMetadata(int uniqueId, const String8* path);
+
+/**
+ * Initialize plug-in.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onInitialize(int uniqueId);
+
+/**
+ * Register a callback to be invoked when the caller required to
+ * receive necessary information.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param infoListener Listener
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener);
+
+/**
+ * Terminate the plug-in and release resources bound to it.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onTerminate(int uniqueId);
+
+/**
+ * Get whether the given content can be handled by this plugin or not.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path to the protected object
+ * @return bool
+ * Returns true if this plugin can handle , false in case of not able to handle
+ */
+bool onCanHandle(int uniqueId, const String8& path);
+
+/**
+ * Processes the given DRM information as appropriate for its type.
+ * Not used for Forward Lock Engine.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param drmInfo Information that needs to be processed
+ * @return DrmInfoStatus
+ * instance as a result of processing given input
+ */
+DrmInfoStatus* onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+/**
+ * Save DRM rights to specified rights path
+ * and make association with content path.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param drmRights DrmRights to be saved
+ * @param rightsPath File path where rights to be saved
+ * @param contentPath File path where content was saved
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onSaveRights(int uniqueId,
+ const DrmRights& drmRights,
+ const String8& rightsPath,
+ const String8& contentPath);
+
+/**
+ * Retrieves necessary information for registration, unregistration or rights
+ * acquisition information.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param drmInfoRequest Request information to retrieve drmInfo
+ * @return DrmInfo
+ * instance as a result of processing given input
+ */
+DrmInfo* onAcquireDrmInfo(int uniqueId,
+ const DrmInfoRequest* drmInfoRequest);
+
+/**
+ * Retrieves the mime type embedded inside the original content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @return String8
+ * Returns mime-type of the original content, such as "video/mpeg"
+ */
+String8 onGetOriginalMimeType(int uniqueId, const String8& path);
+
+/**
+ * Retrieves the type of the protected object (content, rights, etc..)
+ * using specified path or mimetype. At least one parameter should be non null
+ * to retrieve DRM object type.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the content or null.
+ * @param mimeType Mime type of the content or null.
+ * @return type of the DRM content,
+ * such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
+ */
+int onGetDrmObjectType(int uniqueId,
+ const String8& path,
+ const String8& mimeType);
+
+/**
+ * Check whether the given content has valid rights or not.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @param action Action to perform (Action::DEFAULT, Action::PLAY, etc)
+ * @return the status of the rights for the protected content,
+ * such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc.
+ */
+int onCheckRightsStatus(int uniqueId,
+ const String8& path,
+ int action);
+
+/**
+ * Consumes the rights for a content.
+ * If the reserve parameter is true the rights are reserved until the same
+ * application calls this api again with the reserve parameter set to false.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param action Action to perform. (Action::DEFAULT, Action::PLAY, etc)
+ * @param reserve True if the rights should be reserved.
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onConsumeRights(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int action,
+ bool reserve);
+
+/**
+ * Informs the DRM Engine about the playback actions performed on the DRM files.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE)
+ * @param position Position in the file (in milliseconds) where the start occurs.
+ * Only valid together with Playback::START.
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+#ifdef USE_64BIT_DRM_API
+status_t onSetPlaybackStatus(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int playbackStatus,
+ int64_t position);
+#else
+status_t onSetPlaybackStatus(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int playbackStatus,
+ int position);
+#endif
+
+/**
+ * Validates whether an action on the DRM content is allowed or not.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @param action Action to validate (Action::PLAY, Action::TRANSFER, etc)
+ * @param description Detailed description of the action
+ * @return true if the action is allowed.
+ */
+bool onValidateAction(int uniqueId,
+ const String8& path,
+ int action,
+ const ActionDescription& description);
+
+/**
+ * Removes the rights associated with the given protected content.
+ * Not used for Forward Lock Engine.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onRemoveRights(int uniqueId, const String8& path);
+
+/**
+ * Removes all the rights information of each plug-in associated with
+ * DRM framework. Will be used in master reset but does nothing for
+ * Forward Lock Engine.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onRemoveAllRights(int uniqueId);
+
+/**
+ * Starts the Forward Lock file conversion session.
+ * Each time the application tries to download a new DRM file
+ * which needs to be converted, then the application has to
+ * begin with calling this API. The convertId is used as the conversion session key
+ * and must not be the same for different convert sessions.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param convertId Handle for the convert session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onOpenConvertSession(int uniqueId, int convertId);
+
+/**
+ * Accepts and converts the input data which is part of DRM file.
+ * The resultant converted data and the status is returned in the DrmConvertedInfo
+ * object. This method will be called each time there is a new block
+ * of data received by the application.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param convertId Handle for the convert session
+ * @param inputData Input Data which need to be converted
+ * @return Return object contains the status of the data conversion,
+ * the output converted data and offset. In this case the
+ * application will ignore the offset information.
+ */
+DrmConvertedStatus* onConvertData(int uniqueId,
+ int convertId,
+ const DrmBuffer* inputData);
+
+/**
+ * Closes the convert session in case of data supply completed or error occurred.
+ * Upon successful conversion of the complete data, it returns signature calculated over
+ * the entire data used over a conversion session. This signature must be copied to the offset
+ * mentioned in the DrmConvertedStatus. Signature is used for data integrity protection.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param convertId Handle for the convert session
+ * @return Return object contains the status of the data conversion,
+ * the header and body signature data. It also informs
+ * the application about the file offset at which this
+ * signature data should be written.
+ */
+DrmConvertedStatus* onCloseConvertSession(int uniqueId, int convertId);
+
+/**
+ * Returns the information about the Drm Engine capabilities which includes
+ * supported MimeTypes and file suffixes.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return DrmSupportInfo
+ * instance which holds the capabilities of a plug-in
+ */
+DrmSupportInfo* onGetSupportInfo(int uniqueId);
+
+/**
+ * Open the decrypt session to decrypt the given protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the current decryption session
+ * @param fd File descriptor of the protected content to be decrypted
+ * @param offset Start position of the content
+ * @param length The length of the protected content
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+#ifdef USE_64BIT_DRM_API
+status_t onOpenDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int fd, off64_t offset, off64_t length);
+#else
+status_t onOpenDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int fd, int offset, int length);
+#endif
+
+/**
+ * Open the decrypt session to decrypt the given protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the current decryption session
+ * @param uri Path of the protected content to be decrypted
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t onOpenDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle,
+ const char* uri);
+
+/**
+ * Close the decrypt session for the given handle.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onCloseDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle);
+
+/**
+ * Initialize decryption for the given unit of the protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param headerInfo Information for initializing decryption of this decrypUnit
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t onInitializeDecryptUnit(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int decryptUnitId,
+ const DrmBuffer* headerInfo);
+
+/**
+ * Decrypt the protected content buffers for the given unit.
+ * This method will be called any number of times, based on number of
+ * encrypted streams received from application.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param encBuffer Encrypted data block
+ * @param decBuffer Decrypted data block
+ * @return status_t
+ * Returns the error code for this API
+ * DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED
+ * DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
+ * DRM_ERROR_DECRYPT for failure.
+ */
+status_t onDecrypt(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int decryptUnitId,
+ const DrmBuffer* encBuffer,
+ DrmBuffer** decBuffer);
+
+/**
+ * Decrypt the protected content buffers for the given unit.
+ * This method will be called any number of times, based on number of
+ * encrypted streams received from application.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptId Handle for the decryption session
+ * @param decryptUnitId ID Specifies decryption unit, such as track ID
+ * @param encBuffer Encrypted data block
+ * @param decBuffer Decrypted data block
+ * @param IV Optional buffer
+ * @return status_t
+ * Returns the error code for this API
+ * DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED
+ * DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
+ * DRM_ERROR_DECRYPT for failure.
+ */
+status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* encBuffer,
+ DrmBuffer** decBuffer, DrmBuffer* IV);
+
+/**
+ * Finalize decryption for the given unit of the protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param decryptUnitId ID Specifies decryption unit, such as track ID
+ * @return
+ * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t onFinalizeDecryptUnit(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int decryptUnitId);
+
+/**
+ * Reads the specified number of bytes from an open DRM file.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param buffer Reference to the buffer that should receive the read data.
+ * @param numBytes Number of bytes to read.
+ *
+ * @return Number of bytes read.
+ * @retval -1 Failure.
+ */
+ssize_t onRead(int uniqueId,
+ DecryptHandle* decryptHandle,
+ void* pBuffer,
+ int numBytes);
+
+/**
+ * Updates the file position within an open DRM file.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param offset Offset with which to update the file position.
+ * @param whence One of SEEK_SET, SEEK_CUR, and SEEK_END.
+ * These constants are defined in unistd.h.
+ *
+ * @return New file position.
+ * @retval ((off_t)-1) Failure.
+ */
+#ifdef USE_64BIT_DRM_API
+off64_t onLseek(int uniqueId,
+ DecryptHandle* decryptHandle,
+ off64_t offset,
+ int whence);
+#else
+off_t onLseek(int uniqueId,
+ DecryptHandle* decryptHandle,
+ off_t offset,
+ int whence);
+#endif
+
+/**
+ * Reads the specified number of bytes from an open DRM file.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param buffer Reference to the buffer that should receive the read data.
+ * @param numBytes Number of bytes to read.
+ * @param offset Offset with which to update the file position.
+ *
+ * @return Number of bytes read. Returns -1 for Failure.
+ */
+#ifdef USE_64BIT_DRM_API
+ssize_t onPread(int uniqueId,
+ DecryptHandle* decryptHandle,
+ void* buffer,
+ ssize_t numBytes,
+ off64_t offset);
+#else
+ssize_t onPread(int uniqueId,
+ DecryptHandle* decryptHandle,
+ void* buffer,
+ ssize_t numBytes,
+ off_t offset);
+#endif
+
+private:
+
+/**
+ * Session Class for Forward Lock Conversion. An object of this class is created
+ * for every conversion.
+ */
+class ConvertSession {
+ public :
+ int uniqueId;
+ FwdLockConv_Output_t output;
+
+ ConvertSession() {
+ uniqueId = 0;
+ memset(&output, 0, sizeof(FwdLockConv_Output_t));
+ }
+
+ virtual ~ConvertSession() {}
+};
+
+/**
+ * Session Class for Forward Lock decoder. An object of this class is created
+ * for every decoding session.
+ */
+class DecodeSession {
+ public :
+ int fileDesc;
+ off_t offset;
+
+ DecodeSession() {
+ fileDesc = -1;
+ offset = 0;
+ }
+
+ DecodeSession(int fd) {
+ fileDesc = fd;
+ offset = 0;
+ }
+
+ virtual ~DecodeSession() {}
+};
+
+/**
+ * Session Map Tables for Conversion and Decoding of forward lock files.
+ */
+SessionMap<ConvertSession*> convertSessionMap;
+SessionMap<DecodeSession*> decodeSessionMap;
+
+/**
+ * Converts the error code from Forward Lock Converter to DrmConvertStatus error code.
+ *
+ * @param Forward Lock Converter error code
+ *
+ * @return Status code from DrmConvertStatus.
+ */
+static int getConvertedStatus(FwdLockConv_Status_t status);
+};
+
+};
+
+#endif /* __FWDLOCKENGINE_H__ */
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h
new file mode 100644
index 0000000..da95d60
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FWDLOCKENGINECONST_H__
+#define __FWDLOCKENGINECONST_H__
+
+namespace android {
+
+/**
+ * Constants for forward Lock Engine used for exposing engine's capabilities.
+ */
+#define FWDLOCK_EXTENSION_FL ("FL")
+#define FWDLOCK_DOTEXTENSION_FL (".fl")
+#define FWDLOCK_MIMETYPE_FL ("application/x-android-drm-fl")
+
+#define FWDLOCK_EXTENSION_DM ("DM")
+#define FWDLOCK_DOTEXTENSION_DM (".dm")
+#define FWDLOCK_MIMETYPE_DM ("application/vnd.oma.drm.message")
+
+#define FWDLOCK_DESCRIPTION ("OMA V1 Forward Lock")
+
+};
+
+#endif /* __FWDLOCKENGINECONST_H__ */
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
new file mode 100644
index 0000000..d430f72
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SessionMap.h"
+#include "FwdLockEngine.h"
+#include <utils/Log.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "drm_framework_common.h"
+#include <fcntl.h>
+#include <limits.h>
+#include <DrmRights.h>
+#include <DrmConstraints.h>
+#include <DrmMetadata.h>
+#include <DrmInfo.h>
+#include <DrmInfoStatus.h>
+#include <DrmInfoRequest.h>
+#include <DrmSupportInfo.h>
+#include <DrmConvertedStatus.h>
+#include <utils/String8.h>
+#include "FwdLockConv.h"
+#include "FwdLockFile.h"
+#include "FwdLockGlue.h"
+#include "FwdLockEngineConst.h"
+#include "MimeTypeUtil.h"
+
+#undef LOG_TAG
+#define LOG_TAG "FwdLockEngine"
+
+using namespace android;
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" IDrmEngine* create() {
+ return new FwdLockEngine();
+}
+
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" void destroy(IDrmEngine* plugIn) {
+ delete plugIn;
+}
+
+FwdLockEngine::FwdLockEngine() {
+ LOGD("FwdLockEngine Construction");
+}
+
+FwdLockEngine::~FwdLockEngine() {
+ LOGD("FwdLockEngine Destruction");
+
+ convertSessionMap.destroyMap();
+ decodeSessionMap.destroyMap();
+}
+
+int FwdLockEngine::getConvertedStatus(FwdLockConv_Status_t status) {
+ int retStatus = DrmConvertedStatus::STATUS_ERROR;
+
+ switch(status) {
+ case FwdLockConv_Status_OK:
+ retStatus = DrmConvertedStatus::STATUS_OK;
+ break;
+ case FwdLockConv_Status_SyntaxError:
+ case FwdLockConv_Status_InvalidArgument:
+ case FwdLockConv_Status_UnsupportedFileFormat:
+ case FwdLockConv_Status_UnsupportedContentTransferEncoding:
+ LOGD("FwdLockEngine getConvertedStatus: file conversion Error %d. " \
+ "Returning STATUS_INPUTDATA_ERROR", status);
+ retStatus = DrmConvertedStatus::STATUS_INPUTDATA_ERROR;
+ break;
+ default:
+ LOGD("FwdLockEngine getConvertedStatus: file conversion Error %d. " \
+ "Returning STATUS_ERROR", status);
+ retStatus = DrmConvertedStatus::STATUS_ERROR;
+ break;
+ }
+
+ return retStatus;
+}
+
+DrmConstraints* FwdLockEngine::onGetConstraints(int uniqueId, const String8* path, int action) {
+ DrmConstraints* drmConstraints = NULL;
+
+ LOGD("FwdLockEngine::onGetConstraints");
+
+ if (NULL != path &&
+ (RightsStatus::RIGHTS_VALID == onCheckRightsStatus(uniqueId, *path, action))) {
+ // Return the empty constraints to show no error condition.
+ drmConstraints = new DrmConstraints();
+ }
+
+ return drmConstraints;
+}
+
+DrmMetadata* FwdLockEngine::onGetMetadata(int uniqueId, const String8* path) {
+ DrmMetadata* drmMetadata = NULL;
+
+ LOGD("FwdLockEngine::onGetMetadata");
+
+ if (NULL != path) {
+ // Returns empty metadata to show no error condition.
+ drmMetadata = new DrmMetadata();
+ }
+
+ return drmMetadata;
+}
+
+android::status_t FwdLockEngine::onInitialize(int uniqueId) {
+ LOGD("FwdLockEngine::onInitialize");
+
+
+ if (FwdLockGlue_InitializeKeyEncryption()) {
+ LOGD("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption succeeded");
+ } else {
+ LOGD("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption failed:"
+ "errno = %d", errno);
+ }
+
+ return DRM_NO_ERROR;
+}
+
+android::status_t
+FwdLockEngine::onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener) {
+ // Not used
+ LOGD("FwdLockEngine::onSetOnInfoListener");
+
+ return DRM_NO_ERROR;
+}
+
+android::status_t FwdLockEngine::onTerminate(int uniqueId) {
+ LOGD("FwdLockEngine::onTerminate");
+
+ return DRM_NO_ERROR;
+}
+
+DrmSupportInfo* FwdLockEngine::onGetSupportInfo(int uniqueId) {
+ DrmSupportInfo* pSupportInfo = new DrmSupportInfo();
+
+ LOGD("FwdLockEngine::onGetSupportInfo");
+
+ // fill all Forward Lock mimetypes and extensions
+ if (NULL != pSupportInfo) {
+ pSupportInfo->addMimeType(String8(FWDLOCK_MIMETYPE_FL));
+ pSupportInfo->addFileSuffix(String8(FWDLOCK_DOTEXTENSION_FL));
+ pSupportInfo->addMimeType(String8(FWDLOCK_MIMETYPE_DM));
+ pSupportInfo->addFileSuffix(String8(FWDLOCK_DOTEXTENSION_DM));
+
+ pSupportInfo->setDescription(String8(FWDLOCK_DESCRIPTION));
+ }
+
+ return pSupportInfo;
+}
+
+bool FwdLockEngine::onCanHandle(int uniqueId, const String8& path) {
+ bool result = false;
+
+ String8 extString = path.getPathExtension();
+
+ extString.toLower();
+
+ if ((extString == String8(FWDLOCK_DOTEXTENSION_FL)) ||
+ (extString == String8(FWDLOCK_DOTEXTENSION_DM))) {
+ result = true;
+ }
+ return result;
+}
+
+DrmInfoStatus* FwdLockEngine::onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+ DrmInfoStatus *drmInfoStatus = NULL;
+
+ // Nothing to process
+
+ drmInfoStatus = new DrmInfoStatus((int)DrmInfoStatus::STATUS_OK, 0, NULL, String8(""));
+
+ LOGD("FwdLockEngine::onProcessDrmInfo");
+
+ return drmInfoStatus;
+}
+
+status_t FwdLockEngine::onSaveRights(
+ int uniqueId,
+ const DrmRights& drmRights,
+ const String8& rightsPath,
+ const String8& contentPath) {
+ // No rights to save. Return
+ LOGD("FwdLockEngine::onSaveRights");
+ return DRM_ERROR_UNKNOWN;
+}
+
+DrmInfo* FwdLockEngine::onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+ DrmInfo* drmInfo = NULL;
+
+ // Nothing to be done for Forward Lock file
+ LOGD("FwdLockEngine::onAcquireDrmInfo");
+
+ return drmInfo;
+}
+
+int FwdLockEngine::onCheckRightsStatus(int uniqueId,
+ const String8& path,
+ int action) {
+ int result = RightsStatus::RIGHTS_INVALID;
+
+ LOGD("FwdLockEngine::onCheckRightsStatus");
+
+ // Only Transfer action is not allowed for forward Lock files.
+ if (onCanHandle(uniqueId, path)) {
+ switch(action) {
+ case Action::DEFAULT:
+ case Action::PLAY:
+ case Action::RINGTONE:
+ case Action::OUTPUT:
+ case Action::PREVIEW:
+ case Action::EXECUTE:
+ case Action::DISPLAY:
+ result = RightsStatus::RIGHTS_VALID;
+ break;
+
+ case Action::TRANSFER:
+ default:
+ result = RightsStatus::RIGHTS_INVALID;
+ break;
+ }
+ }
+
+ return result;
+}
+
+status_t FwdLockEngine::onConsumeRights(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int action,
+ bool reserve) {
+ // No rights consumption
+ LOGD("FwdLockEngine::onConsumeRights");
+ return DRM_NO_ERROR;
+}
+
+bool FwdLockEngine::onValidateAction(int uniqueId,
+ const String8& path,
+ int action,
+ const ActionDescription& description) {
+ LOGD("FwdLockEngine::onValidateAction");
+
+ // For the forwardlock engine checkRights and ValidateAction are the same.
+ return (onCheckRightsStatus(uniqueId, path, action) == RightsStatus::RIGHTS_VALID);
+}
+
+String8 FwdLockEngine::onGetOriginalMimeType(int uniqueId, const String8& path) {
+ LOGD("FwdLockEngine::onGetOriginalMimeType");
+ String8 mimeString = String8("");
+ int fileDesc = FwdLockFile_open(path.string());
+
+ if (-1 < fileDesc) {
+ const char* pMimeType = FwdLockFile_GetContentType(fileDesc);
+
+ if (NULL != pMimeType) {
+ String8 contentType = String8(pMimeType);
+ contentType.toLower();
+ mimeString = MimeTypeUtil::convertMimeType(contentType);
+ }
+
+ FwdLockFile_close(fileDesc);
+ }
+
+ return mimeString;
+}
+
+int FwdLockEngine::onGetDrmObjectType(int uniqueId,
+ const String8& path,
+ const String8& mimeType) {
+ String8 mimeStr = String8(mimeType);
+
+ LOGD("FwdLockEngine::onGetDrmObjectType");
+
+ mimeStr.toLower();
+
+ /* Checks whether
+ * 1. path and mime type both are not empty strings (meaning unavailable) else content is unknown
+ * 2. if one of them is empty string and if other is known then its a DRM Content Object.
+ * 3. if both of them are available, then both may be of known type
+ * (regardless of the relation between them to make it compatible with other DRM Engines)
+ */
+ if (((0 == path.length()) || onCanHandle(uniqueId, path)) &&
+ ((0 == mimeType.length()) || ((mimeStr == String8(FWDLOCK_MIMETYPE_FL)) ||
+ (mimeStr == String8(FWDLOCK_MIMETYPE_DM)))) && (mimeType != path) ) {
+ return DrmObjectType::CONTENT;
+ }
+
+ return DrmObjectType::UNKNOWN;
+}
+
+status_t FwdLockEngine::onRemoveRights(int uniqueId, const String8& path) {
+ // No Rights to remove
+ LOGD("FwdLockEngine::onRemoveRights");
+ return DRM_NO_ERROR;
+}
+
+status_t FwdLockEngine::onRemoveAllRights(int uniqueId) {
+ // No rights to remove
+ LOGD("FwdLockEngine::onRemoveAllRights");
+ return DRM_NO_ERROR;
+}
+
+#ifdef USE_64BIT_DRM_API
+status_t FwdLockEngine::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+ int playbackStatus, int64_t position) {
+#else
+status_t FwdLockEngine::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+ int playbackStatus, int position) {
+#endif
+ // Not used
+ LOGD("FwdLockEngine::onSetPlaybackStatus");
+ return DRM_NO_ERROR;
+}
+
+status_t FwdLockEngine::onOpenConvertSession(int uniqueId,
+ int convertId) {
+ status_t result = DRM_ERROR_UNKNOWN;
+ LOGD("FwdLockEngine::onOpenConvertSession");
+ if (!convertSessionMap.isCreated(convertId)) {
+ ConvertSession *newSession = new ConvertSession();
+ if (FwdLockConv_Status_OK ==
+ FwdLockConv_OpenSession(&(newSession->uniqueId), &(newSession->output))) {
+ convertSessionMap.addValue(convertId, newSession);
+ result = DRM_NO_ERROR;
+ } else {
+ LOGD("FwdLockEngine::onOpenConvertSession -- FwdLockConv_OpenSession failed.");
+ delete newSession;
+ }
+ }
+ return result;
+}
+
+DrmConvertedStatus* FwdLockEngine::onConvertData(int uniqueId,
+ int convertId,
+ const DrmBuffer* inputData) {
+ FwdLockConv_Status_t retStatus = FwdLockConv_Status_InvalidArgument;
+ DrmBuffer *convResult = new DrmBuffer(NULL, 0);
+ int offset = -1;
+
+ if (NULL != inputData && convertSessionMap.isCreated(convertId)) {
+ ConvertSession *convSession = convertSessionMap.getValue(convertId);
+
+ if (NULL != convSession) {
+ retStatus = FwdLockConv_ConvertData(convSession->uniqueId,
+ inputData->data,
+ inputData->length,
+ &(convSession->output));
+
+ if (FwdLockConv_Status_OK == retStatus) {
+ // return bytes from conversion if available
+ if (convSession->output.fromConvertData.numBytes > 0) {
+ convResult->data = new char[convSession->output.fromConvertData.numBytes];
+
+ if (NULL != convResult->data) {
+ convResult->length = convSession->output.fromConvertData.numBytes;
+ memcpy(convResult->data,
+ (char *)convSession->output.fromConvertData.pBuffer,
+ convResult->length);
+ }
+ }
+ } else {
+ offset = convSession->output.fromConvertData.errorPos;
+ }
+ }
+ }
+ return new DrmConvertedStatus(getConvertedStatus(retStatus), convResult, offset);
+}
+
+DrmConvertedStatus* FwdLockEngine::onCloseConvertSession(int uniqueId,
+ int convertId) {
+ FwdLockConv_Status_t retStatus = FwdLockConv_Status_InvalidArgument;
+ DrmBuffer *convResult = new DrmBuffer(NULL, 0);
+ int offset = -1;
+
+ LOGD("FwdLockEngine::onCloseConvertSession");
+
+ if (convertSessionMap.isCreated(convertId)) {
+ ConvertSession *convSession = convertSessionMap.getValue(convertId);
+
+ if (NULL != convSession) {
+ retStatus = FwdLockConv_CloseSession(convSession->uniqueId, &(convSession->output));
+
+ if (FwdLockConv_Status_OK == retStatus) {
+ offset = convSession->output.fromCloseSession.fileOffset;
+ convResult->data = new char[FWD_LOCK_SIGNATURES_SIZE];
+
+ if (NULL != convResult->data) {
+ convResult->length = FWD_LOCK_SIGNATURES_SIZE;
+ memcpy(convResult->data,
+ (char *)convSession->output.fromCloseSession.signatures,
+ convResult->length);
+ }
+ }
+ }
+ convertSessionMap.removeValue(convertId);
+ }
+ return new DrmConvertedStatus(getConvertedStatus(retStatus), convResult, offset);
+}
+
+#ifdef USE_64BIT_DRM_API
+status_t FwdLockEngine::onOpenDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int fd,
+ off64_t offset,
+ off64_t length) {
+#else
+status_t FwdLockEngine::onOpenDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int fd,
+ int offset,
+ int length) {
+#endif
+ status_t result = DRM_ERROR_CANNOT_HANDLE;
+ int fileDesc = -1;
+
+ LOGD("FwdLockEngine::onOpenDecryptSession");
+
+ if ((-1 < fd) &&
+ (NULL != decryptHandle) &&
+ (!decodeSessionMap.isCreated(decryptHandle->decryptId))) {
+ fileDesc = dup(fd);
+ } else {
+ LOGD("FwdLockEngine::onOpenDecryptSession parameter error");
+ return result;
+ }
+
+ if (-1 < fileDesc &&
+ -1 < ::lseek(fileDesc, offset, SEEK_SET) &&
+ -1 < FwdLockFile_attach(fileDesc)) {
+ // check for file integrity. This must be done to protect the content mangling.
+ int retVal = FwdLockFile_CheckHeaderIntegrity(fileDesc);
+ DecodeSession* decodeSession = new DecodeSession(fileDesc);
+
+ if (retVal && NULL != decodeSession) {
+ decodeSessionMap.addValue(decryptHandle->decryptId, decodeSession);
+ const char *pmime= FwdLockFile_GetContentType(fileDesc);
+ String8 contentType = String8(pmime == NULL ? "" : pmime);
+ contentType.toLower();
+ decryptHandle->mimeType = MimeTypeUtil::convertMimeType(contentType);
+ decryptHandle->decryptApiType = DecryptApiType::CONTAINER_BASED;
+ decryptHandle->status = RightsStatus::RIGHTS_VALID;
+ decryptHandle->decryptInfo = NULL;
+ result = DRM_NO_ERROR;
+ } else {
+ LOGD("FwdLockEngine::onOpenDecryptSession Integrity Check failed for the fd");
+ FwdLockFile_detach(fileDesc);
+ ::close(fileDesc);
+ delete decodeSession;
+ }
+ }
+
+ LOGD("FwdLockEngine::onOpenDecryptSession Exit. result = %d", result);
+
+ return result;
+}
+
+status_t FwdLockEngine::onOpenDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle,
+ const char* uri) {
+ status_t result = DRM_ERROR_CANNOT_HANDLE;
+ const char fileTag [] = "file://";
+
+ if (NULL != decryptHandle && NULL != uri && strlen(uri) > sizeof(fileTag)) {
+ String8 uriTag = String8(uri);
+ uriTag.toLower();
+
+ if (0 == strncmp(uriTag.string(), fileTag, sizeof(fileTag) - 1)) {
+ const char *filePath = strchr(uri + sizeof(fileTag) - 1, '/');
+ if (NULL != filePath && onCanHandle(uniqueId, String8(filePath))) {
+ int fd = open(filePath, O_RDONLY);
+
+ if (-1 < fd) {
+ // offset is always 0 and length is not used. so any positive size.
+ result = onOpenDecryptSession(uniqueId, decryptHandle, fd, 0, 1);
+
+ // fd is duplicated already if success. closing the file
+ close(fd);
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+status_t FwdLockEngine::onCloseDecryptSession(int uniqueId,
+ DecryptHandle* decryptHandle) {
+ status_t result = DRM_ERROR_UNKNOWN;
+ LOGD("FwdLockEngine::onCloseDecryptSession");
+
+ if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) {
+ DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
+ if (NULL != session && session->fileDesc > -1) {
+ FwdLockFile_detach(session->fileDesc);
+ ::close(session->fileDesc);
+ decodeSessionMap.removeValue(decryptHandle->decryptId);
+ result = DRM_NO_ERROR;
+ }
+ }
+
+ LOGD("FwdLockEngine::onCloseDecryptSession Exit");
+ return result;
+}
+
+status_t FwdLockEngine::onInitializeDecryptUnit(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int decryptUnitId,
+ const DrmBuffer* headerInfo) {
+ LOGD("FwdLockEngine::onInitializeDecryptUnit");
+ return DRM_ERROR_UNKNOWN;
+}
+
+status_t FwdLockEngine::onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+ LOGD("FwdLockEngine::onDecrypt");
+ return DRM_ERROR_UNKNOWN;
+}
+
+status_t FwdLockEngine::onDecrypt(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int decryptUnitId,
+ const DrmBuffer* encBuffer,
+ DrmBuffer** decBuffer) {
+ LOGD("FwdLockEngine::onDecrypt");
+ return DRM_ERROR_UNKNOWN;
+}
+
+status_t FwdLockEngine::onFinalizeDecryptUnit(int uniqueId,
+ DecryptHandle* decryptHandle,
+ int decryptUnitId) {
+ LOGD("FwdLockEngine::onFinalizeDecryptUnit");
+ return DRM_ERROR_UNKNOWN;
+}
+
+ssize_t FwdLockEngine::onRead(int uniqueId,
+ DecryptHandle* decryptHandle,
+ void* buffer,
+ int numBytes) {
+ ssize_t size = -1;
+
+ if (NULL != decryptHandle &&
+ decodeSessionMap.isCreated(decryptHandle->decryptId) &&
+ NULL != buffer &&
+ numBytes > -1) {
+ DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
+ if (NULL != session && session->fileDesc > -1) {
+ size = FwdLockFile_read(session->fileDesc, buffer, numBytes);
+
+ if (0 > size) {
+ session->offset = ((off_t)-1);
+ } else {
+ session->offset += size;
+ }
+ }
+ }
+
+ return size;
+}
+
+#ifdef USE_64BIT_DRM_API
+off64_t FwdLockEngine::onLseek(int uniqueId, DecryptHandle* decryptHandle,
+ off64_t offset, int whence) {
+#else
+off_t FwdLockEngine::onLseek(int uniqueId, DecryptHandle* decryptHandle,
+ off_t offset, int whence) {
+#endif
+ off_t offval = -1;
+
+ if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) {
+ DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
+ if (NULL != session && session->fileDesc > -1) {
+ offval = FwdLockFile_lseek(session->fileDesc, offset, whence);
+ session->offset = offval;
+ }
+ }
+
+ return offval;
+}
+
+#ifdef USE_64BIT_DRM_API
+ssize_t FwdLockEngine::onPread(int uniqueId,
+ DecryptHandle* decryptHandle,
+ void* buffer,
+ ssize_t numBytes,
+ off64_t offset) {
+#else
+ssize_t FwdLockEngine::onPread(int uniqueId,
+ DecryptHandle* decryptHandle,
+ void* buffer,
+ ssize_t numBytes,
+ off_t offset) {
+#endif
+ ssize_t bytesRead = -1;
+
+ DecodeSession* decoderSession = NULL;
+
+ if ((NULL != decryptHandle) &&
+ (NULL != (decoderSession = decodeSessionMap.getValue(decryptHandle->decryptId))) &&
+ (NULL != buffer) &&
+ (numBytes > -1) &&
+ (offset > -1)) {
+ if (offset != decoderSession->offset) {
+ decoderSession->offset = onLseek(uniqueId, decryptHandle, offset, SEEK_SET);
+ }
+
+ if (((off_t)-1) != decoderSession->offset) {
+ bytesRead = onRead(uniqueId, decryptHandle, buffer, numBytes);
+ if (bytesRead < 0) {
+ LOGD("FwdLockEngine::onPread error reading");
+ }
+ }
+ } else {
+ LOGD("FwdLockEngine::onPread decryptId not found");
+ }
+
+ return bytesRead;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+include $(call all-subdir-makefiles)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk
new file mode 100644
index 0000000..6c5d3cf
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ FwdLockGlue.c
+
+LOCAL_C_INCLUDES := \
+ external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := libcrypto
+
+LOCAL_MODULE := libfwdlock-common
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c
new file mode 100644
index 0000000..92bda8ff
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <openssl/aes.h>
+
+#include "FwdLockGlue.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define KEY_SIZE 16
+#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
+
+static int isInitialized = FALSE;
+
+static const char strKeyFilename[] = "/data/drm/fwdlock/kek.dat";
+
+static AES_KEY encryptionRoundKeys;
+static AES_KEY decryptionRoundKeys;
+
+/**
+ * Creates all directories along the fully qualified path of the given file.
+ *
+ * @param[in] path A reference to the fully qualified path of a file.
+ * @param[in] mode The access mode to use for the directories being created.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+static int FwdLockGlue_CreateDirectories(const char *path, mode_t mode) {
+ int result = TRUE;
+ size_t partialPathLength = strlen(path);
+ char *partialPath = malloc(partialPathLength + 1);
+ if (partialPath == NULL) {
+ result = FALSE;
+ } else {
+ size_t i;
+ for (i = 0; i < partialPathLength; ++i) {
+ if (path[i] == '/' && i > 0) {
+ partialPath[i] = '\0';
+ if (mkdir(partialPath, mode) != 0 && errno != EEXIST) {
+ result = FALSE;
+ break;
+ }
+ }
+ partialPath[i] = path[i];
+ }
+ free(partialPath);
+ }
+ return result;
+}
+
+/**
+ * Initializes the round keys used for encryption and decryption of session keys. First creates a
+ * device-unique key-encryption key if none exists yet.
+ */
+static void FwdLockGlue_InitializeRoundKeys() {
+ unsigned char keyEncryptionKey[KEY_SIZE];
+ int fileDesc = open(strKeyFilename, O_RDONLY);
+ if (fileDesc >= 0) {
+ if (read(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) {
+ isInitialized = TRUE;
+ }
+ (void)close(fileDesc);
+ } else if (errno == ENOENT &&
+ FwdLockGlue_GetRandomNumber(keyEncryptionKey, KEY_SIZE) &&
+ FwdLockGlue_CreateDirectories(strKeyFilename, S_IRWXU)) {
+ fileDesc = open(strKeyFilename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR);
+ if (fileDesc >= 0) {
+ if (write(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) {
+ isInitialized = TRUE;
+ }
+ (void)close(fileDesc);
+ }
+ }
+ if (isInitialized) {
+ if (AES_set_encrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &encryptionRoundKeys) != 0 ||
+ AES_set_decrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &decryptionRoundKeys) != 0) {
+ isInitialized = FALSE;
+ }
+ }
+ memset(keyEncryptionKey, 0, KEY_SIZE); // Zero out key data.
+}
+
+/**
+ * Validates the padding of a decrypted key.
+ *
+ * @param[in] pData A reference to the buffer containing the decrypted key and padding.
+ * @param[in] decryptedKeyLength The length in bytes of the decrypted key.
+ *
+ * @return A Boolean value indicating whether the padding was valid.
+ */
+static int FwdLockGlue_ValidatePadding(const unsigned char *pData, size_t decryptedKeyLength) {
+ size_t i;
+ size_t padding = AES_BLOCK_SIZE - (decryptedKeyLength % AES_BLOCK_SIZE);
+ pData += decryptedKeyLength;
+ for (i = 0; i < padding; ++i) {
+ if ((size_t)*pData != padding) {
+ return FALSE;
+ }
+ ++pData;
+ }
+ return TRUE;
+}
+
+int FwdLockGlue_GetRandomNumber(void *pBuffer, size_t numBytes) {
+ // Generate 'cryptographically secure' random bytes by reading them from "/dev/urandom" (the
+ // non-blocking version of "/dev/random").
+ ssize_t numBytesRead = 0;
+ int fileDesc = open("/dev/urandom", O_RDONLY);
+ if (fileDesc >= 0) {
+ numBytesRead = read(fileDesc, pBuffer, numBytes);
+ (void)close(fileDesc);
+ }
+ return numBytesRead >= 0 && (size_t)numBytesRead == numBytes;
+}
+
+int FwdLockGlue_InitializeKeyEncryption() {
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, FwdLockGlue_InitializeRoundKeys);
+ return isInitialized;
+}
+
+size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength) {
+ return ((plaintextKeyLength / AES_BLOCK_SIZE) + 2) * AES_BLOCK_SIZE;
+}
+
+int FwdLockGlue_EncryptKey(const void *pPlaintextKey,
+ size_t plaintextKeyLength,
+ void *pEncryptedKey,
+ size_t encryptedKeyLength) {
+ int result = FALSE;
+ assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(plaintextKeyLength));
+ if (FwdLockGlue_InitializeKeyEncryption()) {
+ unsigned char initVector[AES_BLOCK_SIZE];
+ if (FwdLockGlue_GetRandomNumber(initVector, AES_BLOCK_SIZE)) {
+ size_t padding = AES_BLOCK_SIZE - (plaintextKeyLength % AES_BLOCK_SIZE);
+ size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE;
+ memcpy(pEncryptedKey, pPlaintextKey, plaintextKeyLength);
+ memset((unsigned char *)pEncryptedKey + plaintextKeyLength, (int)padding, padding);
+ memcpy((unsigned char *)pEncryptedKey + dataLength, initVector, AES_BLOCK_SIZE);
+ AES_cbc_encrypt(pEncryptedKey, pEncryptedKey, dataLength, &encryptionRoundKeys,
+ initVector, AES_ENCRYPT);
+ result = TRUE;
+ }
+ }
+ return result;
+}
+
+int FwdLockGlue_DecryptKey(const void *pEncryptedKey,
+ size_t encryptedKeyLength,
+ void *pDecryptedKey,
+ size_t decryptedKeyLength) {
+ int result = FALSE;
+ assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(decryptedKeyLength));
+ if (FwdLockGlue_InitializeKeyEncryption()) {
+ size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE;
+ unsigned char *pData = malloc(dataLength);
+ if (pData != NULL) {
+ unsigned char initVector[AES_BLOCK_SIZE];
+ memcpy(pData, pEncryptedKey, dataLength);
+ memcpy(initVector, (const unsigned char *)pEncryptedKey + dataLength, AES_BLOCK_SIZE);
+ AES_cbc_encrypt(pData, pData, dataLength, &decryptionRoundKeys, initVector,
+ AES_DECRYPT);
+ memcpy(pDecryptedKey, pData, decryptedKeyLength);
+ result = FwdLockGlue_ValidatePadding(pData, decryptedKeyLength);
+ free(pData);
+ }
+ }
+ return result;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h
new file mode 100644
index 0000000..f36f6ea
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FWDLOCKGLUE_H__
+#define __FWDLOCKGLUE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Generates the specified number of cryptographically secure random bytes.
+ *
+ * @param[out] pBuffer A reference to the buffer that should receive the random data.
+ * @param[in] numBytes The number of random bytes to generate.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_GetRandomNumber(void *pBuffer, size_t numBytes);
+
+/**
+ * Performs initialization of the key-encryption key. Should be called once during startup to
+ * facilitate encryption and decryption of session keys.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_InitializeKeyEncryption();
+
+/**
+ * Returns the length of the encrypted key, given the length of the plaintext key.
+ *
+ * @param[in] plaintextKeyLength The length in bytes of the plaintext key.
+ *
+ * @return The length in bytes of the encrypted key.
+ */
+size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength);
+
+/**
+ * Encrypts the given session key using a key-encryption key unique to this device.
+ *
+ * @param[in] pPlaintextKey A reference to the buffer containing the plaintext key.
+ * @param[in] plaintextKeyLength The length in bytes of the plaintext key.
+ * @param[out] pEncryptedKey A reference to the buffer containing the encrypted key.
+ * @param[in] encryptedKeyLength The length in bytes of the encrypted key.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_EncryptKey(const void *pPlaintextKey,
+ size_t plaintextKeyLength,
+ void *pEncryptedKey,
+ size_t encryptedKeyLength);
+
+/**
+ * Decrypts the given session key using a key-encryption key unique to this device.
+ *
+ * @param[in] pEncryptedKey A reference to the buffer containing the encrypted key.
+ * @param[in] encryptedKeyLength The length in bytes of the encrypted key.
+ * @param[out] pDecryptedKey A reference to the buffer containing the decrypted key.
+ * @param[in] decryptedKeyLength The length in bytes of the decrypted key.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_DecryptKey(const void *pEncryptedKey,
+ size_t encryptedKeyLength,
+ void *pDecryptedKey,
+ size_t decryptedKeyLength);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __FWDLOCKGLUE_H__
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk
new file mode 100644
index 0000000..00bb788
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ FwdLockConv.c
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/drm/libdrmframework/plugins/forward-lock/internal-format/common \
+ external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := libcrypto
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_MODULE := libfwdlock-converter
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
new file mode 100644
index 0000000..14ea9e9
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
@@ -0,0 +1,1339 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <openssl/aes.h>
+#include <openssl/hmac.h>
+
+#include "FwdLockConv.h"
+#include "FwdLockGlue.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define INVALID_OFFSET ((off64_t)-1)
+
+#define MAX_NUM_SESSIONS 32
+
+#define OUTPUT_BUFFER_SIZE_INCREMENT 1024
+#define READ_BUFFER_SIZE 1024
+
+#define MAX_BOUNDARY_LENGTH 70
+#define MAX_DELIMITER_LENGTH (MAX_BOUNDARY_LENGTH + 4)
+
+#define STRING_LENGTH_INCREMENT 25
+
+#define KEY_SIZE AES_BLOCK_SIZE
+#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
+
+#define SHA1_HASH_SIZE 20
+
+#define FWD_LOCK_VERSION 0
+#define FWD_LOCK_SUBFORMAT 0
+#define USAGE_RESTRICTION_FLAGS 0
+#define CONTENT_TYPE_LENGTH_POS 7
+#define TOP_HEADER_SIZE 8
+
+/**
+ * Data type for the parser states of the converter.
+ */
+typedef enum FwdLockConv_ParserState {
+ FwdLockConv_ParserState_WantsOpenDelimiter,
+ FwdLockConv_ParserState_WantsMimeHeaders,
+ FwdLockConv_ParserState_WantsBinaryEncodedData,
+ FwdLockConv_ParserState_WantsBase64EncodedData,
+ FwdLockConv_ParserState_Done
+} FwdLockConv_ParserState_t;
+
+/**
+ * Data type for the scanner states of the converter.
+ */
+typedef enum FwdLockConv_ScannerState {
+ FwdLockConv_ScannerState_WantsFirstDash,
+ FwdLockConv_ScannerState_WantsSecondDash,
+ FwdLockConv_ScannerState_WantsCR,
+ FwdLockConv_ScannerState_WantsLF,
+ FwdLockConv_ScannerState_WantsBoundary,
+ FwdLockConv_ScannerState_WantsBoundaryEnd,
+ FwdLockConv_ScannerState_WantsMimeHeaderNameStart,
+ FwdLockConv_ScannerState_WantsMimeHeaderName,
+ FwdLockConv_ScannerState_WantsMimeHeaderNameEnd,
+ FwdLockConv_ScannerState_WantsContentTypeStart,
+ FwdLockConv_ScannerState_WantsContentType,
+ FwdLockConv_ScannerState_WantsContentTransferEncodingStart,
+ FwdLockConv_ScannerState_Wants_A_OR_I,
+ FwdLockConv_ScannerState_Wants_N,
+ FwdLockConv_ScannerState_Wants_A,
+ FwdLockConv_ScannerState_Wants_R,
+ FwdLockConv_ScannerState_Wants_Y,
+ FwdLockConv_ScannerState_Wants_S,
+ FwdLockConv_ScannerState_Wants_E,
+ FwdLockConv_ScannerState_Wants_6,
+ FwdLockConv_ScannerState_Wants_4,
+ FwdLockConv_ScannerState_Wants_B,
+ FwdLockConv_ScannerState_Wants_I,
+ FwdLockConv_ScannerState_Wants_T,
+ FwdLockConv_ScannerState_WantsContentTransferEncodingEnd,
+ FwdLockConv_ScannerState_WantsMimeHeaderValueEnd,
+ FwdLockConv_ScannerState_WantsMimeHeadersEnd,
+ FwdLockConv_ScannerState_WantsByte1,
+ FwdLockConv_ScannerState_WantsByte1_AfterCRLF,
+ FwdLockConv_ScannerState_WantsByte2,
+ FwdLockConv_ScannerState_WantsByte3,
+ FwdLockConv_ScannerState_WantsByte4,
+ FwdLockConv_ScannerState_WantsPadding,
+ FwdLockConv_ScannerState_WantsWhitespace,
+ FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF,
+ FwdLockConv_ScannerState_WantsDelimiter
+} FwdLockConv_ScannerState_t;
+
+/**
+ * Data type for the content transfer encoding.
+ */
+typedef enum FwdLockConv_ContentTransferEncoding {
+ FwdLockConv_ContentTransferEncoding_Undefined,
+ FwdLockConv_ContentTransferEncoding_Binary,
+ FwdLockConv_ContentTransferEncoding_Base64
+} FwdLockConv_ContentTransferEncoding_t;
+
+/**
+ * Data type for a dynamically growing string.
+ */
+typedef struct FwdLockConv_String {
+ char *ptr;
+ size_t length;
+ size_t maxLength;
+ size_t lengthIncrement;
+} FwdLockConv_String_t;
+
+/**
+ * Data type for the per-file state information needed by the converter.
+ */
+typedef struct FwdLockConv_Session {
+ FwdLockConv_ParserState_t parserState;
+ FwdLockConv_ScannerState_t scannerState;
+ FwdLockConv_ScannerState_t savedScannerState;
+ off64_t numCharsConsumed;
+ char delimiter[MAX_DELIMITER_LENGTH];
+ size_t delimiterLength;
+ size_t delimiterMatchPos;
+ FwdLockConv_String_t mimeHeaderName;
+ FwdLockConv_String_t contentType;
+ FwdLockConv_ContentTransferEncoding_t contentTransferEncoding;
+ unsigned char sessionKey[KEY_SIZE];
+ void *pEncryptedSessionKey;
+ size_t encryptedSessionKeyLength;
+ AES_KEY encryptionRoundKeys;
+ HMAC_CTX signingContext;
+ unsigned char topHeader[TOP_HEADER_SIZE];
+ unsigned char counter[AES_BLOCK_SIZE];
+ unsigned char keyStream[AES_BLOCK_SIZE];
+ int keyStreamIndex;
+ unsigned char ch;
+ size_t outputBufferSize;
+ size_t dataOffset;
+ size_t numDataBytes;
+} FwdLockConv_Session_t;
+
+static FwdLockConv_Session_t *sessionPtrs[MAX_NUM_SESSIONS] = { NULL };
+
+static pthread_mutex_t sessionAcquisitionMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static const FwdLockConv_String_t nullString = { NULL, 0, 0, STRING_LENGTH_INCREMENT };
+
+static const unsigned char topHeaderTemplate[] =
+ { 'F', 'W', 'L', 'K', FWD_LOCK_VERSION, FWD_LOCK_SUBFORMAT, USAGE_RESTRICTION_FLAGS };
+
+static const char strContent[] = "content-";
+static const char strType[] = "type";
+static const char strTransferEncoding[] = "transfer-encoding";
+static const char strTextPlain[] = "text/plain";
+static const char strApplicationVndOmaDrmRightsXml[] = "application/vnd.oma.drm.rights+xml";
+static const char strApplicationVndOmaDrmContent[] = "application/vnd.oma.drm.content";
+
+static const size_t strlenContent = sizeof strContent - 1;
+static const size_t strlenTextPlain = sizeof strTextPlain - 1;
+
+static const signed char base64Values[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+};
+
+/**
+ * Acquires an unused converter session.
+ *
+ * @return A session ID.
+ */
+static int FwdLockConv_AcquireSession() {
+ int sessionId = -1;
+ int i;
+ pthread_mutex_lock(&sessionAcquisitionMutex);
+ for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
+ if (sessionPtrs[i] == NULL) {
+ sessionPtrs[i] = malloc(sizeof *sessionPtrs[i]);
+ if (sessionPtrs[i] != NULL) {
+ sessionId = i;
+ }
+ break;
+ }
+ }
+ pthread_mutex_unlock(&sessionAcquisitionMutex);
+ return sessionId;
+}
+
+/**
+ * Checks whether a session ID is in range and currently in use.
+ *
+ * @param[in] sessionID A session ID.
+ *
+ * @return A Boolean value indicating whether the session ID is in range and currently in use.
+ */
+static int FwdLockConv_IsValidSession(int sessionId) {
+ return 0 <= sessionId && sessionId < MAX_NUM_SESSIONS && sessionPtrs[sessionId] != NULL;
+}
+
+/**
+ * Releases a converter session.
+ *
+ * @param[in] sessionID A session ID.
+ */
+static void FwdLockConv_ReleaseSession(int sessionId) {
+ pthread_mutex_lock(&sessionAcquisitionMutex);
+ assert(FwdLockConv_IsValidSession(sessionId));
+ memset(sessionPtrs[sessionId], 0, sizeof *sessionPtrs[sessionId]); // Zero out key data.
+ free(sessionPtrs[sessionId]);
+ sessionPtrs[sessionId] = NULL;
+ pthread_mutex_unlock(&sessionAcquisitionMutex);
+}
+
+/**
+ * Derives cryptographically independent keys for encryption and signing from the session key.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static int FwdLockConv_DeriveKeys(FwdLockConv_Session_t *pSession) {
+ FwdLockConv_Status_t status;
+ struct FwdLockConv_DeriveKeys_Data {
+ AES_KEY sessionRoundKeys;
+ unsigned char value[KEY_SIZE];
+ unsigned char key[KEY_SIZE];
+ } *pData = malloc(sizeof *pData);
+ if (pData == NULL) {
+ status = FwdLockConv_Status_OutOfMemory;
+ } else {
+ if (AES_set_encrypt_key(pSession->sessionKey, KEY_SIZE_IN_BITS,
+ &pData->sessionRoundKeys) != 0) {
+ status = FwdLockConv_Status_ProgramError;
+ } else {
+ // Encrypt the 16-byte value {0, 0, ..., 0} to produce the encryption key.
+ memset(pData->value, 0, KEY_SIZE);
+ AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+ if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS,
+ &pSession->encryptionRoundKeys) != 0) {
+ status = FwdLockConv_Status_ProgramError;
+ } else {
+ // Encrypt the 16-byte value {1, 0, ..., 0} to produce the signing key.
+ ++pData->value[0];
+ AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+ HMAC_CTX_init(&pSession->signingContext);
+ HMAC_Init_ex(&pSession->signingContext, pData->key, KEY_SIZE, EVP_sha1(), NULL);
+ status = FwdLockConv_Status_OK;
+ }
+ }
+ memset(pData, 0, sizeof pData); // Zero out key data.
+ free(pData);
+ }
+ return status;
+}
+
+/**
+ * Checks whether a given character is valid in a boundary. Note that the boundary may contain
+ * leading and internal spaces.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in a boundary.
+ */
+static int FwdLockConv_IsBoundaryChar(int ch) {
+ return isalnum(ch) || ch == '\'' ||
+ ch == '(' || ch == ')' || ch == '+' || ch == '_' || ch == ',' || ch == '-' ||
+ ch == '.' || ch == '/' || ch == ':' || ch == '=' || ch == '?' || ch == ' ';
+}
+
+/**
+ * Checks whether a given character should be considered whitespace, using a narrower definition
+ * than the standard-library isspace() function.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character should be considered whitespace.
+ */
+static int FwdLockConv_IsWhitespace(int ch) {
+ return ch == ' ' || ch == '\t';
+}
+
+/**
+ * Removes trailing spaces from the delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_RightTrimDelimiter(FwdLockConv_Session_t *pSession) {
+ while (pSession->delimiterLength > 4 &&
+ pSession->delimiter[pSession->delimiterLength - 1] == ' ') {
+ --pSession->delimiterLength;
+ }
+ if (pSession->delimiterLength > 4) {
+ return FwdLockConv_Status_OK;
+ }
+ return FwdLockConv_Status_SyntaxError;
+}
+
+/**
+ * Matches the open delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchOpenDelimiter(FwdLockConv_Session_t *pSession,
+ int ch) {
+ FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+ switch (pSession->scannerState) {
+ case FwdLockConv_ScannerState_WantsFirstDash:
+ if (ch == '-') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
+ } else if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsSecondDash:
+ if (ch == '-') {
+ // The delimiter starts with "\r\n--" (the open delimiter may omit the initial "\r\n").
+ // The rest is the user-defined boundary that should come next.
+ pSession->delimiter[0] = '\r';
+ pSession->delimiter[1] = '\n';
+ pSession->delimiter[2] = '-';
+ pSession->delimiter[3] = '-';
+ pSession->delimiterLength = 4;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsBoundary;
+ } else if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsCR:
+ if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsLF:
+ if (ch == '\n') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+ } else if (ch != '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsBoundary:
+ if (FwdLockConv_IsBoundaryChar(ch)) {
+ // The boundary may contain leading and internal spaces, so trailing spaces will also be
+ // matched here. These will be removed later.
+ if (pSession->delimiterLength < MAX_DELIMITER_LENGTH) {
+ pSession->delimiter[pSession->delimiterLength++] = ch;
+ } else if (ch != ' ') {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ } else if (ch == '\r') {
+ status = FwdLockConv_RightTrimDelimiter(pSession);
+ if (status == FwdLockConv_Status_OK) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsBoundaryEnd;
+ }
+ } else if (ch == '\t') {
+ status = FwdLockConv_RightTrimDelimiter(pSession);
+ if (status == FwdLockConv_Status_OK) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+ }
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsWhitespace:
+ if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsBoundaryEnd;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsBoundaryEnd:
+ if (ch == '\n') {
+ pSession->parserState = FwdLockConv_ParserState_WantsMimeHeaders;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameStart;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ default:
+ status = FwdLockConv_Status_ProgramError;
+ break;
+ }
+ return status;
+}
+
+/**
+ * Checks whether a given character is valid in a MIME header name.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in a MIME header name.
+ */
+static int FwdLockConv_IsMimeHeaderNameChar(int ch) {
+ return isgraph(ch) && ch != ':';
+}
+
+/**
+ * Checks whether a given character is valid in a MIME header value.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in a MIME header value.
+ */
+static int FwdLockConv_IsMimeHeaderValueChar(int ch) {
+ return isgraph(ch) && ch != ';';
+}
+
+/**
+ * Appends a character to the specified dynamically growing string.
+ *
+ * @param[in,out] pString A reference to a dynamically growing string.
+ * @param[in] ch The character to append.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_StringAppend(FwdLockConv_String_t *pString, int ch) {
+ if (pString->length == pString->maxLength) {
+ size_t newMaxLength = pString->maxLength + pString->lengthIncrement;
+ char *newPtr = realloc(pString->ptr, newMaxLength + 1);
+ if (newPtr == NULL) {
+ return FwdLockConv_Status_OutOfMemory;
+ }
+ pString->ptr = newPtr;
+ pString->maxLength = newMaxLength;
+ }
+ pString->ptr[pString->length++] = ch;
+ pString->ptr[pString->length] = '\0';
+ return FwdLockConv_Status_OK;
+}
+
+/**
+ * Attempts to recognize the MIME header name and changes the scanner state accordingly.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_RecognizeMimeHeaderName(FwdLockConv_Session_t *pSession) {
+ FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+ if (strncmp(pSession->mimeHeaderName.ptr, strContent, strlenContent) == 0) {
+ if (strcmp(pSession->mimeHeaderName.ptr + strlenContent, strType) == 0) {
+ if (pSession->contentType.ptr == NULL) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsContentTypeStart;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ } else if (strcmp(pSession->mimeHeaderName.ptr + strlenContent, strTransferEncoding) == 0) {
+ if (pSession->contentTransferEncoding ==
+ FwdLockConv_ContentTransferEncoding_Undefined) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingStart;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ } else {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ }
+ } else {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ }
+ return status;
+}
+
+/**
+ * Applies defaults to missing MIME header values.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_ApplyDefaults(FwdLockConv_Session_t *pSession) {
+ if (pSession->contentType.ptr == NULL) {
+ // Content type is missing: default to "text/plain".
+ pSession->contentType.ptr = malloc(sizeof strTextPlain);
+ if (pSession->contentType.ptr == NULL) {
+ return FwdLockConv_Status_OutOfMemory;
+ }
+ memcpy(pSession->contentType.ptr, strTextPlain, sizeof strTextPlain);
+ pSession->contentType.length = strlenTextPlain;
+ pSession->contentType.maxLength = strlenTextPlain;
+ }
+ if (pSession->contentTransferEncoding == FwdLockConv_ContentTransferEncoding_Undefined) {
+ // Content transfer encoding is missing: default to binary.
+ pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
+ }
+ return FwdLockConv_Status_OK;
+}
+
+/**
+ * Verifies that the content type is supported.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_VerifyContentType(FwdLockConv_Session_t *pSession) {
+ FwdLockConv_Status_t status;
+ if (pSession->contentType.ptr == NULL) {
+ status = FwdLockConv_Status_ProgramError;
+ } else if (strcmp(pSession->contentType.ptr, strApplicationVndOmaDrmRightsXml) == 0 ||
+ strcmp(pSession->contentType.ptr, strApplicationVndOmaDrmContent) == 0) {
+ status = FwdLockConv_Status_UnsupportedFileFormat;
+ } else {
+ status = FwdLockConv_Status_OK;
+ }
+ return status;
+}
+
+/**
+ * Writes the header of the output file.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_WriteHeader(FwdLockConv_Session_t *pSession,
+ FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status;
+ if (pSession->contentType.length > UCHAR_MAX) {
+ status = FwdLockConv_Status_SyntaxError;
+ } else {
+ pSession->outputBufferSize = OUTPUT_BUFFER_SIZE_INCREMENT;
+ pOutput->fromConvertData.pBuffer = malloc(pSession->outputBufferSize);
+ if (pOutput->fromConvertData.pBuffer == NULL) {
+ status = FwdLockConv_Status_OutOfMemory;
+ } else {
+ size_t encryptedSessionKeyPos = TOP_HEADER_SIZE + pSession->contentType.length;
+ size_t dataSignaturePos = encryptedSessionKeyPos + pSession->encryptedSessionKeyLength;
+ size_t headerSignaturePos = dataSignaturePos + SHA1_HASH_SIZE;
+ pSession->dataOffset = headerSignaturePos + SHA1_HASH_SIZE;
+ memcpy(pSession->topHeader, topHeaderTemplate, sizeof topHeaderTemplate);
+ pSession->topHeader[CONTENT_TYPE_LENGTH_POS] =
+ (unsigned char)pSession->contentType.length;
+ memcpy(pOutput->fromConvertData.pBuffer, pSession->topHeader, TOP_HEADER_SIZE);
+ memcpy((char *)pOutput->fromConvertData.pBuffer + TOP_HEADER_SIZE,
+ pSession->contentType.ptr, pSession->contentType.length);
+ memcpy((char *)pOutput->fromConvertData.pBuffer + encryptedSessionKeyPos,
+ pSession->pEncryptedSessionKey, pSession->encryptedSessionKeyLength);
+
+ // Set the signatures to all zeros for now; they will have to be updated later.
+ memset((char *)pOutput->fromConvertData.pBuffer + dataSignaturePos, 0,
+ SHA1_HASH_SIZE);
+ memset((char *)pOutput->fromConvertData.pBuffer + headerSignaturePos, 0,
+ SHA1_HASH_SIZE);
+
+ pOutput->fromConvertData.numBytes = pSession->dataOffset;
+ status = FwdLockConv_Status_OK;
+ }
+ }
+ return status;
+}
+
+/**
+ * Matches the MIME headers.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchMimeHeaders(FwdLockConv_Session_t *pSession,
+ int ch,
+ FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+ switch (pSession->scannerState) {
+ case FwdLockConv_ScannerState_WantsMimeHeaderNameStart:
+ if (FwdLockConv_IsMimeHeaderNameChar(ch)) {
+ pSession->mimeHeaderName.length = 0;
+ status = FwdLockConv_StringAppend(&pSession->mimeHeaderName, tolower(ch));
+ if (status == FwdLockConv_Status_OK) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderName;
+ }
+ } else if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeadersEnd;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsMimeHeaderName:
+ if (FwdLockConv_IsMimeHeaderNameChar(ch)) {
+ status = FwdLockConv_StringAppend(&pSession->mimeHeaderName, tolower(ch));
+ } else if (ch == ':') {
+ status = FwdLockConv_RecognizeMimeHeaderName(pSession);
+ } else if (FwdLockConv_IsWhitespace(ch)) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameEnd;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsMimeHeaderNameEnd:
+ if (ch == ':') {
+ status = FwdLockConv_RecognizeMimeHeaderName(pSession);
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsContentTypeStart:
+ if (FwdLockConv_IsMimeHeaderValueChar(ch)) {
+ status = FwdLockConv_StringAppend(&pSession->contentType, tolower(ch));
+ if (status == FwdLockConv_Status_OK) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsContentType;
+ }
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsContentType:
+ if (FwdLockConv_IsMimeHeaderValueChar(ch)) {
+ status = FwdLockConv_StringAppend(&pSession->contentType, tolower(ch));
+ } else if (ch == ';') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ } else if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (FwdLockConv_IsWhitespace(ch)) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderValueEnd;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsContentTransferEncodingStart:
+ if (ch == 'b' || ch == 'B') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_A_OR_I;
+ } else if (ch == '7' || ch == '8') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_B;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_A_OR_I:
+ if (ch == 'i' || ch == 'I') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_N;
+ } else if (ch == 'a' || ch == 'A') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_S;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_N:
+ if (ch == 'n' || ch == 'N') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_A;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_A:
+ if (ch == 'a' || ch == 'A') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_R;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_R:
+ if (ch == 'r' || ch == 'R') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_Y;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_Y:
+ if (ch == 'y' || ch == 'Y') {
+ pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_S:
+ if (ch == 's' || ch == 'S') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_E;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_E:
+ if (ch == 'e' || ch == 'E') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_6;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_6:
+ if (ch == '6') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_4;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_4:
+ if (ch == '4') {
+ pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Base64;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_B:
+ if (ch == 'b' || ch == 'B') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_I;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_I:
+ if (ch == 'i' || ch == 'I') {
+ pSession->scannerState = FwdLockConv_ScannerState_Wants_T;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_Wants_T:
+ if (ch == 't' || ch == 'T') {
+ pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsContentTransferEncodingEnd:
+ if (ch == ';') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ } else if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (FwdLockConv_IsWhitespace(ch)) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderValueEnd;
+ } else {
+ status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsMimeHeaderValueEnd:
+ if (ch == ';') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+ } else if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsCR:
+ if (ch == '\r') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsLF:
+ if (ch == '\n') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameStart;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsMimeHeadersEnd:
+ if (ch == '\n') {
+ status = FwdLockConv_ApplyDefaults(pSession);
+ if (status == FwdLockConv_Status_OK) {
+ status = FwdLockConv_VerifyContentType(pSession);
+ }
+ if (status == FwdLockConv_Status_OK) {
+ status = FwdLockConv_WriteHeader(pSession, pOutput);
+ }
+ if (status == FwdLockConv_Status_OK) {
+ if (pSession->contentTransferEncoding ==
+ FwdLockConv_ContentTransferEncoding_Binary) {
+ pSession->parserState = FwdLockConv_ParserState_WantsBinaryEncodedData;
+ } else {
+ pSession->parserState = FwdLockConv_ParserState_WantsBase64EncodedData;
+ }
+ pSession->scannerState = FwdLockConv_ScannerState_WantsByte1;
+ }
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ default:
+ status = FwdLockConv_Status_ProgramError;
+ break;
+ }
+ return status;
+}
+
+/**
+ * Increments the counter, treated as a 16-byte little-endian number, by one.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ */
+static void FwdLockConv_IncrementCounter(FwdLockConv_Session_t *pSession) {
+ size_t i = 0;
+ while ((++pSession->counter[i] == 0) && (++i < AES_BLOCK_SIZE))
+ ;
+}
+
+/**
+ * Encrypts the given character and writes it to the output buffer.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch The character to encrypt and write.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_WriteEncryptedChar(FwdLockConv_Session_t *pSession,
+ unsigned char ch,
+ FwdLockConv_Output_t *pOutput) {
+ if (pOutput->fromConvertData.numBytes == pSession->outputBufferSize) {
+ void *pBuffer;
+ pSession->outputBufferSize += OUTPUT_BUFFER_SIZE_INCREMENT;
+ pBuffer = realloc(pOutput->fromConvertData.pBuffer, pSession->outputBufferSize);
+ if (pBuffer == NULL) {
+ return FwdLockConv_Status_OutOfMemory;
+ }
+ pOutput->fromConvertData.pBuffer = pBuffer;
+ }
+ if (++pSession->keyStreamIndex == AES_BLOCK_SIZE) {
+ FwdLockConv_IncrementCounter(pSession);
+ pSession->keyStreamIndex = 0;
+ }
+ if (pSession->keyStreamIndex == 0) {
+ AES_encrypt(pSession->counter, pSession->keyStream, &pSession->encryptionRoundKeys);
+ }
+ ch ^= pSession->keyStream[pSession->keyStreamIndex];
+ ((unsigned char *)pOutput->fromConvertData.pBuffer)[pOutput->fromConvertData.numBytes++] = ch;
+ ++pSession->numDataBytes;
+ return FwdLockConv_Status_OK;
+}
+
+/**
+ * Matches binary-encoded content data and encrypts it, while looking out for the close delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchBinaryEncodedData(FwdLockConv_Session_t *pSession,
+ int ch,
+ FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+ switch (pSession->scannerState) {
+ case FwdLockConv_ScannerState_WantsByte1:
+ if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
+ // The partial match of the delimiter turned out to be spurious. Flush the matched bytes
+ // to the output buffer and start over.
+ size_t i;
+ for (i = 0; i < pSession->delimiterMatchPos; ++i) {
+ status = FwdLockConv_WriteEncryptedChar(pSession, pSession->delimiter[i], pOutput);
+ if (status != FwdLockConv_Status_OK) {
+ return status;
+ }
+ }
+ pSession->delimiterMatchPos = 0;
+ }
+ if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
+ // The current character isn't part of the delimiter. Write it to the output buffer.
+ status = FwdLockConv_WriteEncryptedChar(pSession, ch, pOutput);
+ } else if (++pSession->delimiterMatchPos == pSession->delimiterLength) {
+ // The entire delimiter has been matched. The only valid characters now are the "--"
+ // that complete the close delimiter (no more message parts are expected).
+ pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsFirstDash:
+ if (ch == '-') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsSecondDash:
+ if (ch == '-') {
+ pSession->parserState = FwdLockConv_ParserState_Done;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ default:
+ status = FwdLockConv_Status_ProgramError;
+ break;
+ }
+ return status;
+}
+
+/**
+ * Checks whether a given character is valid in base64-encoded data.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in base64-encoded data.
+ */
+static int FwdLockConv_IsBase64Char(int ch) {
+ return 0 <= ch && ch <= 'z' && base64Values[ch] >= 0;
+}
+
+/**
+ * Matches base64-encoded content data and encrypts it, while looking out for the close delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchBase64EncodedData(FwdLockConv_Session_t *pSession,
+ int ch,
+ FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+ switch (pSession->scannerState) {
+ case FwdLockConv_ScannerState_WantsByte1:
+ case FwdLockConv_ScannerState_WantsByte1_AfterCRLF:
+ if (FwdLockConv_IsBase64Char(ch)) {
+ pSession->ch = base64Values[ch] << 2;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsByte2;
+ } else if (ch == '\r') {
+ pSession->savedScannerState = FwdLockConv_ScannerState_WantsByte1_AfterCRLF;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (ch == '-') {
+ if (pSession->scannerState == FwdLockConv_ScannerState_WantsByte1_AfterCRLF) {
+ pSession->delimiterMatchPos = 3;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsDelimiter;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsByte2:
+ if (FwdLockConv_IsBase64Char(ch)) {
+ pSession->ch |= base64Values[ch] >> 4;
+ status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
+ if (status == FwdLockConv_Status_OK) {
+ pSession->ch = base64Values[ch] << 4;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsByte3;
+ }
+ } else if (ch == '\r') {
+ pSession->savedScannerState = pSession->scannerState;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsByte3:
+ if (FwdLockConv_IsBase64Char(ch)) {
+ pSession->ch |= base64Values[ch] >> 2;
+ status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
+ if (status == FwdLockConv_Status_OK) {
+ pSession->ch = base64Values[ch] << 6;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsByte4;
+ }
+ } else if (ch == '\r') {
+ pSession->savedScannerState = pSession->scannerState;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (ch == '=') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsPadding;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsByte4:
+ if (FwdLockConv_IsBase64Char(ch)) {
+ pSession->ch |= base64Values[ch];
+ status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
+ if (status == FwdLockConv_Status_OK) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsByte1;
+ }
+ } else if (ch == '\r') {
+ pSession->savedScannerState = pSession->scannerState;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (ch == '=') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+ } else if (!FwdLockConv_IsWhitespace(ch)) {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsLF:
+ if (ch == '\n') {
+ pSession->scannerState = pSession->savedScannerState;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsPadding:
+ if (ch == '=') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsWhitespace:
+ case FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF:
+ if (ch == '\r') {
+ pSession->savedScannerState = FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+ } else if (ch == '-') {
+ if (pSession->scannerState == FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF) {
+ pSession->delimiterMatchPos = 3;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsDelimiter;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ } else if (FwdLockConv_IsWhitespace(ch)) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsDelimiter:
+ if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
+ status = FwdLockConv_Status_SyntaxError;
+ } else if (++pSession->delimiterMatchPos == pSession->delimiterLength) {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsFirstDash:
+ if (ch == '-') {
+ pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ case FwdLockConv_ScannerState_WantsSecondDash:
+ if (ch == '-') {
+ pSession->parserState = FwdLockConv_ParserState_Done;
+ } else {
+ status = FwdLockConv_Status_SyntaxError;
+ }
+ break;
+ default:
+ status = FwdLockConv_Status_ProgramError;
+ break;
+ }
+ return status;
+}
+
+/**
+ * Pushes a single character into the converter's state machine.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_PushChar(FwdLockConv_Session_t *pSession,
+ int ch,
+ FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status;
+ ++pSession->numCharsConsumed;
+ switch (pSession->parserState) {
+ case FwdLockConv_ParserState_WantsOpenDelimiter:
+ status = FwdLockConv_MatchOpenDelimiter(pSession, ch);
+ break;
+ case FwdLockConv_ParserState_WantsMimeHeaders:
+ status = FwdLockConv_MatchMimeHeaders(pSession, ch, pOutput);
+ break;
+ case FwdLockConv_ParserState_WantsBinaryEncodedData:
+ status = FwdLockConv_MatchBinaryEncodedData(pSession, ch, pOutput);
+ break;
+ case FwdLockConv_ParserState_WantsBase64EncodedData:
+ status = FwdLockConv_MatchBase64EncodedData(pSession, ch, pOutput);
+ break;
+ case FwdLockConv_ParserState_Done:
+ status = FwdLockConv_Status_OK;
+ break;
+ default:
+ status = FwdLockConv_Status_ProgramError;
+ break;
+ }
+ return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_OpenSession(int *pSessionId, FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status;
+ if (pSessionId == NULL || pOutput == NULL) {
+ status = FwdLockConv_Status_InvalidArgument;
+ } else {
+ *pSessionId = FwdLockConv_AcquireSession();
+ if (*pSessionId < 0) {
+ status = FwdLockConv_Status_TooManySessions;
+ } else {
+ FwdLockConv_Session_t *pSession = sessionPtrs[*pSessionId];
+ pSession->encryptedSessionKeyLength = FwdLockGlue_GetEncryptedKeyLength(KEY_SIZE);
+ if (pSession->encryptedSessionKeyLength < AES_BLOCK_SIZE) {
+ // The encrypted session key is used as the CTR-mode nonce, so it must be at least
+ // the size of a single AES block.
+ status = FwdLockConv_Status_ProgramError;
+ } else {
+ pSession->pEncryptedSessionKey = malloc(pSession->encryptedSessionKeyLength);
+ if (pSession->pEncryptedSessionKey == NULL) {
+ status = FwdLockConv_Status_OutOfMemory;
+ } else {
+ if (!FwdLockGlue_GetRandomNumber(pSession->sessionKey, KEY_SIZE)) {
+ status = FwdLockConv_Status_RandomNumberGenerationFailed;
+ } else if (!FwdLockGlue_EncryptKey(pSession->sessionKey, KEY_SIZE,
+ pSession->pEncryptedSessionKey,
+ pSession->encryptedSessionKeyLength)) {
+ status = FwdLockConv_Status_KeyEncryptionFailed;
+ } else {
+ status = FwdLockConv_DeriveKeys(pSession);
+ }
+ if (status == FwdLockConv_Status_OK) {
+ memset(pSession->sessionKey, 0, KEY_SIZE); // Zero out key data.
+ memcpy(pSession->counter, pSession->pEncryptedSessionKey, AES_BLOCK_SIZE);
+ pSession->parserState = FwdLockConv_ParserState_WantsOpenDelimiter;
+ pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+ pSession->numCharsConsumed = 0;
+ pSession->delimiterMatchPos = 0;
+ pSession->mimeHeaderName = nullString;
+ pSession->contentType = nullString;
+ pSession->contentTransferEncoding =
+ FwdLockConv_ContentTransferEncoding_Undefined;
+ pSession->keyStreamIndex = -1;
+ pOutput->fromConvertData.pBuffer = NULL;
+ pOutput->fromConvertData.errorPos = INVALID_OFFSET;
+ } else {
+ free(pSession->pEncryptedSessionKey);
+ }
+ }
+ }
+ if (status != FwdLockConv_Status_OK) {
+ FwdLockConv_ReleaseSession(*pSessionId);
+ *pSessionId = -1;
+ }
+ }
+ }
+ return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_ConvertData(int sessionId,
+ const void *pBuffer,
+ size_t numBytes,
+ FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status;
+ if (!FwdLockConv_IsValidSession(sessionId) || pBuffer == NULL || pOutput == NULL) {
+ status = FwdLockConv_Status_InvalidArgument;
+ } else {
+ size_t i;
+ FwdLockConv_Session_t *pSession = sessionPtrs[sessionId];
+ pSession->dataOffset = 0;
+ pSession->numDataBytes = 0;
+ pOutput->fromConvertData.numBytes = 0;
+ status = FwdLockConv_Status_OK;
+
+ for (i = 0; i < numBytes; ++i) {
+ status = FwdLockConv_PushChar(pSession, ((char *)pBuffer)[i], pOutput);
+ if (status != FwdLockConv_Status_OK) {
+ break;
+ }
+ }
+ if (status == FwdLockConv_Status_OK) {
+ // Update the data signature.
+ HMAC_Update(&pSession->signingContext,
+ &((unsigned char *)pOutput->fromConvertData.pBuffer)[pSession->dataOffset],
+ pSession->numDataBytes);
+ } else if (status == FwdLockConv_Status_SyntaxError) {
+ pOutput->fromConvertData.errorPos = pSession->numCharsConsumed;
+ }
+ }
+ return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_CloseSession(int sessionId, FwdLockConv_Output_t *pOutput) {
+ FwdLockConv_Status_t status;
+ if (!FwdLockConv_IsValidSession(sessionId) || pOutput == NULL) {
+ status = FwdLockConv_Status_InvalidArgument;
+ } else {
+ FwdLockConv_Session_t *pSession = sessionPtrs[sessionId];
+ free(pOutput->fromConvertData.pBuffer);
+ if (pSession->parserState != FwdLockConv_ParserState_Done) {
+ pOutput->fromCloseSession.errorPos = pSession->numCharsConsumed;
+ status = FwdLockConv_Status_SyntaxError;
+ } else {
+ // Finalize the data signature.
+ size_t signatureSize;
+ HMAC_Final(&pSession->signingContext, pOutput->fromCloseSession.signatures,
+ &signatureSize);
+ if (signatureSize != SHA1_HASH_SIZE) {
+ status = FwdLockConv_Status_ProgramError;
+ } else {
+ // Calculate the header signature, which is a signature of the rest of the header
+ // including the data signature.
+ HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
+ HMAC_Update(&pSession->signingContext, pSession->topHeader, TOP_HEADER_SIZE);
+ HMAC_Update(&pSession->signingContext, (unsigned char *)pSession->contentType.ptr,
+ pSession->contentType.length);
+ HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey,
+ pSession->encryptedSessionKeyLength);
+ HMAC_Update(&pSession->signingContext, pOutput->fromCloseSession.signatures,
+ signatureSize);
+ HMAC_Final(&pSession->signingContext, &pOutput->fromCloseSession.
+ signatures[signatureSize], &signatureSize);
+ if (signatureSize != SHA1_HASH_SIZE) {
+ status = FwdLockConv_Status_ProgramError;
+ } else {
+ pOutput->fromCloseSession.fileOffset = TOP_HEADER_SIZE +
+ pSession->contentType.length + pSession->encryptedSessionKeyLength;
+ status = FwdLockConv_Status_OK;
+ }
+ }
+ pOutput->fromCloseSession.errorPos = INVALID_OFFSET;
+ }
+ free(pSession->mimeHeaderName.ptr);
+ free(pSession->contentType.ptr);
+ free(pSession->pEncryptedSessionKey);
+ HMAC_CTX_cleanup(&pSession->signingContext);
+ FwdLockConv_ReleaseSession(sessionId);
+ }
+ return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_ConvertOpenFile(int inputFileDesc,
+ FwdLockConv_ReadFunc_t *fpReadFunc,
+ int outputFileDesc,
+ FwdLockConv_WriteFunc_t *fpWriteFunc,
+ FwdLockConv_LSeekFunc_t *fpLSeekFunc,
+ off64_t *pErrorPos) {
+ FwdLockConv_Status_t status;
+ if (pErrorPos != NULL) {
+ *pErrorPos = INVALID_OFFSET;
+ }
+ if (fpReadFunc == NULL || fpWriteFunc == NULL || fpLSeekFunc == NULL || inputFileDesc < 0 ||
+ outputFileDesc < 0) {
+ status = FwdLockConv_Status_InvalidArgument;
+ } else {
+ char *pReadBuffer = malloc(READ_BUFFER_SIZE);
+ if (pReadBuffer == NULL) {
+ status = FwdLockConv_Status_OutOfMemory;
+ } else {
+ int sessionId;
+ FwdLockConv_Output_t output;
+ status = FwdLockConv_OpenSession(&sessionId, &output);
+ if (status == FwdLockConv_Status_OK) {
+ ssize_t numBytesRead;
+ FwdLockConv_Status_t closeStatus;
+ while ((numBytesRead =
+ fpReadFunc(inputFileDesc, pReadBuffer, READ_BUFFER_SIZE)) > 0) {
+ status = FwdLockConv_ConvertData(sessionId, pReadBuffer, (size_t)numBytesRead,
+ &output);
+ if (status == FwdLockConv_Status_OK) {
+ if (output.fromConvertData.pBuffer != NULL &&
+ output.fromConvertData.numBytes > 0) {
+ ssize_t numBytesWritten = fpWriteFunc(outputFileDesc,
+ output.fromConvertData.pBuffer,
+ output.fromConvertData.numBytes);
+ if (numBytesWritten != (ssize_t)output.fromConvertData.numBytes) {
+ status = FwdLockConv_Status_FileWriteError;
+ break;
+ }
+ }
+ } else {
+ if (status == FwdLockConv_Status_SyntaxError && pErrorPos != NULL) {
+ *pErrorPos = output.fromConvertData.errorPos;
+ }
+ break;
+ }
+ } // end while
+ if (numBytesRead < 0) {
+ status = FwdLockConv_Status_FileReadError;
+ }
+ closeStatus = FwdLockConv_CloseSession(sessionId, &output);
+ if (status == FwdLockConv_Status_OK) {
+ if (closeStatus != FwdLockConv_Status_OK) {
+ if (closeStatus == FwdLockConv_Status_SyntaxError && pErrorPos != NULL) {
+ *pErrorPos = output.fromCloseSession.errorPos;
+ }
+ status = closeStatus;
+ } else if (fpLSeekFunc(outputFileDesc, output.fromCloseSession.fileOffset,
+ SEEK_SET) < 0) {
+ status = FwdLockConv_Status_FileSeekError;
+ } else if (fpWriteFunc(outputFileDesc, output.fromCloseSession.signatures,
+ FWD_LOCK_SIGNATURES_SIZE) != FWD_LOCK_SIGNATURES_SIZE) {
+ status = FwdLockConv_Status_FileWriteError;
+ }
+ }
+ }
+ free(pReadBuffer);
+ }
+ }
+ return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_ConvertFile(const char *pInputFilename,
+ const char *pOutputFilename,
+ off64_t *pErrorPos) {
+ FwdLockConv_Status_t status;
+ if (pErrorPos != NULL) {
+ *pErrorPos = INVALID_OFFSET;
+ }
+ if (pInputFilename == NULL || pOutputFilename == NULL) {
+ status = FwdLockConv_Status_InvalidArgument;
+ } else {
+ int inputFileDesc = open(pInputFilename, O_RDONLY);
+ if (inputFileDesc < 0) {
+ status = FwdLockConv_Status_FileNotFound;
+ } else {
+ int outputFileDesc = open(pOutputFilename, O_CREAT | O_TRUNC | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (outputFileDesc < 0) {
+ status = FwdLockConv_Status_FileCreationFailed;
+ } else {
+ status = FwdLockConv_ConvertOpenFile(inputFileDesc, read, outputFileDesc, write,
+ lseek64, pErrorPos);
+ if (close(outputFileDesc) == 0 && status != FwdLockConv_Status_OK) {
+ remove(pOutputFilename);
+ }
+ }
+ (void)close(inputFileDesc);
+ }
+ }
+ return status;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h
new file mode 100644
index 0000000..e20c0c3
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FWDLOCKCONV_H__
+#define __FWDLOCKCONV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/**
+ * The size of the data and header signatures combined. The signatures are adjacent to each other in
+ * the produced output file.
+ */
+#define FWD_LOCK_SIGNATURES_SIZE (2 * 20)
+
+/**
+ * Data type for the output from FwdLockConv_ConvertData.
+ */
+typedef struct FwdLockConv_ConvertData_Output {
+ /// The converted data.
+ void *pBuffer;
+
+ /// The size of the converted data.
+ size_t numBytes;
+
+ /// The file position where the error occurred, in the case of a syntax error.
+ off64_t errorPos;
+} FwdLockConv_ConvertData_Output_t;
+
+/**
+ * Data type for the output from FwdLockConv_CloseSession.
+ */
+typedef struct FwdLockConv_CloseSession_Output {
+ /// The final set of signatures.
+ unsigned char signatures[FWD_LOCK_SIGNATURES_SIZE];
+
+ /// The offset in the produced output file where the signatures are located.
+ off64_t fileOffset;
+
+ /// The file position where the error occurred, in the case of a syntax error.
+ off64_t errorPos;
+} FwdLockConv_CloseSession_Output_t;
+
+/**
+ * Data type for the output from the conversion process.
+ */
+typedef union FwdLockConv_Output {
+ FwdLockConv_ConvertData_Output_t fromConvertData;
+ FwdLockConv_CloseSession_Output_t fromCloseSession;
+} FwdLockConv_Output_t;
+
+/**
+ * Data type for the Posix-style read function used by the converter in pull mode.
+ *
+ * @param[in] fileDesc The file descriptor of a file opened for reading.
+ * @param[out] pBuffer A reference to the buffer that should receive the read data.
+ * @param[in] numBytes The number of bytes to read.
+ *
+ * @return The number of bytes read.
+ * @retval -1 Failure.
+ */
+typedef ssize_t FwdLockConv_ReadFunc_t(int fileDesc, void *pBuffer, size_t numBytes);
+
+/**
+ * Data type for the Posix-style write function used by the converter in pull mode.
+ *
+ * @param[in] fileDesc The file descriptor of a file opened for writing.
+ * @param[in] pBuffer A reference to the buffer containing the data to be written.
+ * @param[in] numBytes The number of bytes to write.
+ *
+ * @return The number of bytes written.
+ * @retval -1 Failure.
+ */
+typedef ssize_t FwdLockConv_WriteFunc_t(int fileDesc, const void *pBuffer, size_t numBytes);
+
+/**
+ * Data type for the Posix-style lseek function used by the converter in pull mode.
+ *
+ * @param[in] fileDesc The file descriptor of a file opened for writing.
+ * @param[in] offset The offset with which to update the file position.
+ * @param[in] whence One of SEEK_SET, SEEK_CUR, and SEEK_END.
+ *
+ * @return The new file position.
+ * @retval ((off64_t)-1) Failure.
+ */
+typedef off64_t FwdLockConv_LSeekFunc_t(int fileDesc, off64_t offset, int whence);
+
+/**
+ * The status codes returned by the converter functions.
+ */
+typedef enum FwdLockConv_Status {
+ /// The operation was successful.
+ FwdLockConv_Status_OK = 0,
+
+ /// An actual argument to the function is invalid (a program error on the caller's part).
+ FwdLockConv_Status_InvalidArgument = 1,
+
+ /// There is not enough free dynamic memory to complete the operation.
+ FwdLockConv_Status_OutOfMemory = 2,
+
+ /// An error occurred while opening the input file.
+ FwdLockConv_Status_FileNotFound = 3,
+
+ /// An error occurred while creating the output file.
+ FwdLockConv_Status_FileCreationFailed = 4,
+
+ /// An error occurred while reading from the input file.
+ FwdLockConv_Status_FileReadError = 5,
+
+ /// An error occurred while writing to the output file.
+ FwdLockConv_Status_FileWriteError = 6,
+
+ /// An error occurred while seeking to a new file position within the output file.
+ FwdLockConv_Status_FileSeekError = 7,
+
+ /// The input file is not a syntactically correct OMA DRM v1 Forward Lock file.
+ FwdLockConv_Status_SyntaxError = 8,
+
+ /// Support for this DRM file format has been disabled in the current product configuration.
+ FwdLockConv_Status_UnsupportedFileFormat = 9,
+
+ /// The content transfer encoding is not one of "binary", "base64", "7bit", or "8bit"
+ /// (case-insensitive).
+ FwdLockConv_Status_UnsupportedContentTransferEncoding = 10,
+
+ /// The generation of a random number failed.
+ FwdLockConv_Status_RandomNumberGenerationFailed = 11,
+
+ /// Key encryption failed.
+ FwdLockConv_Status_KeyEncryptionFailed = 12,
+
+ /// The calculation of a keyed hash for integrity protection failed.
+ FwdLockConv_Status_IntegrityProtectionFailed = 13,
+
+ /// There are too many ongoing sessions for another one to be opened.
+ FwdLockConv_Status_TooManySessions = 14,
+
+ /// An unexpected error occurred.
+ FwdLockConv_Status_ProgramError = 15
+} FwdLockConv_Status_t;
+
+/**
+ * Opens a session for converting an OMA DRM v1 Forward Lock file to the internal Forward Lock file
+ * format.
+ *
+ * @param[out] pSessionId The session ID.
+ * @param[out] pOutput The output from the conversion process (initialized).
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_TooManySessions
+ */
+FwdLockConv_Status_t FwdLockConv_OpenSession(int *pSessionId, FwdLockConv_Output_t *pOutput);
+
+/**
+ * Supplies the converter with data to convert. The caller is expected to write the converted data
+ * to file. Can be called an arbitrary number of times.
+ *
+ * @param[in] sessionId The session ID.
+ * @param[in] pBuffer A reference to a buffer containing the data to convert.
+ * @param[in] numBytes The number of bytes to convert.
+ * @param[in,out] pOutput The output from the conversion process (allocated/reallocated).
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_SyntaxError
+ * @retval FwdLockConv_Status_UnsupportedFileFormat
+ * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding
+ * @retval FwdLockConv_Status_RandomNumberGenerationFailed
+ * @retval FwdLockConv_Status_KeyEncryptionFailed
+ * @retval FwdLockConv_Status_DataEncryptionFailed
+ */
+FwdLockConv_Status_t FwdLockConv_ConvertData(int sessionId,
+ const void *pBuffer,
+ size_t numBytes,
+ FwdLockConv_Output_t *pOutput);
+
+/**
+ * Closes a session for converting an OMA DRM v1 Forward Lock file to the internal Forward Lock
+ * file format. The caller must update the produced output file at the indicated file offset with
+ * the final set of signatures.
+ *
+ * @param[in] sessionId The session ID.
+ * @param[in,out] pOutput The output from the conversion process (deallocated and overwritten).
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_IntegrityProtectionFailed
+ */
+FwdLockConv_Status_t FwdLockConv_CloseSession(int sessionId, FwdLockConv_Output_t *pOutput);
+
+/**
+ * Converts an open OMA DRM v1 Forward Lock file to the internal Forward Lock file format in pull
+ * mode.
+ *
+ * @param[in] inputFileDesc The file descriptor of the open input file.
+ * @param[in] fpReadFunc A reference to a read function that can operate on the open input file.
+ * @param[in] outputFileDesc The file descriptor of the open output file.
+ * @param[in] fpWriteFunc A reference to a write function that can operate on the open output file.
+ * @param[in] fpLSeekFunc A reference to an lseek function that can operate on the open output file.
+ * @param[out] pErrorPos
+ * The file position where the error occurred, in the case of a syntax error. May be NULL.
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_FileReadError
+ * @retval FwdLockConv_Status_FileWriteError
+ * @retval FwdLockConv_Status_FileSeekError
+ * @retval FwdLockConv_Status_SyntaxError
+ * @retval FwdLockConv_Status_UnsupportedFileFormat
+ * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding
+ * @retval FwdLockConv_Status_RandomNumberGenerationFailed
+ * @retval FwdLockConv_Status_KeyEncryptionFailed
+ * @retval FwdLockConv_Status_DataEncryptionFailed
+ * @retval FwdLockConv_Status_IntegrityProtectionFailed
+ * @retval FwdLockConv_Status_TooManySessions
+ */
+FwdLockConv_Status_t FwdLockConv_ConvertOpenFile(int inputFileDesc,
+ FwdLockConv_ReadFunc_t *fpReadFunc,
+ int outputFileDesc,
+ FwdLockConv_WriteFunc_t *fpWriteFunc,
+ FwdLockConv_LSeekFunc_t *fpLSeekFunc,
+ off64_t *pErrorPos);
+
+/**
+ * Converts an OMA DRM v1 Forward Lock file to the internal Forward Lock file format in pull mode.
+ *
+ * @param[in] pInputFilename A reference to the input filename.
+ * @param[in] pOutputFilename A reference to the output filename.
+ * @param[out] pErrorPos
+ * The file position where the error occurred, in the case of a syntax error. May be NULL.
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_FileNotFound
+ * @retval FwdLockConv_Status_FileCreationFailed
+ * @retval FwdLockConv_Status_FileReadError
+ * @retval FwdLockConv_Status_FileWriteError
+ * @retval FwdLockConv_Status_FileSeekError
+ * @retval FwdLockConv_Status_SyntaxError
+ * @retval FwdLockConv_Status_UnsupportedFileFormat
+ * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding
+ * @retval FwdLockConv_Status_RandomNumberGenerationFailed
+ * @retval FwdLockConv_Status_KeyEncryptionFailed
+ * @retval FwdLockConv_Status_DataEncryptionFailed
+ * @retval FwdLockConv_Status_IntegrityProtectionFailed
+ * @retval FwdLockConv_Status_TooManySessions
+ */
+FwdLockConv_Status_t FwdLockConv_ConvertFile(const char *pInputFilename,
+ const char *pOutputFilename,
+ off64_t *pErrorPos);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __FWDLOCKCONV_H__
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk
new file mode 100644
index 0000000..b625edf
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ FwdLockFile.c
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/drm/libdrmframework/plugins/forward-lock/internal-format/common \
+ external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := libcrypto
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_MODULE := libfwdlock-decoder
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
new file mode 100644
index 0000000..98284e72
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <openssl/aes.h>
+#include <openssl/hmac.h>
+
+#include "FwdLockFile.h"
+#include "FwdLockGlue.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define INVALID_OFFSET ((off64_t)-1)
+
+#define INVALID_BLOCK_INDEX ((uint64_t)-1)
+
+#define MAX_NUM_SESSIONS 128
+
+#define KEY_SIZE AES_BLOCK_SIZE
+#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
+
+#define SHA1_HASH_SIZE 20
+#define SHA1_BLOCK_SIZE 64
+
+#define FWD_LOCK_VERSION 0
+#define FWD_LOCK_SUBFORMAT 0
+#define USAGE_RESTRICTION_FLAGS 0
+#define CONTENT_TYPE_LENGTH_POS 7
+#define TOP_HEADER_SIZE 8
+
+#define SIG_CALC_BUFFER_SIZE (16 * SHA1_BLOCK_SIZE)
+
+/**
+ * Data type for the per-file state information needed by the decoder.
+ */
+typedef struct FwdLockFile_Session {
+ int fileDesc;
+ unsigned char topHeader[TOP_HEADER_SIZE];
+ char *pContentType;
+ size_t contentTypeLength;
+ void *pEncryptedSessionKey;
+ size_t encryptedSessionKeyLength;
+ unsigned char dataSignature[SHA1_HASH_SIZE];
+ unsigned char headerSignature[SHA1_HASH_SIZE];
+ off64_t dataOffset;
+ off64_t filePos;
+ AES_KEY encryptionRoundKeys;
+ HMAC_CTX signingContext;
+ unsigned char keyStream[AES_BLOCK_SIZE];
+ uint64_t blockIndex;
+} FwdLockFile_Session_t;
+
+static FwdLockFile_Session_t *sessionPtrs[MAX_NUM_SESSIONS] = { NULL };
+
+static pthread_mutex_t sessionAcquisitionMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static const unsigned char topHeaderTemplate[] =
+ { 'F', 'W', 'L', 'K', FWD_LOCK_VERSION, FWD_LOCK_SUBFORMAT, USAGE_RESTRICTION_FLAGS };
+
+/**
+ * Acquires an unused file session for the given file descriptor.
+ *
+ * @param[in] fileDesc A file descriptor.
+ *
+ * @return A session ID.
+ */
+static int FwdLockFile_AcquireSession(int fileDesc) {
+ int sessionId = -1;
+ if (fileDesc < 0) {
+ errno = EBADF;
+ } else {
+ int i;
+ pthread_mutex_lock(&sessionAcquisitionMutex);
+ for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
+ int candidateSessionId = (fileDesc + i) % MAX_NUM_SESSIONS;
+ if (sessionPtrs[candidateSessionId] == NULL) {
+ sessionPtrs[candidateSessionId] = malloc(sizeof **sessionPtrs);
+ if (sessionPtrs[candidateSessionId] != NULL) {
+ sessionPtrs[candidateSessionId]->fileDesc = fileDesc;
+ sessionPtrs[candidateSessionId]->pContentType = NULL;
+ sessionPtrs[candidateSessionId]->pEncryptedSessionKey = NULL;
+ sessionId = candidateSessionId;
+ }
+ break;
+ }
+ }
+ pthread_mutex_unlock(&sessionAcquisitionMutex);
+ if (i == MAX_NUM_SESSIONS) {
+ errno = ENFILE;
+ }
+ }
+ return sessionId;
+}
+
+/**
+ * Finds the file session associated to the given file descriptor.
+ *
+ * @param[in] fileDesc A file descriptor.
+ *
+ * @return A session ID.
+ */
+static int FwdLockFile_FindSession(int fileDesc) {
+ int sessionId = -1;
+ if (fileDesc < 0) {
+ errno = EBADF;
+ } else {
+ int i;
+ pthread_mutex_lock(&sessionAcquisitionMutex);
+ for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
+ int candidateSessionId = (fileDesc + i) % MAX_NUM_SESSIONS;
+ if (sessionPtrs[candidateSessionId] != NULL &&
+ sessionPtrs[candidateSessionId]->fileDesc == fileDesc) {
+ sessionId = candidateSessionId;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&sessionAcquisitionMutex);
+ if (i == MAX_NUM_SESSIONS) {
+ errno = EBADF;
+ }
+ }
+ return sessionId;
+}
+
+/**
+ * Releases a file session.
+ *
+ * @param[in] sessionID A session ID.
+ */
+static void FwdLockFile_ReleaseSession(int sessionId) {
+ pthread_mutex_lock(&sessionAcquisitionMutex);
+ assert(0 <= sessionId && sessionId < MAX_NUM_SESSIONS && sessionPtrs[sessionId] != NULL);
+ free(sessionPtrs[sessionId]->pContentType);
+ free(sessionPtrs[sessionId]->pEncryptedSessionKey);
+ memset(sessionPtrs[sessionId], 0, sizeof *sessionPtrs[sessionId]); // Zero out key data.
+ free(sessionPtrs[sessionId]);
+ sessionPtrs[sessionId] = NULL;
+ pthread_mutex_unlock(&sessionAcquisitionMutex);
+}
+
+/**
+ * Derives keys for encryption and signing from the encrypted session key.
+ *
+ * @param[in,out] pSession A reference to a file session.
+ *
+ * @return A Boolean value indicating whether key derivation was successful.
+ */
+static int FwdLockFile_DeriveKeys(FwdLockFile_Session_t * pSession) {
+ int result;
+ struct FwdLockFile_DeriveKeys_Data {
+ AES_KEY sessionRoundKeys;
+ unsigned char value[KEY_SIZE];
+ unsigned char key[KEY_SIZE];
+ } *pData = malloc(sizeof *pData);
+ if (pData == NULL) {
+ result = FALSE;
+ } else {
+ result = FwdLockGlue_DecryptKey(pSession->pEncryptedSessionKey,
+ pSession->encryptedSessionKeyLength, pData->key, KEY_SIZE);
+ if (result) {
+ if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS, &pData->sessionRoundKeys) != 0) {
+ result = FALSE;
+ } else {
+ // Encrypt the 16-byte value {0, 0, ..., 0} to produce the encryption key.
+ memset(pData->value, 0, KEY_SIZE);
+ AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+ if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS,
+ &pSession->encryptionRoundKeys) != 0) {
+ result = FALSE;
+ } else {
+ // Encrypt the 16-byte value {1, 0, ..., 0} to produce the signing key.
+ ++pData->value[0];
+ AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+ HMAC_CTX_init(&pSession->signingContext);
+ HMAC_Init_ex(&pSession->signingContext, pData->key, KEY_SIZE, EVP_sha1(), NULL);
+ }
+ }
+ }
+ if (!result) {
+ errno = ENOSYS;
+ }
+ memset(pData, 0, sizeof pData); // Zero out key data.
+ free(pData);
+ }
+ return result;
+}
+
+/**
+ * Calculates the counter, treated as a 16-byte little-endian number, used to generate the keystream
+ * for the given block.
+ *
+ * @param[in] pNonce A reference to the nonce.
+ * @param[in] blockIndex The index number of the block.
+ * @param[out] pCounter A reference to the counter.
+ */
+static void FwdLockFile_CalculateCounter(const unsigned char *pNonce,
+ uint64_t blockIndex,
+ unsigned char *pCounter) {
+ unsigned char carry = 0;
+ size_t i = 0;
+ for (; i < sizeof blockIndex; ++i) {
+ unsigned char part = pNonce[i] + (unsigned char)(blockIndex >> (i * CHAR_BIT));
+ pCounter[i] = part + carry;
+ carry = (part < pNonce[i] || pCounter[i] < part) ? 1 : 0;
+ }
+ for (; i < AES_BLOCK_SIZE; ++i) {
+ pCounter[i] = pNonce[i] + carry;
+ carry = (pCounter[i] < pNonce[i]) ? 1 : 0;
+ }
+}
+
+/**
+ * Decrypts the byte at the current file position using AES-128-CTR. In CTR (or "counter") mode,
+ * encryption and decryption are performed using the same algorithm.
+ *
+ * @param[in,out] pSession A reference to a file session.
+ * @param[in] pByte The byte to decrypt.
+ */
+void FwdLockFile_DecryptByte(FwdLockFile_Session_t * pSession, unsigned char *pByte) {
+ uint64_t blockIndex = pSession->filePos / AES_BLOCK_SIZE;
+ uint64_t blockOffset = pSession->filePos % AES_BLOCK_SIZE;
+ if (blockIndex != pSession->blockIndex) {
+ // The first 16 bytes of the encrypted session key is used as the nonce.
+ unsigned char counter[AES_BLOCK_SIZE];
+ FwdLockFile_CalculateCounter(pSession->pEncryptedSessionKey, blockIndex, counter);
+ AES_encrypt(counter, pSession->keyStream, &pSession->encryptionRoundKeys);
+ pSession->blockIndex = blockIndex;
+ }
+ *pByte ^= pSession->keyStream[blockOffset];
+}
+
+int FwdLockFile_attach(int fileDesc) {
+ int sessionId = FwdLockFile_AcquireSession(fileDesc);
+ if (sessionId >= 0) {
+ FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+ int isSuccess = FALSE;
+ if (read(fileDesc, pSession->topHeader, TOP_HEADER_SIZE) == TOP_HEADER_SIZE &&
+ memcmp(pSession->topHeader, topHeaderTemplate, sizeof topHeaderTemplate) == 0) {
+ pSession->contentTypeLength = pSession->topHeader[CONTENT_TYPE_LENGTH_POS];
+ assert(pSession->contentTypeLength <= UCHAR_MAX); // Untaint scalar for code checkers.
+ pSession->pContentType = malloc(pSession->contentTypeLength + 1);
+ if (pSession->pContentType != NULL &&
+ read(fileDesc, pSession->pContentType, pSession->contentTypeLength) ==
+ (ssize_t)pSession->contentTypeLength) {
+ pSession->pContentType[pSession->contentTypeLength] = '\0';
+ pSession->encryptedSessionKeyLength = FwdLockGlue_GetEncryptedKeyLength(KEY_SIZE);
+ pSession->pEncryptedSessionKey = malloc(pSession->encryptedSessionKeyLength);
+ if (pSession->pEncryptedSessionKey != NULL &&
+ read(fileDesc, pSession->pEncryptedSessionKey,
+ pSession->encryptedSessionKeyLength) ==
+ (ssize_t)pSession->encryptedSessionKeyLength &&
+ read(fileDesc, pSession->dataSignature, SHA1_HASH_SIZE) ==
+ SHA1_HASH_SIZE &&
+ read(fileDesc, pSession->headerSignature, SHA1_HASH_SIZE) ==
+ SHA1_HASH_SIZE) {
+ isSuccess = FwdLockFile_DeriveKeys(pSession);
+ }
+ }
+ }
+ if (isSuccess) {
+ pSession->dataOffset = pSession->contentTypeLength +
+ pSession->encryptedSessionKeyLength + TOP_HEADER_SIZE + 2 * SHA1_HASH_SIZE;
+ pSession->filePos = 0;
+ pSession->blockIndex = INVALID_BLOCK_INDEX;
+ } else {
+ FwdLockFile_ReleaseSession(sessionId);
+ sessionId = -1;
+ }
+ }
+ return (sessionId >= 0) ? 0 : -1;
+}
+
+int FwdLockFile_open(const char *pFilename) {
+ int fileDesc = open(pFilename, O_RDONLY);
+ if (fileDesc >= 0 && FwdLockFile_attach(fileDesc) < 0) {
+ (void)close(fileDesc);
+ fileDesc = -1;
+ }
+ return fileDesc;
+}
+
+ssize_t FwdLockFile_read(int fileDesc, void *pBuffer, size_t numBytes) {
+ ssize_t numBytesRead;
+ int sessionId = FwdLockFile_FindSession(fileDesc);
+ if (sessionId < 0) {
+ numBytesRead = -1;
+ } else {
+ FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+ ssize_t i;
+ numBytesRead = read(pSession->fileDesc, pBuffer, numBytes);
+ for (i = 0; i < numBytesRead; ++i) {
+ FwdLockFile_DecryptByte(pSession, &((unsigned char *)pBuffer)[i]);
+ ++pSession->filePos;
+ }
+ }
+ return numBytesRead;
+}
+
+off64_t FwdLockFile_lseek(int fileDesc, off64_t offset, int whence) {
+ off64_t newFilePos;
+ int sessionId = FwdLockFile_FindSession(fileDesc);
+ if (sessionId < 0) {
+ newFilePos = INVALID_OFFSET;
+ } else {
+ FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+ switch (whence) {
+ case SEEK_SET:
+ newFilePos = lseek64(pSession->fileDesc, pSession->dataOffset + offset, whence);
+ break;
+ case SEEK_CUR:
+ case SEEK_END:
+ newFilePos = lseek64(pSession->fileDesc, offset, whence);
+ break;
+ default:
+ errno = EINVAL;
+ newFilePos = INVALID_OFFSET;
+ break;
+ }
+ if (newFilePos != INVALID_OFFSET) {
+ if (newFilePos < pSession->dataOffset) {
+ // The new file position is illegal for an internal Forward Lock file. Restore the
+ // original file position.
+ (void)lseek64(pSession->fileDesc, pSession->dataOffset + pSession->filePos,
+ SEEK_SET);
+ errno = EINVAL;
+ newFilePos = INVALID_OFFSET;
+ } else {
+ // The return value should be the file position that lseek64() would have returned
+ // for the embedded content file.
+ pSession->filePos = newFilePos - pSession->dataOffset;
+ newFilePos = pSession->filePos;
+ }
+ }
+ }
+ return newFilePos;
+}
+
+int FwdLockFile_detach(int fileDesc) {
+ int sessionId = FwdLockFile_FindSession(fileDesc);
+ if (sessionId < 0) {
+ return -1;
+ }
+ HMAC_CTX_cleanup(&sessionPtrs[sessionId]->signingContext);
+ FwdLockFile_ReleaseSession(sessionId);
+ return 0;
+}
+
+int FwdLockFile_close(int fileDesc) {
+ return (FwdLockFile_detach(fileDesc) == 0) ? close(fileDesc) : -1;
+}
+
+int FwdLockFile_CheckDataIntegrity(int fileDesc) {
+ int result;
+ int sessionId = FwdLockFile_FindSession(fileDesc);
+ if (sessionId < 0) {
+ result = FALSE;
+ } else {
+ struct FwdLockFile_CheckDataIntegrity_Data {
+ unsigned char signature[SHA1_HASH_SIZE];
+ unsigned char buffer[SIG_CALC_BUFFER_SIZE];
+ } *pData = malloc(sizeof *pData);
+ if (pData == NULL) {
+ result = FALSE;
+ } else {
+ FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+ if (lseek64(pSession->fileDesc, pSession->dataOffset, SEEK_SET) !=
+ pSession->dataOffset) {
+ result = FALSE;
+ } else {
+ ssize_t numBytesRead;
+ size_t signatureSize = SHA1_HASH_SIZE;
+ while ((numBytesRead =
+ read(pSession->fileDesc, pData->buffer, SIG_CALC_BUFFER_SIZE)) > 0) {
+ HMAC_Update(&pSession->signingContext, pData->buffer, (size_t)numBytesRead);
+ }
+ if (numBytesRead < 0) {
+ result = FALSE;
+ } else {
+ HMAC_Final(&pSession->signingContext, pData->signature, &signatureSize);
+ assert(signatureSize == SHA1_HASH_SIZE);
+ result = memcmp(pData->signature, pSession->dataSignature, signatureSize) == 0;
+ }
+ HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
+ (void)lseek64(pSession->fileDesc, pSession->dataOffset + pSession->filePos,
+ SEEK_SET);
+ }
+ free(pData);
+ }
+ }
+ return result;
+}
+
+int FwdLockFile_CheckHeaderIntegrity(int fileDesc) {
+ int result;
+ int sessionId = FwdLockFile_FindSession(fileDesc);
+ if (sessionId < 0) {
+ result = FALSE;
+ } else {
+ FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+ unsigned char signature[SHA1_HASH_SIZE];
+ size_t signatureSize = SHA1_HASH_SIZE;
+ HMAC_Update(&pSession->signingContext, pSession->topHeader, TOP_HEADER_SIZE);
+ HMAC_Update(&pSession->signingContext, (unsigned char *)pSession->pContentType,
+ pSession->contentTypeLength);
+ HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey,
+ pSession->encryptedSessionKeyLength);
+ HMAC_Update(&pSession->signingContext, pSession->dataSignature, signatureSize);
+ HMAC_Final(&pSession->signingContext, signature, &signatureSize);
+ assert(signatureSize == SHA1_HASH_SIZE);
+ result = memcmp(signature, pSession->headerSignature, signatureSize) == 0;
+ HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
+ }
+ return result;
+}
+
+int FwdLockFile_CheckIntegrity(int fileDesc) {
+ return FwdLockFile_CheckHeaderIntegrity(fileDesc) && FwdLockFile_CheckDataIntegrity(fileDesc);
+}
+
+const char *FwdLockFile_GetContentType(int fileDesc) {
+ int sessionId = FwdLockFile_FindSession(fileDesc);
+ if (sessionId < 0) {
+ return NULL;
+ }
+ return sessionPtrs[sessionId]->pContentType;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h
new file mode 100644
index 0000000..fc64050
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FWDLOCKFILE_H__
+#define __FWDLOCKFILE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/**
+ * Attaches to an open Forward Lock file. The file position is assumed to be at the beginning of the
+ * file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A status code.
+ * @retval 0 Success.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_attach(int fileDesc);
+
+/**
+ * Opens a Forward Lock file for reading.
+ *
+ * @param[in] pFilename A reference to a filename.
+ *
+ * @return A file descriptor.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_open(const char *pFilename);
+
+/**
+ * Reads the specified number of bytes from an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ * @param[out] pBuffer A reference to the buffer that should receive the read data.
+ * @param[in] numBytes The number of bytes to read.
+ *
+ * @return The number of bytes read.
+ * @retval -1 Failure.
+ */
+ssize_t FwdLockFile_read(int fileDesc, void *pBuffer, size_t numBytes);
+
+/**
+ * Updates the file position within an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ * @param[in] offset The offset with which to update the file position.
+ * @param[in] whence One of SEEK_SET, SEEK_CUR, and SEEK_END.
+ *
+ * @return The new file position.
+ * @retval ((off64_t)-1) Failure.
+ */
+off64_t FwdLockFile_lseek(int fileDesc, off64_t offset, int whence);
+
+/**
+ * Detaches from an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A status code.
+ * @retval 0 Success.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_detach(int fileDesc);
+
+/**
+ * Closes an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A status code.
+ * @retval 0 Success.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_close(int fileDesc);
+
+/**
+ * Checks the data integrity of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A Boolean value indicating whether the integrity check was successful.
+ */
+int FwdLockFile_CheckDataIntegrity(int fileDesc);
+
+/**
+ * Checks the header integrity of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A Boolean value indicating whether the integrity check was successful.
+ */
+int FwdLockFile_CheckHeaderIntegrity(int fileDesc);
+
+/**
+ * Checks both the data and header integrity of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A Boolean value indicating whether the integrity check was successful.
+ */
+int FwdLockFile_CheckIntegrity(int fileDesc);
+
+/**
+ * Returns the content type of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return
+ * A reference to the content type. The reference remains valid as long as the file is kept open.
+ */
+const char *FwdLockFile_GetContentType(int fileDesc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __FWDLOCKFILE_H__
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html
new file mode 100755
index 0000000..8f95cd2
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html
@@ -0,0 +1,1039 @@
+<html>
+
+<head>
+<meta http-equiv=Content-Type content="text/html; charset=windows-1252">
+<meta name=Generator content="Microsoft Word 12 (filtered)">
+<title>Forward Lock Converter and Decoder</title>
+<style>
+<!--
+ /* Font Definitions */
+ @font-face
+ {font-family:SimSun;
+ panose-1:2 1 6 0 3 1 1 1 1 1;}
+@font-face
+ {font-family:"Cambria Math";
+ panose-1:2 4 5 3 5 4 6 3 2 4;}
+@font-face
+ {font-family:Tahoma;
+ panose-1:2 11 6 4 3 5 4 4 2 4;}
+@font-face
+ {font-family:"Lucida Console","DejaVu Sans Mono";
+ panose-1:2 11 6 9 4 5 4 2 2 4;}
+@font-face
+ {font-family:"\@SimSun";
+ panose-1:2 1 6 0 3 1 1 1 1 1;}
+ /* Style Definitions */
+ p.MsoNormal, li.MsoNormal, div.MsoNormal
+ {margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";}
+h1
+ {margin-right:0cm;
+ margin-left:21.6pt;
+ text-indent:-21.6pt;
+ page-break-after:avoid;
+ font-size:16.0pt;
+ font-family:"Arial","sans-serif";}
+h2
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:28.8pt;
+ text-indent:-28.8pt;
+ page-break-after:avoid;
+ font-size:14.0pt;
+ font-family:"Arial","sans-serif";
+ font-style:italic;}
+h3
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:36.0pt;
+ text-indent:-36.0pt;
+ page-break-after:avoid;
+ font-size:13.0pt;
+ font-family:"Arial","sans-serif";}
+h4
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:43.2pt;
+ text-indent:-43.2pt;
+ page-break-after:avoid;
+ font-size:14.0pt;
+ font-family:"Times New Roman","serif";}
+h5
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:50.4pt;
+ text-indent:-50.4pt;
+ font-size:13.0pt;
+ font-family:"Times New Roman","serif";
+ font-style:italic;}
+h6
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:57.6pt;
+ text-indent:-57.6pt;
+ font-size:11.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoHeading7, li.MsoHeading7, div.MsoHeading7
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:64.8pt;
+ text-indent:-64.8pt;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoHeading8, li.MsoHeading8, div.MsoHeading8
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:72.0pt;
+ text-indent:-72.0pt;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";
+ font-style:italic;}
+p.MsoHeading9, li.MsoHeading9, div.MsoHeading9
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:3.0pt;
+ margin-left:79.2pt;
+ text-indent:-79.2pt;
+ font-size:11.0pt;
+ font-family:"Arial","sans-serif";}
+p.MsoToc1, li.MsoToc1, div.MsoToc1
+ {margin-top:6.0pt;
+ margin-right:0cm;
+ margin-bottom:6.0pt;
+ margin-left:0cm;
+ line-height:150%;
+ font-size:10.5pt;
+ font-family:"Times New Roman","serif";
+ text-transform:uppercase;
+ font-weight:bold;}
+p.MsoToc2, li.MsoToc2, div.MsoToc2
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:12.0pt;
+ margin-bottom:.0001pt;
+ line-height:150%;
+ font-size:10.5pt;
+ font-family:"Times New Roman","serif";
+ font-variant:small-caps;}
+p.MsoToc3, li.MsoToc3, div.MsoToc3
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:24.0pt;
+ margin-bottom:.0001pt;
+ line-height:150%;
+ font-size:10.5pt;
+ font-family:"Times New Roman","serif";
+ font-style:italic;}
+p.MsoToc4, li.MsoToc4, div.MsoToc4
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:36.0pt;
+ margin-bottom:.0001pt;
+ font-size:9.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoToc5, li.MsoToc5, div.MsoToc5
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:48.0pt;
+ margin-bottom:.0001pt;
+ font-size:9.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoToc6, li.MsoToc6, div.MsoToc6
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:60.0pt;
+ margin-bottom:.0001pt;
+ font-size:9.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoToc7, li.MsoToc7, div.MsoToc7
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:72.0pt;
+ margin-bottom:.0001pt;
+ font-size:9.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoToc8, li.MsoToc8, div.MsoToc8
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:84.0pt;
+ margin-bottom:.0001pt;
+ font-size:9.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoToc9, li.MsoToc9, div.MsoToc9
+ {margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:0cm;
+ margin-left:96.0pt;
+ margin-bottom:.0001pt;
+ font-size:9.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoFootnoteText, li.MsoFootnoteText, div.MsoFootnoteText
+ {margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:10.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoHeader, li.MsoHeader, div.MsoHeader
+ {margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoFooter, li.MsoFooter, div.MsoFooter
+ {margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";}
+p.MsoCaption, li.MsoCaption, div.MsoCaption
+ {margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:11.0pt;
+ font-family:"Times New Roman","serif";
+ font-weight:bold;}
+span.MsoFootnoteReference
+ {vertical-align:super;}
+p.MsoTitle, li.MsoTitle, div.MsoTitle
+ {margin-top:12.0pt;
+ margin-right:0cm;
+ margin-bottom:120.0pt;
+ margin-left:0cm;
+ text-align:center;
+ font-size:16.0pt;
+ font-family:"Arial","sans-serif";
+ font-weight:bold;}
+p.MsoBodyText, li.MsoBodyText, div.MsoBodyText
+ {mso-style-link:"Body Text Char";
+ margin-top:0cm;
+ margin-right:0cm;
+ margin-bottom:6.0pt;
+ margin-left:0cm;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";}
+a:link, span.MsoHyperlink
+ {color:blue;
+ text-decoration:underline;}
+a:visited, span.MsoHyperlinkFollowed
+ {color:purple;
+ text-decoration:underline;}
+p.MsoAcetate, li.MsoAcetate, div.MsoAcetate
+ {margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:8.0pt;
+ font-family:"Tahoma","sans-serif";}
+span.BodyTextChar
+ {mso-style-name:"Body Text Char";
+ mso-style-link:"Body Text";}
+ /* Page Definitions */
+ @page WordSection1
+ {size:595.45pt 841.7pt;
+ margin:72.0pt 90.0pt 72.0pt 90.0pt;}
+div.WordSection1
+ {page:WordSection1;}
+@page WordSection2
+ {size:595.45pt 841.7pt;
+ margin:72.0pt 90.0pt 72.0pt 90.0pt;}
+div.WordSection2
+ {page:WordSection2;}
+ /* List Definitions */
+ ol
+ {margin-bottom:0cm;}
+ul
+ {margin-bottom:0cm;}
+-->
+</style>
+
+</head>
+
+<body lang=EN-US link=blue vlink=purple>
+
+<div class=WordSection1>
+
+<p class=MsoTitle>Forward Lock Converter And Decoder</p>
+
+<p class=MsoToc1><span
+class=MsoHyperlink><a href="#_Toc276471422">1<span style='font-size:12.0pt;
+line-height:150%;color:windowtext;text-transform:none;font-weight:normal;
+text-decoration:none'> </span>Introduction<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471423">2<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'> </span>Overview<span
+style='color:windowtext;display:none;text-decoration:none'>... </span><span
+style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471424">3<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'> </span>Use Cases<span
+style='color:windowtext;display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471425">3.1<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'> </span>Converter<span style='color:windowtext;display:none;
+text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471426">3.1.1<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'> </span>Convert Data (Push-Mode Conversion)<span
+style='color:windowtext;display:none;text-decoration:none'> </span><span
+style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471427">3.1.2<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'> </span>Convert File (Pull-Mode Conversion)<span
+style='color:windowtext;display:none;text-decoration:none'> </span><span
+style='color:windowtext;display:none;text-decoration:none'>6</span></a></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471428">3.2<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'> </span>Decoder<span style='color:windowtext;display:none;
+text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>7</span></a></span></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471429">3.2.1<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'> </span>Check Integrity<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>8</span></a></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471430">3.2.2<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'> </span>Get Content Type<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>9</span></a></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471431">3.2.3<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'> </span>Decode File<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>10</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471432">4<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'> </span>Definition of the
+Internal Forward Lock File Format<span style='color:windowtext;display:none;
+text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>11</span></a></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471433">4.1<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'> </span>Key Derivation<span style='color:windowtext;display:none;
+text-decoration:none'>.. </span><span
+style='color:windowtext;display:none;text-decoration:none'>11</span></a></span></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471434">4.2<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'> </span>Calculation of the Counters<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471435">5<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'> </span>Unit Test Cases<span
+style='color:windowtext;display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471436">6<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'> </span>References<span
+style='color:windowtext;display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></p>
+
+<p class=MsoBodyText></p>
+
+</div>
+
+<span style='font-size:12.0pt;font-family:"Times New Roman","serif"'><br
+clear=all style='page-break-before:right'>
+</span>
+
+<div class=WordSection2>
+
+<h1><a name="_Toc276471422"></a><a name="_Ref263085474">1<span
+style='font:7.0pt "Times New Roman"'> </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
+files 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>
+functionthey 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 lockprotected. 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 shouldnt 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 parameteraccess 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 signaturethe 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 signaturethe 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 cant 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 OperationMethods and
+Techniques, NIST Special Publication 800-38A, December 2001.</a><a
+name="_Ref151269073"></a></p>
+
+</div>
+
+</body>
+
+</html>
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif
new file mode 100644
index 0000000..ee94513
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif
new file mode 100644
index 0000000..8c12f46
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif
new file mode 100644
index 0000000..9e019ca
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif
new file mode 100644
index 0000000..cae1d01
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif
new file mode 100644
index 0000000..0d87be9
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif
new file mode 100644
index 0000000..9445b6b
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/passthru/Android.mk b/drm/libdrmframework/plugins/passthru/Android.mk
new file mode 100644
index 0000000..7856d37
--- /dev/null
+++ b/drm/libdrmframework/plugins/passthru/Android.mk
@@ -0,0 +1,48 @@
+#
+# 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/DrmPassthruPlugIn.cpp
+
+LOCAL_MODULE := libdrmpassthruplugin
+
+LOCAL_STATIC_LIBRARIES := libdrmframeworkcommon
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_C_INCLUDES += \
+ $(TOP)/frameworks/base/drm/libdrmframework/include \
+ $(TOP)/frameworks/base/drm/libdrmframework/plugins/passthru/include \
+ $(TOP)/frameworks/base/drm/libdrmframework/plugins/common/include \
+ $(TOP)/frameworks/base/include
+
+# Set the following flag to enable the decryption passthru flow
+#LOCAL_CFLAGS += -DENABLE_PASSTHRU_DECRYPTION
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h b/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h
new file mode 100644
index 0000000..bbcd9ed
--- /dev/null
+++ b/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h
@@ -0,0 +1,102 @@
+/*
+ * 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 __DRM_PASSTHRU_PLUGIN_H__
+#define __DRM_PASSTHRU_PLUGIN_H__
+
+#include <DrmEngineBase.h>
+
+namespace android {
+
+class DrmPassthruPlugIn : public DrmEngineBase {
+
+public:
+ DrmPassthruPlugIn();
+ virtual ~DrmPassthruPlugIn();
+
+protected:
+ DrmConstraints* onGetConstraints(int uniqueId, const String8* path, int action);
+
+ DrmMetadata* onGetMetadata(int uniqueId, const String8* path);
+
+ status_t onInitialize(int uniqueId);
+
+ status_t onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener);
+
+ status_t onTerminate(int uniqueId);
+
+ bool onCanHandle(int uniqueId, const String8& path);
+
+ DrmInfoStatus* onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+ status_t onSaveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath);
+
+ DrmInfo* onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest);
+
+ String8 onGetOriginalMimeType(int uniqueId, const String8& path);
+
+ int onGetDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+
+ int onCheckRightsStatus(int uniqueId, const String8& path, int action);
+
+ status_t onConsumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+
+ status_t onSetPlaybackStatus(
+ int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+ bool onValidateAction(
+ int uniqueId, const String8& path, int action, const ActionDescription& description);
+
+ status_t onRemoveRights(int uniqueId, const String8& path);
+
+ status_t onRemoveAllRights(int uniqueId);
+
+ status_t onOpenConvertSession(int uniqueId, int convertId);
+
+ DrmConvertedStatus* onConvertData(int uniqueId, int convertId, const DrmBuffer* inputData);
+
+ DrmConvertedStatus* onCloseConvertSession(int uniqueId, int convertId);
+
+ DrmSupportInfo* onGetSupportInfo(int uniqueId);
+
+ status_t onOpenDecryptSession(
+ int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length);
+
+ status_t onOpenDecryptSession(
+ int uniqueId, DecryptHandle* decryptHandle, const char* uri);
+
+ status_t onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+
+ status_t onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo);
+
+ status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
+
+ status_t onFinalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+
+ ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off_t offset);
+
+private:
+ DecryptHandle* openDecryptSessionImpl();
+};
+
+};
+
+#endif /* __DRM_PASSTHRU_PLUGIN_H__ */
+
diff --git a/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
new file mode 100644
index 0000000..dee1fdb
--- /dev/null
+++ b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
@@ -0,0 +1,299 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "DrmPassthruPlugIn"
+#include <utils/Log.h>
+
+#include <drm/DrmRights.h>
+#include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
+#include <drm/DrmInfo.h>
+#include <drm/DrmInfoEvent.h>
+#include <drm/DrmInfoStatus.h>
+#include <drm/DrmConvertedStatus.h>
+#include <drm/DrmInfoRequest.h>
+#include <drm/DrmSupportInfo.h>
+#include <DrmPassthruPlugIn.h>
+
+using namespace android;
+
+
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" IDrmEngine* create() {
+ return new DrmPassthruPlugIn();
+}
+
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" void destroy(IDrmEngine* pPlugIn) {
+ delete pPlugIn;
+ pPlugIn = NULL;
+}
+
+DrmPassthruPlugIn::DrmPassthruPlugIn()
+ : DrmEngineBase() {
+
+}
+
+DrmPassthruPlugIn::~DrmPassthruPlugIn() {
+
+}
+
+DrmMetadata* DrmPassthruPlugIn::onGetMetadata(int uniqueId, const String8* path) {
+ return NULL;
+}
+
+DrmConstraints* DrmPassthruPlugIn::onGetConstraints(
+ int uniqueId, const String8* path, int action) {
+ LOGD("DrmPassthruPlugIn::onGetConstraints From Path: %d", uniqueId);
+ DrmConstraints* drmConstraints = new DrmConstraints();
+
+ String8 value("dummy_available_time");
+ char* charValue = NULL;
+ charValue = new char[value.length() + 1];
+ strncpy(charValue, value.string(), value.length());
+
+ //Just add dummy available time for verification
+ drmConstraints->put(&(DrmConstraints::LICENSE_AVAILABLE_TIME), charValue);
+
+ return drmConstraints;
+}
+
+DrmInfoStatus* DrmPassthruPlugIn::onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+ LOGD("DrmPassthruPlugIn::onProcessDrmInfo - Enter : %d", uniqueId);
+ DrmInfoStatus* drmInfoStatus = NULL;
+ if (NULL != drmInfo) {
+ switch (drmInfo->getInfoType()) {
+ case DrmInfoRequest::TYPE_REGISTRATION_INFO: {
+ const DrmBuffer* emptyBuffer = new DrmBuffer();
+ drmInfoStatus = new DrmInfoStatus(DrmInfoStatus::STATUS_OK,
+ DrmInfoRequest::TYPE_REGISTRATION_INFO, emptyBuffer, drmInfo->getMimeType());
+ break;
+ }
+ case DrmInfoRequest::TYPE_UNREGISTRATION_INFO: {
+ const DrmBuffer* emptyBuffer = new DrmBuffer();
+ drmInfoStatus = new DrmInfoStatus(DrmInfoStatus::STATUS_OK,
+ DrmInfoRequest::TYPE_UNREGISTRATION_INFO, emptyBuffer, drmInfo->getMimeType());
+ break;
+ }
+ case DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO: {
+ String8 licenseString("dummy_license_string");
+ const int bufferSize = licenseString.size();
+ char* data = NULL;
+ data = new char[bufferSize];
+ memcpy(data, licenseString.string(), bufferSize);
+ const DrmBuffer* buffer = new DrmBuffer(data, bufferSize);
+ drmInfoStatus = new DrmInfoStatus(DrmInfoStatus::STATUS_OK,
+ DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO, buffer, drmInfo->getMimeType());
+ break;
+ }
+ }
+ }
+ LOGD("DrmPassthruPlugIn::onProcessDrmInfo - Exit");
+ return drmInfoStatus;
+}
+
+status_t DrmPassthruPlugIn::onSetOnInfoListener(
+ int uniqueId, const IDrmEngine::OnInfoListener* infoListener) {
+ LOGD("DrmPassthruPlugIn::onSetOnInfoListener : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onInitialize(int uniqueId) {
+ LOGD("DrmPassthruPlugIn::onInitialize : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onTerminate(int uniqueId) {
+ LOGD("DrmPassthruPlugIn::onTerminate : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+DrmSupportInfo* DrmPassthruPlugIn::onGetSupportInfo(int uniqueId) {
+ LOGD("DrmPassthruPlugIn::onGetSupportInfo : %d", uniqueId);
+ DrmSupportInfo* drmSupportInfo = new DrmSupportInfo();
+ // Add mimetype's
+ drmSupportInfo->addMimeType(String8("application/vnd.passthru.drm"));
+ // Add File Suffixes
+ drmSupportInfo->addFileSuffix(String8(".passthru"));
+ // Add plug-in description
+ drmSupportInfo->setDescription(String8("Passthru plug-in"));
+ return drmSupportInfo;
+}
+
+status_t DrmPassthruPlugIn::onSaveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath) {
+ LOGD("DrmPassthruPlugIn::onSaveRights : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+DrmInfo* DrmPassthruPlugIn::onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+ LOGD("DrmPassthruPlugIn::onAcquireDrmInfo : %d", uniqueId);
+ DrmInfo* drmInfo = NULL;
+
+ if (NULL != drmInfoRequest) {
+ String8 dataString("dummy_acquistion_string");
+ int length = dataString.length();
+ char* data = NULL;
+ data = new char[length];
+ memcpy(data, dataString.string(), length);
+ drmInfo = new DrmInfo(drmInfoRequest->getInfoType(),
+ DrmBuffer(data, length), drmInfoRequest->getMimeType());
+ }
+ return drmInfo;
+}
+
+bool DrmPassthruPlugIn::onCanHandle(int uniqueId, const String8& path) {
+ LOGD("DrmPassthruPlugIn::canHandle: %s ", path.string());
+ String8 extension = path.getPathExtension();
+ extension.toLower();
+ return (String8(".passthru") == extension);
+}
+
+String8 DrmPassthruPlugIn::onGetOriginalMimeType(int uniqueId, const String8& path) {
+ LOGD("DrmPassthruPlugIn::onGetOriginalMimeType() : %d", uniqueId);
+ return String8("video/passthru");
+}
+
+int DrmPassthruPlugIn::onGetDrmObjectType(
+ int uniqueId, const String8& path, const String8& mimeType) {
+ LOGD("DrmPassthruPlugIn::onGetDrmObjectType() : %d", uniqueId);
+ return DrmObjectType::UNKNOWN;
+}
+
+int DrmPassthruPlugIn::onCheckRightsStatus(int uniqueId, const String8& path, int action) {
+ LOGD("DrmPassthruPlugIn::onCheckRightsStatus() : %d", uniqueId);
+ int rightsStatus = RightsStatus::RIGHTS_VALID;
+ return rightsStatus;
+}
+
+status_t DrmPassthruPlugIn::onConsumeRights(int uniqueId, DecryptHandle* decryptHandle,
+ int action, bool reserve) {
+ LOGD("DrmPassthruPlugIn::onConsumeRights() : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+ int playbackStatus, int position) {
+ LOGD("DrmPassthruPlugIn::onSetPlaybackStatus() : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+bool DrmPassthruPlugIn::onValidateAction(int uniqueId, const String8& path,
+ int action, const ActionDescription& description) {
+ LOGD("DrmPassthruPlugIn::onValidateAction() : %d", uniqueId);
+ return true;
+}
+
+status_t DrmPassthruPlugIn::onRemoveRights(int uniqueId, const String8& path) {
+ LOGD("DrmPassthruPlugIn::onRemoveRights() : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onRemoveAllRights(int uniqueId) {
+ LOGD("DrmPassthruPlugIn::onRemoveAllRights() : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onOpenConvertSession(int uniqueId, int convertId) {
+ LOGD("DrmPassthruPlugIn::onOpenConvertSession() : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+DrmConvertedStatus* DrmPassthruPlugIn::onConvertData(
+ int uniqueId, int convertId, const DrmBuffer* inputData) {
+ LOGD("DrmPassthruPlugIn::onConvertData() : %d", uniqueId);
+ DrmBuffer* convertedData = NULL;
+
+ if (NULL != inputData && 0 < inputData->length) {
+ int length = inputData->length;
+ char* data = NULL;
+ data = new char[length];
+ convertedData = new DrmBuffer(data, length);
+ memcpy(convertedData->data, inputData->data, length);
+ }
+ return new DrmConvertedStatus(DrmConvertedStatus::STATUS_OK, convertedData, 0 /*offset*/);
+}
+
+DrmConvertedStatus* DrmPassthruPlugIn::onCloseConvertSession(int uniqueId, int convertId) {
+ LOGD("DrmPassthruPlugIn::onCloseConvertSession() : %d", uniqueId);
+ return new DrmConvertedStatus(DrmConvertedStatus::STATUS_OK, NULL, 0 /*offset*/);
+}
+
+status_t DrmPassthruPlugIn::onOpenDecryptSession(
+ int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) {
+ LOGD("DrmPassthruPlugIn::onOpenDecryptSession() : %d", uniqueId);
+
+#ifdef ENABLE_PASSTHRU_DECRYPTION
+ decryptHandle->mimeType = String8("video/passthru");
+ decryptHandle->decryptApiType = DecryptApiType::ELEMENTARY_STREAM_BASED;
+ decryptHandle->status = DRM_NO_ERROR;
+ decryptHandle->decryptInfo = NULL;
+ return DRM_NO_ERROR;
+#endif
+
+ return DRM_ERROR_CANNOT_HANDLE;
+}
+
+status_t DrmPassthruPlugIn::onOpenDecryptSession(
+ int uniqueId, DecryptHandle* decryptHandle, const char* uri) {
+ return DRM_ERROR_CANNOT_HANDLE;
+}
+
+status_t DrmPassthruPlugIn::onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+ LOGD("DrmPassthruPlugIn::onCloseDecryptSession() : %d", uniqueId);
+ if (NULL != decryptHandle) {
+ if (NULL != decryptHandle->decryptInfo) {
+ delete decryptHandle->decryptInfo; decryptHandle->decryptInfo = NULL;
+ }
+ delete decryptHandle; decryptHandle = NULL;
+ }
+ return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo) {
+ LOGD("DrmPassthruPlugIn::onInitializeDecryptUnit() : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onDecrypt(int uniqueId, DecryptHandle* decryptHandle,
+ int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+ LOGD("DrmPassthruPlugIn::onDecrypt() : %d", uniqueId);
+ /**
+ * As a workaround implementation passthru would copy the given
+ * encrypted buffer as it is to decrypted buffer. Note, decBuffer
+ * memory has to be allocated by the caller.
+ */
+ if (NULL != (*decBuffer) && 0 < (*decBuffer)->length) {
+ memcpy((*decBuffer)->data, encBuffer->data, encBuffer->length);
+ (*decBuffer)->length = encBuffer->length;
+ }
+ return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onFinalizeDecryptUnit(
+ int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+ LOGD("DrmPassthruPlugIn::onFinalizeDecryptUnit() : %d", uniqueId);
+ return DRM_NO_ERROR;
+}
+
+ssize_t DrmPassthruPlugIn::onPread(int uniqueId, DecryptHandle* decryptHandle,
+ void* buffer, ssize_t numBytes, off_t offset) {
+ LOGD("DrmPassthruPlugIn::onPread() : %d", uniqueId);
+ return 0;
+}
+
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 8ac2aa0..a587d0d1 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1221,7 +1221,7 @@
checkRange(texs.length, texOffset, vertexCount);
}
if (colors != null) {
- checkRange(colors.length, colorOffset, vertexCount);
+ checkRange(colors.length, colorOffset, vertexCount / 2);
}
if (indices != null) {
checkRange(indices.length, indexOffset, indexCount);
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index 58206d4..49f497c 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -204,7 +204,7 @@
@Override
public ConstantState getConstantState() {
if (mState.canConstantState()) {
- mState.mChangingConfigurations = super.getChangingConfigurations();
+ mState.mChangingConfigurations = getChangingConfigurations();
return mState;
}
return null;
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 32111e8..0b8465e 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -427,7 +427,7 @@
@Override
public final ConstantState getConstantState() {
- mBitmapState.mChangingConfigurations = super.getChangingConfigurations();
+ mBitmapState.mChangingConfigurations = getChangingConfigurations();
return mBitmapState;
}
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index a772871..2b3bd80 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -229,7 +229,7 @@
@Override
public ConstantState getConstantState() {
if (mClipState.canConstantState()) {
- mClipState.mChangingConfigurations = super.getChangingConfigurations();
+ mClipState.mChangingConfigurations = getChangingConfigurations();
return mClipState;
}
return null;
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index 604c602..0985c1b 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -124,7 +124,7 @@
@Override
public ConstantState getConstantState() {
- mState.mChangingConfigurations = super.getChangingConfigurations();
+ mState.mChangingConfigurations = getChangingConfigurations();
return mState;
}
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index c6f57d4..b13f26f 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -236,7 +236,7 @@
@Override
public ConstantState getConstantState() {
if (mDrawableContainerState.canConstantState()) {
- mDrawableContainerState.mChangingConfigurations = super.getChangingConfigurations();
+ mDrawableContainerState.mChangingConfigurations = getChangingConfigurations();
return mDrawableContainerState;
}
return null;
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 33ecbea..308fd08 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -832,7 +832,7 @@
@Override
public ConstantState getConstantState() {
- mGradientState.mChangingConfigurations = super.getChangingConfigurations();
+ mGradientState.mChangingConfigurations = getChangingConfigurations();
return mGradientState;
}
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index a9c983e..67c928c 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -238,7 +238,7 @@
@Override
public ConstantState getConstantState() {
if (mInsetState.canConstantState()) {
- mInsetState.mChangingConfigurations = super.getChangingConfigurations();
+ mInsetState.mChangingConfigurations = getChangingConfigurations();
return mInsetState;
}
return null;
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 8047dd4..234b80d 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -523,7 +523,7 @@
@Override
public ConstantState getConstantState() {
if (mLayerState.canConstantState()) {
- mLayerState.mChangingConfigurations = super.getChangingConfigurations();
+ mLayerState.mChangingConfigurations = getChangingConfigurations();
return mLayerState;
}
return null;
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 00416d8..6768186 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -327,7 +327,7 @@
@Override
public ConstantState getConstantState() {
- mNinePatchState.mChangingConfigurations = super.getChangingConfigurations();
+ mNinePatchState.mChangingConfigurations = getChangingConfigurations();
return mNinePatchState;
}
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 9c47dab..1428efa 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -189,7 +189,7 @@
@Override
public ConstantState getConstantState() {
if (mState.canConstantState()) {
- mState.mChangingConfigurations = super.getChangingConfigurations();
+ mState.mChangingConfigurations = getChangingConfigurations();
return mState;
}
return null;
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index b623d80..a95eb06 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -237,7 +237,7 @@
@Override
public ConstantState getConstantState() {
if (mScaleState.canConstantState()) {
- mScaleState.mChangingConfigurations = super.getChangingConfigurations();
+ mScaleState.mChangingConfigurations = getChangingConfigurations();
return mScaleState;
}
return null;
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index be1892e..0201fb0 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -348,7 +348,7 @@
@Override
public ConstantState getConstantState() {
- mShapeState.mChangingConfigurations = super.getChangingConfigurations();
+ mShapeState.mChangingConfigurations = getChangingConfigurations();
return mShapeState;
}
diff --git a/include/drm/DrmConstraints.h b/include/drm/DrmConstraints.h
new file mode 100644
index 0000000..a9ec942
--- /dev/null
+++ b/include/drm/DrmConstraints.h
@@ -0,0 +1,183 @@
+/*
+ * 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 __DRM_CONSTRAINTS_H__
+#define __DRM_CONSTRAINTS_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which contains the constraints information.
+ *
+ * As a result of DrmManagerClient::getConstraints(const String8*, const int)
+ * an instance of DrmConstraints would be returned.
+ *
+ */
+class DrmConstraints {
+public:
+ /**
+ * The following variables are replica of android.drm.DrmStore.ConstraintsColumns
+ * Any changes should also be incorporated with Java Layer as well
+ */
+ /**
+ * The max repeat count
+ */
+ static const String8 MAX_REPEAT_COUNT;
+ /**
+ * The remaining repeat count
+ */
+ static const String8 REMAINING_REPEAT_COUNT;
+
+ /**
+ * The time before which the protected file can not be played/viewed
+ */
+ static const String8 LICENSE_START_TIME;
+
+ /**
+ * The time after which the protected file can not be played/viewed
+ */
+ static const String8 LICENSE_EXPIRY_TIME;
+
+ /**
+ * The available time for license
+ */
+ static const String8 LICENSE_AVAILABLE_TIME;
+
+ /**
+ * The data stream for extended metadata
+ */
+ static const String8 EXTENDED_METADATA;
+
+public:
+ /**
+ * Iterator for key
+ */
+ class KeyIterator {
+ friend class DrmConstraints;
+ private:
+ KeyIterator(DrmConstraints* drmConstraints)
+ : mDrmConstraints(drmConstraints), mIndex(0) {}
+
+ public:
+ KeyIterator(const KeyIterator& keyIterator);
+ KeyIterator& operator=(const KeyIterator& keyIterator);
+ virtual ~KeyIterator() {}
+
+ public:
+ bool hasNext();
+ const String8& next();
+
+ private:
+ DrmConstraints* mDrmConstraints;
+ unsigned int mIndex;
+ };
+
+ /**
+ * Iterator for constraints
+ */
+ class Iterator {
+ friend class DrmConstraints;
+ private:
+ Iterator(DrmConstraints* drmConstraints)
+ : mDrmConstraints(drmConstraints), mIndex(0) {}
+
+ public:
+ Iterator(const Iterator& iterator);
+ Iterator& operator=(const Iterator& iterator);
+ virtual ~Iterator() {}
+
+ public:
+ bool hasNext();
+ String8 next();
+
+ private:
+ DrmConstraints* mDrmConstraints;
+ unsigned int mIndex;
+ };
+
+public:
+ DrmConstraints() {}
+ virtual ~DrmConstraints() {
+ DrmConstraints::KeyIterator keyIt = this->keyIterator();
+
+ while (keyIt.hasNext()) {
+ String8 key = keyIt.next();
+ const char* value = this->getAsByteArray(&key);
+ if (NULL != value) {
+ delete[] value;
+ value = NULL;
+ }
+ }
+ mConstraintMap.clear();
+ }
+public:
+ /**
+ * Returns the number of constraints contained in this instance
+ *
+ * @return Number of constraints
+ */
+ int getCount(void) const;
+
+ /**
+ * Adds constraint information as <key, value> pair to this instance
+ *
+ * @param[in] key Key to add
+ * @param[in] value Value to add
+ * @return Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t put(const String8* key, const char* value);
+
+ /**
+ * Retrieves the value of given key
+ *
+ * @param key Key whose value to be retrieved
+ * @return The value
+ */
+ String8 get(const String8& key) const;
+
+ /**
+ * Retrieves the value as byte array of given key
+ * @param key Key whose value to be retrieved as byte array
+ * @return The byte array value
+ */
+ const char* getAsByteArray(const String8* key) const;
+
+ /**
+ * Returns KeyIterator object to walk through the keys associated with this instance
+ *
+ * @return KeyIterator object
+ */
+ KeyIterator keyIterator();
+
+ /**
+ * Returns Iterator object to walk through the values associated with this instance
+ *
+ * @return Iterator object
+ */
+ Iterator iterator();
+private:
+ const char* getValue(const String8* key) const;
+private:
+ typedef KeyedVector<String8, const char*> DrmConstraintsMap;
+ DrmConstraintsMap mConstraintMap;
+};
+
+};
+
+#endif /* __DRM_CONSTRAINTS_H__ */
+
diff --git a/include/drm/DrmConvertedStatus.h b/include/drm/DrmConvertedStatus.h
new file mode 100644
index 0000000..679e48d
--- /dev/null
+++ b/include/drm/DrmConvertedStatus.h
@@ -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.
+ */
+
+#ifndef __DRM_CONVERTED_STATUS_H__
+#define __DRM_CONVERTED_STATUS_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which wraps the status of the conversion, the converted
+ * data/checksum data and the offset. Offset is going to be used in the case of close
+ * session where the agent will inform where the header and body signature should be added
+ *
+ * As a result of DrmManagerClient::convertData(int, const DrmBuffer*) and
+ * DrmManagerClient::closeConvertSession(int) an instance of DrmConvertedStatus
+ * would be returned.
+ *
+ */
+class DrmConvertedStatus {
+public:
+ // Should be in sync with DrmConvertedStatus.java
+ static const int STATUS_OK = 1;
+ static const int STATUS_INPUTDATA_ERROR = 2;
+ static const int STATUS_ERROR = 3;
+
+public:
+ /**
+ * Constructor for DrmConvertedStatus
+ *
+ * @param[in] _statusCode Status of the conversion
+ * @param[in] _convertedData Converted data/checksum data
+ * @param[in] _offset Offset value
+ */
+ DrmConvertedStatus(int _statusCode, const DrmBuffer* _convertedData, int _offset);
+
+ /**
+ * Destructor for DrmConvertedStatus
+ */
+ virtual ~DrmConvertedStatus() {
+
+ }
+
+public:
+ int statusCode;
+ const DrmBuffer* convertedData;
+ int offset;
+};
+
+};
+
+#endif /* __DRM_CONVERTED_STATUS_H__ */
+
diff --git a/include/drm/DrmInfo.h b/include/drm/DrmInfo.h
new file mode 100644
index 0000000..7b48541
--- /dev/null
+++ b/include/drm/DrmInfo.h
@@ -0,0 +1,176 @@
+/*
+ * 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 __DRM_INFO_H__
+#define __DRM_INFO_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class in which necessary information required to transact
+ * between device and online DRM server is described. DRM Framework achieves
+ * server registration, license acquisition and any other server related transaction
+ * by passing an instance of this class to DrmManagerClient::processDrmInfo(const DrmInfo*).
+ *
+ * The Caller can retrieve the DrmInfo instance by using
+ * DrmManagerClient::acquireDrmInfo(const DrmInfoRequest*) by passing DrmInfoRequest instance.
+ *
+ */
+class DrmInfo {
+public:
+ /**
+ * Constructor for DrmInfo
+ *
+ * @param[in] infoType Type of information
+ * @param[in] drmBuffer Trigger data
+ * @param[in] mimeType MIME type
+ */
+ DrmInfo(int infoType, const DrmBuffer& drmBuffer, const String8& mimeType);
+
+ /**
+ * Destructor for DrmInfo
+ */
+ virtual ~DrmInfo() {}
+
+public:
+ /**
+ * Iterator for key
+ */
+ class KeyIterator {
+ friend class DrmInfo;
+
+ private:
+ KeyIterator(const DrmInfo* drmInfo)
+ : mDrmInfo(const_cast <DrmInfo*> (drmInfo)), mIndex(0) {}
+
+ public:
+ KeyIterator(const KeyIterator& keyIterator);
+ KeyIterator& operator=(const KeyIterator& keyIterator);
+ virtual ~KeyIterator() {}
+
+ public:
+ bool hasNext();
+ const String8& next();
+
+ private:
+ DrmInfo* mDrmInfo;
+ unsigned int mIndex;
+ };
+
+ /**
+ * Iterator
+ */
+ class Iterator {
+ friend class DrmInfo;
+
+ private:
+ Iterator(const DrmInfo* drmInfo)
+ : mDrmInfo(const_cast <DrmInfo*> (drmInfo)), mIndex(0) {}
+
+ public:
+ Iterator(const Iterator& iterator);
+ Iterator& operator=(const Iterator& iterator);
+ virtual ~Iterator() {}
+
+ public:
+ bool hasNext();
+ String8& next();
+
+ private:
+ DrmInfo* mDrmInfo;
+ unsigned int mIndex;
+ };
+
+public:
+ /**
+ * Returns information type associated with this instance
+ *
+ * @return Information type
+ */
+ int getInfoType(void) const;
+
+ /**
+ * Returns MIME type associated with this instance
+ *
+ * @return MIME type
+ */
+ String8 getMimeType(void) const;
+
+ /**
+ * Returns the trigger data associated with this instance
+ *
+ * @return Trigger data
+ */
+ const DrmBuffer& getData(void) const;
+
+ /**
+ * Returns the number of attributes contained in this instance
+ *
+ * @return Number of attributes
+ */
+ int getCount(void) const;
+
+ /**
+ * Adds optional information as <key, value> pair to this instance
+ *
+ * @param[in] key Key to add
+ * @param[in] value Value to add
+ * @return Returns the error code
+ */
+ status_t put(const String8& key, const String8& value);
+
+ /**
+ * Retrieves the value of given key
+ *
+ * @param key Key whose value to be retrieved
+ * @return The value
+ */
+ String8 get(const String8& key) const;
+
+ /**
+ * Returns KeyIterator object to walk through the keys associated with this instance
+ *
+ * @return KeyIterator object
+ */
+ KeyIterator keyIterator() const;
+
+ /**
+ * Returns Iterator object to walk through the values associated with this instance
+ *
+ * @return Iterator object
+ */
+ Iterator iterator() const;
+
+ /**
+ * Returns index of the given key
+ *
+ * @return index
+ */
+ int indexOfKey(const String8& key) const;
+
+protected:
+ int mInfoType;
+ DrmBuffer mData;
+ String8 mMimeType;
+ KeyedVector<String8, String8> mAttributes;
+};
+
+};
+
+#endif /* __DRM_INFO_H__ */
+
diff --git a/include/drm/DrmInfoEvent.h b/include/drm/DrmInfoEvent.h
new file mode 100644
index 0000000..7b409ff
--- /dev/null
+++ b/include/drm/DrmInfoEvent.h
@@ -0,0 +1,111 @@
+/*
+ * 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 __DRM_INFO_EVENT_H__
+#define __DRM_INFO_EVENT_H__
+
+namespace android {
+
+class String8;
+
+/**
+ * This is an entity class which would be passed to caller in
+ * DrmManagerClient::OnInfoListener::onInfo(const DrmInfoEvent&).
+ */
+class DrmInfoEvent {
+public:
+ /**
+ * The following constant values should be in sync with DrmInfoEvent.java
+ */
+ //! TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT, when registration has been
+ //! already done by another account ID.
+ static const int TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT = 1;
+ //! TYPE_REMOVE_RIGHTS, when the rights needs to be removed completely.
+ static const int TYPE_REMOVE_RIGHTS = 2;
+ //! TYPE_RIGHTS_INSTALLED, when the rights are downloaded and installed ok.
+ static const int TYPE_RIGHTS_INSTALLED = 3;
+ //! TYPE_WAIT_FOR_RIGHTS, rights object is on it's way to phone,
+ //! wait before calling checkRights again
+ static const int TYPE_WAIT_FOR_RIGHTS = 4;
+ //! TYPE_ACCOUNT_ALREADY_REGISTERED, when registration has been
+ //! already done for the given account.
+ static const int TYPE_ACCOUNT_ALREADY_REGISTERED = 5;
+
+ /**
+ * The following constant values should be in sync with DrmErrorEvent.java
+ */
+ //! TYPE_RIGHTS_NOT_INSTALLED, when something went wrong installing the rights
+ static const int TYPE_RIGHTS_NOT_INSTALLED = 2001;
+ //! TYPE_RIGHTS_RENEWAL_NOT_ALLOWED, when the server rejects renewal of rights
+ static const int TYPE_RIGHTS_RENEWAL_NOT_ALLOWED = 2002;
+ //! TYPE_NOT_SUPPORTED, when answer from server can not be handled by the native agent
+ static const int TYPE_NOT_SUPPORTED = 2003;
+ //! TYPE_OUT_OF_MEMORY, when memory allocation fail during renewal.
+ //! Can in the future perhaps be used to trigger garbage collector
+ static const int TYPE_OUT_OF_MEMORY = 2004;
+ //! TYPE_NO_INTERNET_CONNECTION, when the Internet connection is missing and no attempt
+ //! can be made to renew rights
+ static const int TYPE_NO_INTERNET_CONNECTION = 2005;
+ //! TYPE_PROCESS_DRM_INFO_FAILED, when failed to process DrmInfo.
+ static const int TYPE_PROCESS_DRM_INFO_FAILED = 2006;
+
+public:
+ /**
+ * Constructor for DrmInfoEvent
+ *
+ * @param[in] uniqueId Unique session identifier
+ * @param[in] infoType Type of information
+ * @param[in] message Message description
+ */
+ DrmInfoEvent(int uniqueId, int infoType, const String8& message);
+
+ /**
+ * Destructor for DrmInfoEvent
+ */
+ virtual ~DrmInfoEvent() {}
+
+public:
+ /**
+ * Returns the Unique Id associated with this instance
+ *
+ * @return Unique Id
+ */
+ int getUniqueId() const;
+
+ /**
+ * Returns the Type of information associated with this object
+ *
+ * @return Type of information
+ */
+ int getType() const;
+
+ /**
+ * Returns the message description associated with this object
+ *
+ * @return Message description
+ */
+ const String8& getMessage() const;
+
+private:
+ int mUniqueId;
+ int mInfoType;
+ const String8& mMessage;
+};
+
+};
+
+#endif /* __DRM_INFO_EVENT_H__ */
+
diff --git a/include/drm/DrmInfoRequest.h b/include/drm/DrmInfoRequest.h
new file mode 100644
index 0000000..3e48ecc
--- /dev/null
+++ b/include/drm/DrmInfoRequest.h
@@ -0,0 +1,177 @@
+/*
+ * 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 __DRM_INFO_REQUEST_H__
+#define __DRM_INFO_REQUEST_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class used to pass required parameters to get
+ * the necessary information to communicate with online DRM server
+ *
+ * An instance of this class is passed to
+ * DrmManagerClient::acquireDrmInfo(const DrmInfoRequest*) to get the
+ * instance of DrmInfo.
+ *
+ */
+class DrmInfoRequest {
+public:
+ // Changes in following constants should be in sync with DrmInfoRequest.java
+ static const int TYPE_REGISTRATION_INFO = 1;
+ static const int TYPE_UNREGISTRATION_INFO = 2;
+ static const int TYPE_RIGHTS_ACQUISITION_INFO = 3;
+ static const int TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO = 4;
+
+ /**
+ * Key to pass the unique id for the account or the user
+ */
+ static const String8 ACCOUNT_ID;
+ /**
+ * Key to pass the subscription id
+ */
+ static const String8 SUBSCRIPTION_ID;
+
+public:
+ /**
+ * Constructor for DrmInfoRequest
+ *
+ * @param[in] infoType Type of information
+ * @param[in] mimeType MIME type
+ */
+ DrmInfoRequest(int infoType, const String8& mimeType);
+
+ /**
+ * Destructor for DrmInfoRequest
+ */
+ virtual ~DrmInfoRequest() {}
+
+public:
+ /**
+ * Iterator for key
+ */
+ class KeyIterator {
+ friend class DrmInfoRequest;
+
+ private:
+ KeyIterator(const DrmInfoRequest* drmInfoRequest)
+ : mDrmInfoRequest(const_cast <DrmInfoRequest*> (drmInfoRequest)), mIndex(0) {}
+
+ public:
+ KeyIterator(const KeyIterator& keyIterator);
+ KeyIterator& operator=(const KeyIterator& keyIterator);
+ virtual ~KeyIterator() {}
+
+ public:
+ bool hasNext();
+ const String8& next();
+
+ private:
+ DrmInfoRequest* mDrmInfoRequest;
+ unsigned int mIndex;
+ };
+
+ /**
+ * Iterator
+ */
+ class Iterator {
+ friend class DrmInfoRequest;
+
+ private:
+ Iterator(const DrmInfoRequest* drmInfoRequest)
+ : mDrmInfoRequest(const_cast <DrmInfoRequest*> (drmInfoRequest)), mIndex(0) {}
+
+ public:
+ Iterator(const Iterator& iterator);
+ Iterator& operator=(const Iterator& iterator);
+ virtual ~Iterator() {}
+
+ public:
+ bool hasNext();
+ String8& next();
+
+ private:
+ DrmInfoRequest* mDrmInfoRequest;
+ unsigned int mIndex;
+ };
+
+public:
+ /**
+ * Returns information type associated with this instance
+ *
+ * @return Information type
+ */
+ int getInfoType(void) const;
+
+ /**
+ * Returns MIME type associated with this instance
+ *
+ * @return MIME type
+ */
+ String8 getMimeType(void) const;
+
+ /**
+ * Returns the number of entries in DrmRequestInfoMap
+ *
+ * @return Number of entries
+ */
+ int getCount(void) const;
+
+ /**
+ * Adds optional information as <key, value> pair to this instance
+ *
+ * @param[in] key Key to add
+ * @param[in] value Value to add
+ * @return Returns the error code
+ */
+ status_t put(const String8& key, const String8& value);
+
+ /**
+ * Retrieves the value of given key
+ *
+ * @param key Key whose value to be retrieved
+ * @return The value
+ */
+ String8 get(const String8& key) const;
+
+ /**
+ * Returns KeyIterator object to walk through the keys associated with this instance
+ *
+ * @return KeyIterator object
+ */
+ KeyIterator keyIterator() const;
+
+ /**
+ * Returns Iterator object to walk through the values associated with this instance
+ *
+ * @return Iterator object
+ */
+ Iterator iterator() const;
+
+private:
+ int mInfoType;
+ String8 mMimeType;
+
+ typedef KeyedVector<String8, String8> DrmRequestInfoMap;
+ DrmRequestInfoMap mRequestInformationMap;
+};
+
+};
+
+#endif /* __DRM_INFO_REQUEST_H__ */
+
diff --git a/include/drm/DrmInfoStatus.h b/include/drm/DrmInfoStatus.h
new file mode 100644
index 0000000..88c0f40
--- /dev/null
+++ b/include/drm/DrmInfoStatus.h
@@ -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.
+ */
+
+#ifndef __DRM_INFO_STATUS_H__
+#define __DRM_INFO_STATUS_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which wraps the result of communication between device
+ * and online DRM server.
+ *
+ * As a result of DrmManagerClient::processDrmInfo(const DrmInfo*) an instance of
+ * DrmInfoStatus would be returned. This class holds DrmBuffer which could be
+ * used to instantiate DrmRights in license acquisition.
+ *
+ */
+class DrmInfoStatus {
+public:
+ // Should be in sync with DrmInfoStatus.java
+ static const int STATUS_OK = 1;
+ static const int STATUS_ERROR = 2;
+
+public:
+ /**
+ * Constructor for DrmInfoStatus
+ *
+ * @param[in] _statusCode Status of the communication
+ * @param[in] _infoType Type of the DRM information processed
+ * @param[in] _drmBuffer Rights information
+ * @param[in] _mimeType MIME type
+ */
+ DrmInfoStatus(int _statusCode, int _infoType, const DrmBuffer* _drmBuffer, const String8& _mimeType);
+
+ /**
+ * Destructor for DrmInfoStatus
+ */
+ virtual ~DrmInfoStatus() {
+
+ }
+
+public:
+ int statusCode;
+ int infoType;
+ const DrmBuffer* drmBuffer;
+ String8 mimeType;
+};
+
+};
+
+#endif /* __DRM_INFO_STATUS_H__ */
+
diff --git a/include/drm/DrmManagerClient.h b/include/drm/DrmManagerClient.h
new file mode 100644
index 0000000..004556f
--- /dev/null
+++ b/include/drm/DrmManagerClient.h
@@ -0,0 +1,375 @@
+/*
+ * 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 __DRM_MANAGER_CLIENT_H__
+#define __DRM_MANAGER_CLIENT_H__
+
+#include <utils/threads.h>
+#include <binder/IInterface.h>
+#include "drm_framework_common.h"
+
+namespace android {
+
+class DrmInfo;
+class DrmRights;
+class DrmMetadata;
+class DrmInfoEvent;
+class DrmInfoStatus;
+class DrmInfoRequest;
+class DrmSupportInfo;
+class DrmConstraints;
+class DrmConvertedStatus;
+class DrmManagerClientImpl;
+
+/**
+ * The Native application will instantiate this class and access DRM Framework
+ * services through this class.
+ *
+ */
+class DrmManagerClient {
+public:
+ DrmManagerClient();
+
+ virtual ~DrmManagerClient();
+
+public:
+ class OnInfoListener: virtual public RefBase {
+
+ public:
+ virtual ~OnInfoListener() {}
+
+ public:
+ virtual void onInfo(const DrmInfoEvent& event) = 0;
+ };
+
+/**
+ * APIs which will be used by native modules (e.g. StageFright)
+ *
+ */
+public:
+ /**
+ * Open the decrypt session to decrypt the given protected content
+ *
+ * @param[in] fd File descriptor of the protected content to be decrypted
+ * @param[in] offset Start position of the content
+ * @param[in] length The length of the protected content
+ * @return
+ * Handle for the decryption session
+ */
+ DecryptHandle* openDecryptSession(int fd, int offset, int length);
+
+ /**
+ * Open the decrypt session to decrypt the given protected content
+ *
+ * @param[in] uri Path of the protected content to be decrypted
+ * @return
+ * Handle for the decryption session
+ */
+ DecryptHandle* openDecryptSession(const char* uri);
+
+ /**
+ * Close the decrypt session for the given handle
+ *
+ * @param[in] decryptHandle Handle for the decryption session
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t closeDecryptSession(DecryptHandle* decryptHandle);
+
+ /**
+ * Consumes the rights for a content.
+ * If the reserve parameter is true the rights is reserved until the same
+ * application calls this api again with the reserve parameter set to false.
+ *
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] action Action to perform. (Action::DEFAULT, Action::PLAY, etc)
+ * @param[in] reserve True if the rights should be reserved.
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure.
+ * In case license has been expired, DRM_ERROR_LICENSE_EXPIRED will be returned.
+ */
+ status_t consumeRights(DecryptHandle* decryptHandle, int action, bool reserve);
+
+ /**
+ * Informs the DRM engine about the playback actions performed on the DRM files.
+ *
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE)
+ * @param[in] 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
+ */
+ status_t setPlaybackStatus(DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+ /**
+ * Initialize decryption for the given unit of the protected content
+ *
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param[in] headerInfo Information for initializing decryption of this decrypUnit
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t initializeDecryptUnit(
+ 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[in] decryptHandle Handle for the decryption session
+ * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param[in] encBuffer Encrypted data block
+ * @param[out] decBuffer Decrypted data block
+ * @param[in] 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 decrypt(
+ DecryptHandle* decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV = NULL);
+
+ /**
+ * Finalize decryption for the given unit of the protected content
+ *
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t finalizeDecryptUnit(DecryptHandle* decryptHandle, int decryptUnitId);
+
+ /**
+ * Reads the specified number of bytes from an open DRM file.
+ *
+ * @param[in] decryptHandle Handle for the decryption session
+ * @param[out] buffer Reference to the buffer that should receive the read data.
+ * @param[in] numBytes Number of bytes to read.
+ * @param[in] offset Offset with which to update the file position.
+ *
+ * @return Number of bytes read. Returns -1 for Failure.
+ */
+ ssize_t pread(DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset);
+
+ /**
+ * Validates whether an action on the DRM content is allowed or not.
+ *
+ * @param[in] path Path of the protected content
+ * @param[in] action Action to validate. (Action::DEFAULT, Action::PLAY, etc)
+ * @param[in] description Detailed description of the action
+ * @return true if the action is allowed.
+ */
+ bool validateAction(const String8& path, int action, const ActionDescription& description);
+
+/**
+ * APIs which are just the underlying implementation for the Java API
+ *
+ */
+public:
+ /**
+ * Register a callback to be invoked when the caller required to
+ * receive necessary information
+ *
+ * @param[in] infoListener Listener
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t setOnInfoListener(const sp<DrmManagerClient::OnInfoListener>& infoListener);
+
+ /**
+ * Get constraint information associated with input content
+ *
+ * @param[in] path Path of the protected content
+ * @param[in] 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* getConstraints(const String8* path, const int action);
+
+ /**
+ * Get metadata information associated with input content
+ *
+ * @param[in] path Path of the protected content
+ * @return DrmMetadata
+ * key-value pairs of metadata
+ * @note
+ * In case of error, return NULL
+ */
+ DrmMetadata* getMetadata(const String8* path);
+
+ /**
+ * Check whether the given mimetype or path can be handled
+ *
+ * @param[in] path Path of the content needs to be handled
+ * @param[in] mimetype Mimetype of the content needs to be handled
+ * @return
+ * True if DrmManager can handle given path or mime type.
+ */
+ bool canHandle(const String8& path, const String8& mimeType);
+
+ /**
+ * Executes given drm information based on its type
+ *
+ * @param[in] drmInfo Information needs to be processed
+ * @return DrmInfoStatus
+ * instance as a result of processing given input
+ */
+ DrmInfoStatus* processDrmInfo(const DrmInfo* drmInfo);
+
+ /**
+ * Retrieves necessary information for registration, unregistration or rights
+ * acquisition information.
+ *
+ * @param[in] drmInfoRequest Request information to retrieve drmInfo
+ * @return DrmInfo
+ * instance as a result of processing given input
+ */
+ DrmInfo* acquireDrmInfo(const DrmInfoRequest* drmInfoRequest);
+
+ /**
+ * Save DRM rights to specified rights path
+ * and make association with content path
+ *
+ * @param[in] drmRights DrmRights to be saved
+ * @param[in] rightsPath File path where rights to be saved
+ * @param[in] contentPath File path where content was saved
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t saveRights(
+ const DrmRights& drmRights, const String8& rightsPath, const String8& contentPath);
+
+ /**
+ * Retrieves the mime type embedded inside the original content
+ *
+ * @param[in] path the path of the protected content
+ * @return String8
+ * Returns mime-type of the original content, such as "video/mpeg"
+ */
+ String8 getOriginalMimeType(const String8& path);
+
+ /**
+ * Retrieves the type of the protected object (content, rights, etc..)
+ * by using specified path or mimetype. At least one parameter should be non null
+ * to retrieve DRM object type
+ *
+ * @param[in] path Path of the content or null.
+ * @param[in] mimeType Mime type of the content or null.
+ * @return type of the DRM content,
+ * such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
+ */
+ int getDrmObjectType(const String8& path, const String8& mimeType);
+
+ /**
+ * Check whether the given content has valid rights or not
+ *
+ * @param[in] path Path of the protected content
+ * @param[in] action Action to perform
+ * @return the status of the rights for the protected content,
+ * such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc.
+ */
+ int checkRightsStatus(const String8& path, int action);
+
+ /**
+ * Removes the rights associated with the given protected content
+ *
+ * @param[in] path Path of the protected content
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t removeRights(const String8& path);
+
+ /**
+ * Removes all the rights information of each plug-in associated with
+ * DRM framework. Will be used in master reset
+ *
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t removeAllRights();
+
+ /**
+ * This API is for Forward Lock DRM.
+ * 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.
+ *
+ * @param[in] convertId Handle for the convert session
+ * @param[in] mimeType Description/MIME type of the input data packet
+ * @return Return handle for the convert session
+ */
+ int openConvertSession(const String8& mimeType);
+
+ /**
+ * Passes the input data which need to be converted. The resultant
+ * converted data and the status is returned in the DrmConvertedInfo
+ * object. This method will be called each time there are new block
+ * of data received by the application.
+ *
+ * @param[in] convertId Handle for the convert session
+ * @param[in] 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* convertData(int convertId, const DrmBuffer* inputData);
+
+ /**
+ * When there is no more data which need to be converted or when an
+ * error occurs that time the application has to inform the Drm agent
+ * via this API. Upon successful conversion of the complete data,
+ * the agent will inform that where the header and body signature
+ * should be added. This signature appending is needed to integrity
+ * protect the converted file.
+ *
+ * @param[in] 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 on which offset these signature data
+ * should be appended.
+ */
+ DrmConvertedStatus* closeConvertSession(int convertId);
+
+ /**
+ * Retrieves all DrmSupportInfo instance that native DRM framework can handle.
+ * This interface is meant to be used by JNI layer
+ *
+ * @param[out] length Number of elements in drmSupportInfoArray
+ * @param[out] drmSupportInfoArray Array contains all DrmSupportInfo
+ * that native DRM framework can handle
+ * @return status_t
+ * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t getAllSupportInfo(int* length, DrmSupportInfo** drmSupportInfoArray);
+
+private:
+ int mUniqueId;
+ Mutex mDecryptLock;
+ DrmManagerClientImpl* mDrmManagerClientImpl;
+};
+
+};
+
+#endif /* __DRM_MANAGER_CLIENT_H__ */
+
diff --git a/include/drm/DrmMetadata.h b/include/drm/DrmMetadata.h
new file mode 100644
index 0000000..2c7538a
--- /dev/null
+++ b/include/drm/DrmMetadata.h
@@ -0,0 +1,111 @@
+/*
+ * 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 __DRM_METADATA_H__
+#define __DRM_METADATA_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which contains the constraints information.
+ *
+ * As a result of DrmManagerClient::getMetadata(const String8*)
+ * an instance of DrmMetadata would be returned.
+ */
+class DrmMetadata {
+public:
+ /**
+ * Iterator for key
+ */
+ class KeyIterator {
+ friend class DrmMetadata;
+ private:
+ KeyIterator(DrmMetadata* drmMetadata) : mDrmMetadata(drmMetadata), mIndex(0) {}
+
+ public:
+ KeyIterator(const KeyIterator& keyIterator);
+ KeyIterator& operator=(const KeyIterator& keyIterator);
+ virtual ~KeyIterator() {}
+
+ public:
+ bool hasNext();
+ const String8& next();
+
+ private:
+ DrmMetadata* mDrmMetadata;
+ unsigned int mIndex;
+ };
+
+ /**
+ * Iterator for constraints
+ */
+ class Iterator {
+ friend class DrmMetadata;
+ private:
+ Iterator(DrmMetadata* drmMetadata) : mDrmMetadata(drmMetadata), mIndex(0) {}
+
+ public:
+ Iterator(const Iterator& iterator);
+ Iterator& operator=(const Iterator& iterator);
+ virtual ~Iterator() {}
+
+ public:
+ bool hasNext();
+ String8 next();
+
+ private:
+ DrmMetadata* mDrmMetadata;
+ unsigned int mIndex;
+ };
+
+public:
+ DrmMetadata() {}
+ virtual ~DrmMetadata() {
+ DrmMetadata::KeyIterator keyIt = this->keyIterator();
+
+ while (keyIt.hasNext()) {
+ String8 key = keyIt.next();
+ const char* value = this->getAsByteArray(&key);
+ if (NULL != value) {
+ delete[] value;
+ value = NULL;
+ }
+ }
+ mMetadataMap.clear();
+ }
+
+public:
+ int getCount(void) const;
+ status_t put(const String8* key, const char* value);
+ String8 get(const String8& key) const;
+ const char* getAsByteArray(const String8* key) const;
+ KeyIterator keyIterator();
+ Iterator iterator();
+
+private:
+ const char* getValue(const String8* key) const;
+
+private:
+ typedef KeyedVector<String8, const char*> DrmMetadataMap;
+ DrmMetadataMap mMetadataMap;
+};
+
+};
+
+#endif /* __DRM_METADATA_H__ */
+
diff --git a/include/drm/DrmRights.h b/include/drm/DrmRights.h
new file mode 100644
index 0000000..11f8f78
--- /dev/null
+++ b/include/drm/DrmRights.h
@@ -0,0 +1,106 @@
+/*
+ * 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 __DRM_RIGHTS_H__
+#define __DRM_RIGHTS_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which wraps the license information which was
+ * retrieved from the online DRM server.
+ *
+ * Caller can instantiate DrmRights by invoking DrmRights(const DrmBuffer&, String)
+ * constructor by using the result of DrmManagerClient::ProcessDrmInfo(const DrmInfo*) API.
+ * Caller can also instantiate DrmRights using the file path which contains rights information.
+ *
+ */
+class DrmRights {
+public:
+ /**
+ * Constructor for DrmRights
+ *
+ * @param[in] rightsFilePath Path of the file containing rights data
+ * @param[in] mimeType MIME type
+ * @param[in] accountId Account Id of the user
+ * @param[in] subscriptionId Subscription Id of the user
+ */
+ DrmRights(
+ const String8& rightsFilePath, const String8& mimeType,
+ const String8& accountId = String8("_NO_USER"),
+ const String8& subscriptionId = String8(""));
+
+ /**
+ * Constructor for DrmRights
+ *
+ * @param[in] rightsData Rights data
+ * @param[in] mimeType MIME type
+ * @param[in] accountId Account Id of the user
+ * @param[in] subscriptionId Subscription Id of the user
+ */
+ DrmRights(
+ const DrmBuffer& rightsData, const String8& mimeType,
+ const String8& accountId = String8("_NO_USER"),
+ const String8& subscriptionId = String8(""));
+
+ /**
+ * Destructor for DrmRights
+ */
+ virtual ~DrmRights();
+
+public:
+ /**
+ * Returns the rights data associated with this instance
+ *
+ * @return Rights data
+ */
+ const DrmBuffer& getData(void) const;
+
+ /**
+ * Returns MIME type associated with this instance
+ *
+ * @return MIME type
+ */
+ String8 getMimeType(void) const;
+
+ /**
+ * Returns the account-id associated with this instance
+ *
+ * @return Account Id
+ */
+ String8 getAccountId(void) const;
+
+ /**
+ * Returns the subscription-id associated with this object
+ *
+ * @return Subscription Id
+ */
+ String8 getSubscriptionId(void) const;
+
+private:
+ DrmBuffer mData;
+ String8 mMimeType;
+ String8 mAccountId;
+ String8 mSubscriptionId;
+ char* mRightsFromFile;
+};
+
+};
+
+#endif /* __DRM_RIGHTS_H__ */
+
diff --git a/include/drm/DrmSupportInfo.h b/include/drm/DrmSupportInfo.h
new file mode 100644
index 0000000..bf12b0b
--- /dev/null
+++ b/include/drm/DrmSupportInfo.h
@@ -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.
+ */
+
+#ifndef __DRM_SUPPORT_INFO_H__
+#define __DRM_SUPPORT_INFO_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which wraps the capability of each plug-in,
+ * such as mimetype's and file suffixes it could handle.
+ *
+ * Plug-in developer could return the capability of the plugin by passing
+ * DrmSupportInfo instance.
+ *
+ */
+class DrmSupportInfo {
+public:
+ /**
+ * Iterator for mMimeTypeVector
+ */
+ class MimeTypeIterator {
+ friend class DrmSupportInfo;
+ private:
+ MimeTypeIterator(DrmSupportInfo* drmSupportInfo)
+ : mDrmSupportInfo(drmSupportInfo), mIndex(0) {}
+ public:
+ MimeTypeIterator(const MimeTypeIterator& iterator);
+ MimeTypeIterator& operator=(const MimeTypeIterator& iterator);
+ virtual ~MimeTypeIterator() {}
+
+ public:
+ bool hasNext();
+ String8& next();
+
+ private:
+ DrmSupportInfo* mDrmSupportInfo;
+ unsigned int mIndex;
+ };
+
+ /**
+ * Iterator for mFileSuffixVector
+ */
+ class FileSuffixIterator {
+ friend class DrmSupportInfo;
+
+ private:
+ FileSuffixIterator(DrmSupportInfo* drmSupportInfo)
+ : mDrmSupportInfo(drmSupportInfo), mIndex(0) {}
+ public:
+ FileSuffixIterator(const FileSuffixIterator& iterator);
+ FileSuffixIterator& operator=(const FileSuffixIterator& iterator);
+ virtual ~FileSuffixIterator() {}
+
+ public:
+ bool hasNext();
+ String8& next();
+
+ private:
+ DrmSupportInfo* mDrmSupportInfo;
+ unsigned int mIndex;
+ };
+
+public:
+ /**
+ * Constructor for DrmSupportInfo
+ */
+ DrmSupportInfo();
+
+ /**
+ * Copy constructor for DrmSupportInfo
+ */
+ DrmSupportInfo(const DrmSupportInfo& drmSupportInfo);
+
+ /**
+ * Destructor for DrmSupportInfo
+ */
+ virtual ~DrmSupportInfo() {}
+
+ DrmSupportInfo& operator=(const DrmSupportInfo& drmSupportInfo);
+ bool operator<(const DrmSupportInfo& drmSupportInfo) const;
+ bool operator==(const DrmSupportInfo& drmSupportInfo) const;
+
+ /**
+ * Returns FileSuffixIterator object to walk through file suffix values
+ * associated with this instance
+ *
+ * @return FileSuffixIterator object
+ */
+ FileSuffixIterator getFileSuffixIterator();
+
+ /**
+ * Returns MimeTypeIterator object to walk through mimetype values
+ * associated with this instance
+ *
+ * @return MimeTypeIterator object
+ */
+ MimeTypeIterator getMimeTypeIterator();
+
+public:
+ /**
+ * Returns the number of mimetypes supported.
+ *
+ * @return Number of mimetypes supported
+ */
+ int getMimeTypeCount(void) const;
+
+ /**
+ * Returns the number of file types supported.
+ *
+ * @return Number of file types supported
+ */
+ int getFileSuffixCount(void) const;
+
+ /**
+ * Adds the mimetype to the list of supported mimetypes
+ *
+ * @param[in] mimeType mimetype to be added
+ * @return Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t addMimeType(const String8& mimeType);
+
+ /**
+ * Adds the filesuffix to the list of supported file types
+ *
+ * @param[in] filesuffix file suffix to be added
+ * @return Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t addFileSuffix(const String8& fileSuffix);
+
+ /**
+ * Set the unique description about the plugin
+ *
+ * @param[in] description Unique description
+ * @return Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+ status_t setDescription(const String8& description);
+
+ /**
+ * Returns the unique description associated with the plugin
+ *
+ * @return Unique description
+ */
+ String8 getDescription() const;
+
+ /**
+ * Returns whether given mimetype is supported or not
+ *
+ * @param[in] mimeType MIME type
+ * @return
+ * true - if mime-type is supported
+ * false - if mime-type is not supported
+ */
+ bool isSupportedMimeType(const String8& mimeType) const;
+
+ /**
+ * Returns whether given file type is supported or not
+ *
+ * @param[in] fileType File type
+ * @return
+ * true if file type is supported
+ * false if file type is not supported
+ */
+ bool isSupportedFileSuffix(const String8& fileType) const;
+
+private:
+ Vector<String8> mMimeTypeVector;
+ Vector<String8> mFileSuffixVector;
+
+ String8 mDescription;
+};
+
+};
+
+#endif /* __DRM_SUPPORT_INFO_H__ */
+
diff --git a/include/drm/drm_framework_common.h b/include/drm/drm_framework_common.h
new file mode 100644
index 0000000..c5765a9
--- /dev/null
+++ b/include/drm/drm_framework_common.h
@@ -0,0 +1,300 @@
+/*
+ * 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 __DRM_FRAMEWORK_COMMON_H__
+#define __DRM_FRAMEWORK_COMMON_H__
+
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/Errors.h>
+
+#define INVALID_VALUE -1
+
+namespace android {
+
+/**
+ * Error code for DRM Frameowrk
+ */
+enum {
+ DRM_ERROR_BASE = -2000,
+
+ DRM_ERROR_UNKNOWN = DRM_ERROR_BASE,
+ DRM_ERROR_LICENSE_EXPIRED = DRM_ERROR_BASE - 1,
+ DRM_ERROR_SESSION_NOT_OPENED = DRM_ERROR_BASE - 2,
+ DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED = DRM_ERROR_BASE - 3,
+ DRM_ERROR_DECRYPT = DRM_ERROR_BASE - 4,
+ DRM_ERROR_CANNOT_HANDLE = DRM_ERROR_BASE - 5,
+
+ DRM_NO_ERROR = NO_ERROR
+};
+
+/**
+ * Defines DRM Buffer
+ */
+class DrmBuffer {
+public:
+ char* data;
+ int length;
+
+ DrmBuffer() :
+ data(NULL),
+ length(0) {
+ }
+
+ DrmBuffer(char* dataBytes, int dataLength) :
+ data(dataBytes),
+ length(dataLength) {
+ }
+
+};
+
+/**
+ * Defines detailed description of the action
+ */
+class ActionDescription {
+public:
+ ActionDescription(int _outputType, int _configuration) :
+ outputType(_outputType),
+ configuration(_configuration) {
+ }
+
+public:
+ int outputType; /* BLUETOOTH , HDMI*/
+ int configuration; /* RESOLUTION_720_480 , RECORDABLE etc.*/
+};
+
+/**
+ * Defines constants related to DRM types
+ */
+class DrmObjectType {
+private:
+ DrmObjectType();
+
+public:
+ /**
+ * Field specifies the unknown type
+ */
+ static const int UNKNOWN = 0x00;
+ /**
+ * Field specifies the protected content type
+ */
+ static const int CONTENT = 0x01;
+ /**
+ * Field specifies the rights information
+ */
+ static const int RIGHTS_OBJECT = 0x02;
+ /**
+ * Field specifies the trigger information
+ */
+ static const int TRIGGER_OBJECT = 0x03;
+};
+
+/**
+ * Defines constants related to play back
+ */
+class Playback {
+private:
+ Playback();
+
+public:
+ /**
+ * Constant field signifies playback start
+ */
+ static const int START = 0x00;
+ /**
+ * Constant field signifies playback stop
+ */
+ static const int STOP = 0x01;
+ /**
+ * Constant field signifies playback paused
+ */
+ static const int PAUSE = 0x02;
+ /**
+ * Constant field signifies playback resumed
+ */
+ static const int RESUME = 0x03;
+};
+
+/**
+ * Defines actions that can be performed on protected content
+ */
+class Action {
+private:
+ Action();
+
+public:
+ /**
+ * Constant field signifies that the default action
+ */
+ static const int DEFAULT = 0x00;
+ /**
+ * Constant field signifies that the content can be played
+ */
+ static const int PLAY = 0x01;
+ /**
+ * Constant field signifies that the content can be set as ring tone
+ */
+ static const int RINGTONE = 0x02;
+ /**
+ * Constant field signifies that the content can be transfered
+ */
+ static const int TRANSFER = 0x03;
+ /**
+ * Constant field signifies that the content can be set as output
+ */
+ static const int OUTPUT = 0x04;
+ /**
+ * Constant field signifies that preview is allowed
+ */
+ static const int PREVIEW = 0x05;
+ /**
+ * Constant field signifies that the content can be executed
+ */
+ static const int EXECUTE = 0x06;
+ /**
+ * Constant field signifies that the content can displayed
+ */
+ static const int DISPLAY = 0x07;
+};
+
+/**
+ * Defines constants related to status of the rights
+ */
+class RightsStatus {
+private:
+ RightsStatus();
+
+public:
+ /**
+ * Constant field signifies that the rights are valid
+ */
+ static const int RIGHTS_VALID = 0x00;
+ /**
+ * Constant field signifies that the rights are invalid
+ */
+ static const int RIGHTS_INVALID = 0x01;
+ /**
+ * Constant field signifies that the rights are expired for the content
+ */
+ static const int RIGHTS_EXPIRED = 0x02;
+ /**
+ * Constant field signifies that the rights are not acquired for the content
+ */
+ static const int RIGHTS_NOT_ACQUIRED = 0x03;
+};
+
+/**
+ * Defines API set for decryption
+ */
+class DecryptApiType {
+private:
+ DecryptApiType();
+
+public:
+ /**
+ * Decrypt API set for non encrypted content
+ */
+ static const int NON_ENCRYPTED = 0x00;
+ /**
+ * Decrypt API set for ES based DRM
+ */
+ static const int ELEMENTARY_STREAM_BASED = 0x01;
+ /**
+ * POSIX based Decrypt API set for container based DRM
+ */
+ static const int CONTAINER_BASED = 0x02;
+};
+
+/**
+ * Defines decryption information
+ */
+class DecryptInfo {
+public:
+ /**
+ * size of memory to be allocated to get the decrypted content.
+ */
+ int decryptBufferLength;
+ /**
+ * reserved for future purpose
+ */
+};
+
+/**
+ * Defines decryption handle
+ */
+class DecryptHandle {
+public:
+ /**
+ * Decryption session Handle
+ */
+ int decryptId;
+ /**
+ * Mimetype of the content to be used to select the media extractor
+ * For e.g., "video/mpeg" or "audio/mp3"
+ */
+ String8 mimeType;
+ /**
+ * Defines which decryption pattern should be used to decrypt the given content
+ * DrmFramework provides two different set of decryption APIs.
+ * 1. Decrypt APIs for elementary stream based DRM
+ * (file format is not encrypted but ES is encrypted)
+ * e.g., Marlin DRM (MP4 file format), WM-DRM (asf file format)
+ *
+ * DecryptApiType::ELEMENTARY_STREAM_BASED
+ * Decryption API set for ES based DRM
+ * initializeDecryptUnit(), decrypt(), and finalizeDecryptUnit()
+ * 2. Decrypt APIs for container based DRM (file format itself is encrypted)
+ * e.g., OMA DRM (dcf file format)
+ *
+ * DecryptApiType::CONTAINER_BASED
+ * POSIX based Decryption API set for container based DRM
+ * pread()
+ */
+ int decryptApiType;
+ /**
+ * Defines the status of the rights like
+ * RIGHTS_VALID, RIGHTS_INVALID, RIGHTS_EXPIRED or RIGHTS_NOT_ACQUIRED
+ */
+ int status;
+ /**
+ * Information required to decrypt content
+ * e.g. size of memory to be allocated to get the decrypted content.
+ */
+ DecryptInfo* decryptInfo;
+
+public:
+ DecryptHandle():
+ decryptId(INVALID_VALUE),
+ mimeType(""),
+ decryptApiType(INVALID_VALUE),
+ status(INVALID_VALUE) {
+
+ }
+
+ bool operator<(const DecryptHandle& handle) const {
+ return (decryptId < handle.decryptId);
+ }
+
+ bool operator==(const DecryptHandle& handle) const {
+ return (decryptId == handle.decryptId);
+ }
+};
+
+};
+
+#endif /* __DRM_FRAMEWORK_COMMON_H__ */
+
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
new file mode 100644
index 0000000..54ce789
--- /dev/null
+++ b/include/gui/SurfaceTexture.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_SURFACETEXTURE_H
+#define ANDROID_GUI_SURFACETEXTURE_H
+
+namespace android {
+
+struct SurfaceTexture {
+ struct FrameAvailableListener : public virtual RefBase {};
+
+ SurfaceTexture(GLuint) {}
+ void updateTexImage() {}
+ void decStrong(android::sp<android::SurfaceTexture>* const) {}
+ void incStrong(android::sp<android::SurfaceTexture>* const) {}
+ void getTransformMatrix(float mtx[16]) {}
+ void setFrameAvailableListener(const sp<FrameAvailableListener>&) {}
+};
+
+}
+
+#endif
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java b/include/gui/SurfaceTextureClient.h
similarity index 63%
copy from telephony/java/com/android/internal/telephony/gsm/stk/StkException.java
copy to include/gui/SurfaceTextureClient.h
index 86de366..a83756e 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java
+++ b/include/gui/SurfaceTextureClient.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,17 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+#ifndef ANDROID_GUI_SURFACETEXTURECLIENT_H
+#define ANDROID_GUI_SURFACETEXTURECLIENT_H
-import android.util.AndroidException;
+#include <ui/egl/android_natives.h>
+namespace android {
-/**
- * Base class for all the exceptions in STK service.
- *
- * {@hide}
- */
-class StkException extends AndroidException {
- public StkException() {
- super();
- }
+struct SurfaceTextureClient : public ANativeWindow {
+ SurfaceTextureClient(const sp<SurfaceTexture>&){}
+};
+
}
+
+#endif
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index a3da3ed..d0b9fcd 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -25,6 +25,7 @@
#include <utils/List.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
+#include <drm/DrmManagerClient.h>
namespace android {
@@ -73,6 +74,13 @@
static void RegisterSniffer(SnifferFunc func);
static void RegisterDefaultSniffers();
+ // for DRM
+ virtual DecryptHandle* DrmInitialization(DrmManagerClient *client) {
+ return NULL;
+ }
+ virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {};
+
+
protected:
virtual ~DataSource() {}
diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h
index 8a215ea..4307263 100644
--- a/include/media/stagefright/FileSource.h
+++ b/include/media/stagefright/FileSource.h
@@ -23,6 +23,7 @@
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaErrors.h>
#include <utils/threads.h>
+#include <drm/DrmManagerClient.h>
namespace android {
@@ -37,15 +38,29 @@
virtual status_t getSize(off_t *size);
+ virtual DecryptHandle* DrmInitialization(DrmManagerClient *client);
+
+ virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client);
+
protected:
virtual ~FileSource();
private:
FILE *mFile;
+ int mFd;
int64_t mOffset;
int64_t mLength;
Mutex mLock;
+ /*for DRM*/
+ DecryptHandle *mDecryptHandle;
+ DrmManagerClient *mDrmManagerClient;
+ int64_t mDrmBufOffset;
+ int64_t mDrmBufSize;
+ unsigned char *mDrmBuf;
+
+ ssize_t readAtDRM(off_t offset, void *data, size_t size);
+
FileSource(const FileSource &);
FileSource &operator=(const FileSource &);
};
diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h
index e44122d..6df4d86 100644
--- a/include/media/stagefright/MediaErrors.h
+++ b/include/media/stagefright/MediaErrors.h
@@ -40,6 +40,8 @@
// Not technically an error.
INFO_FORMAT_CHANGED = MEDIA_ERROR_BASE - 12,
INFO_DISCONTINUITY = MEDIA_ERROR_BASE - 13,
+
+ ERROR_NO_LICENSE = MEDIA_ERROR_BASE - 14,
};
} // namespace android
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
index 16b0a4c..a82106e 100644
--- a/include/media/stagefright/MediaExtractor.h
+++ b/include/media/stagefright/MediaExtractor.h
@@ -55,6 +55,12 @@
// CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE
virtual uint32_t flags() const;
+ // for DRM
+ virtual void setDrmFlag(bool flag) {};
+ virtual char* getDrmTrackInfo(size_t trackID, int *len) {
+ return NULL;
+ }
+
protected:
MediaExtractor() {}
virtual ~MediaExtractor() {}
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index bffb9e0..ea2fa52 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -58,6 +58,8 @@
kKeyBufferID = 'bfID',
kKeyMaxInputSize = 'inpS',
kKeyThumbnailTime = 'thbT', // int64_t (usecs)
+ kKeyTrackID = 'trID',
+ kKeyIsDRM = 'idrm', // int32_t (bool)
kKeyAlbum = 'albu', // cstring
kKeyArtist = 'arti', // cstring
diff --git a/include/ui/FramebufferNativeWindow.h b/include/ui/FramebufferNativeWindow.h
index c913355..2cd0911 100644
--- a/include/ui/FramebufferNativeWindow.h
+++ b/include/ui/FramebufferNativeWindow.h
@@ -29,6 +29,7 @@
#include <ui/egl/android_natives.h>
+#define NUM_FRAME_BUFFERS 2
extern "C" EGLNativeWindowType android_createDisplaySurface(void);
@@ -72,7 +73,7 @@
framebuffer_device_t* fbDev;
alloc_device_t* grDev;
- sp<NativeBuffer> buffers[2];
+ sp<NativeBuffer> buffers[NUM_FRAME_BUFFERS];
sp<NativeBuffer> front;
mutable Mutex mutex;
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 8c6018b..4e809d6 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -38,6 +38,15 @@
AKEY_EVENT_FLAG_START_TRACKING = 0x40000000
};
+enum {
+ /*
+ * Indicates that an input device has switches.
+ * This input source flag is hidden from the API because switches are only used by the system
+ * and applications have no way to interact with them.
+ */
+ AINPUT_SOURCE_SWITCH = 0x80000000,
+};
+
/*
* Maximum number of pointers supported per motion event.
* Smallest number of pointers is 1.
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java b/include/utils/Functor.h
similarity index 64%
copy from telephony/java/com/android/internal/telephony/gsm/stk/StkException.java
copy to include/utils/Functor.h
index 86de366..56a7557 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java
+++ b/include/utils/Functor.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,14 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+#ifndef ANDROID_FUNCTOR_H
+#define ANDROID_FUNCTOR_H
-import android.util.AndroidException;
+namespace android {
+struct Functor{};
-/**
- * Base class for all the exceptions in STK service.
- *
- * {@hide}
- */
-class StkException extends AndroidException {
- public StkException() {
- super();
- }
}
+
+#endif
+
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index da86da4..ab7b973 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -1983,6 +1983,7 @@
#ifndef HAVE_ANDROID_OS
void print(bool inclValues) const;
+ static String8 normalizeForOutput(const char* input);
#endif
private:
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index f329ac4..d57f2c9 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -619,7 +619,10 @@
status_t Parcel::writeString8(const String8& str)
{
status_t err = writeInt32(str.bytes());
- if (err == NO_ERROR) {
+ // only write string if its length is more than zero characters,
+ // as readString8 will only read if the length field is non-zero.
+ // this is slightly different from how writeString16 works.
+ if (str.bytes() > 0 && err == NO_ERROR) {
err = write(str.string(), str.bytes()+1);
}
return err;
diff --git a/libs/rs/rsNoise.cpp b/libs/rs/rsNoise.cpp
index 764dc1a..4b67586 100644
--- a/libs/rs/rsNoise.cpp
+++ b/libs/rs/rsNoise.cpp
@@ -253,4 +253,4 @@
}
}
-}
\ No newline at end of file
+}
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index a36d555..04a0195 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -83,6 +83,7 @@
if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) {
int stride;
int err;
+ int i;
err = framebuffer_open(module, &fbDev);
LOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));
@@ -96,27 +97,33 @@
mUpdateOnDemand = (fbDev->setUpdateRect != 0);
// initialize the buffer FIFO
- mNumBuffers = 2;
- mNumFreeBuffers = 2;
+ mNumBuffers = NUM_FRAME_BUFFERS;
+ mNumFreeBuffers = NUM_FRAME_BUFFERS;
mBufferHead = mNumBuffers-1;
- buffers[0] = new NativeBuffer(
- fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);
- buffers[1] = new NativeBuffer(
- fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);
-
- err = grDev->alloc(grDev,
- fbDev->width, fbDev->height, fbDev->format,
- GRALLOC_USAGE_HW_FB, &buffers[0]->handle, &buffers[0]->stride);
- LOGE_IF(err, "fb buffer 0 allocation failed w=%d, h=%d, err=%s",
- fbDev->width, fbDev->height, strerror(-err));
+ for (i = 0; i < mNumBuffers; i++)
+ {
+ buffers[i] = new NativeBuffer(
+ fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);
+ }
- err = grDev->alloc(grDev,
- fbDev->width, fbDev->height, fbDev->format,
- GRALLOC_USAGE_HW_FB, &buffers[1]->handle, &buffers[1]->stride);
+ for (i = 0; i < mNumBuffers; i++)
+ {
+ err = grDev->alloc(grDev,
+ fbDev->width, fbDev->height, fbDev->format,
+ GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride);
- LOGE_IF(err, "fb buffer 1 allocation failed w=%d, h=%d, err=%s",
- fbDev->width, fbDev->height, strerror(-err));
+ LOGE_IF(err, "fb buffer %d allocation failed w=%d, h=%d, err=%s",
+ i, fbDev->width, fbDev->height, strerror(-err));
+
+ if (err)
+ {
+ mNumBuffers = i;
+ mNumFreeBuffers = i;
+ mBufferHead = mNumBuffers-1;
+ break;
+ }
+ }
const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags;
const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi;
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 83b382b..34e44e4 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -824,7 +824,7 @@
}
uint32_t SwitchInputMapper::getSources() {
- return 0;
+ return AINPUT_SOURCE_SWITCH;
}
void SwitchInputMapper::process(const RawEvent* rawEvent) {
diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp
index 3aa8950..b082c53 100644
--- a/libs/ui/Overlay.cpp
+++ b/libs/ui/Overlay.cpp
@@ -96,7 +96,6 @@
}
void Overlay::destroy() {
- if (mStatus != NO_ERROR) return;
// Must delete the objects in reverse creation order, thus the
// data side must be closed first and then the destroy send to
@@ -104,9 +103,15 @@
if (mOverlayData) {
overlay_data_close(mOverlayData);
mOverlayData = NULL;
+ } else {
+ LOGD("Overlay::destroy mOverlayData is NULL");
}
- mOverlayRef->mOverlayChannel->destroy();
+ if (mOverlayRef != 0) {
+ mOverlayRef->mOverlayChannel->destroy();
+ } else {
+ LOGD("Overlay::destroy mOverlayRef is NULL");
+ }
}
status_t Overlay::getStatus() const {
diff --git a/libs/ui/tests/InputReader_test.cpp b/libs/ui/tests/InputReader_test.cpp
index f31a6be..09d1680 100644
--- a/libs/ui/tests/InputReader_test.cpp
+++ b/libs/ui/tests/InputReader_test.cpp
@@ -1368,7 +1368,7 @@
SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
addMapperAndConfigure(mapper);
- ASSERT_EQ(uint32_t(0), mapper->getSources());
+ ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper->getSources());
}
TEST_F(SwitchInputMapperTest, GetSwitchState) {
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 8345cc3..7fb7ae3 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -4038,6 +4038,38 @@
}
}
+// Normalize a string for output
+String8 ResTable::normalizeForOutput( const char *input )
+{
+ String8 ret;
+ char buff[2];
+ buff[1] = '\0';
+
+ while (*input != '\0') {
+ switch (*input) {
+ // All interesting characters are in the ASCII zone, so we are making our own lives
+ // easier by scanning the string one byte at a time.
+ case '\\':
+ ret += "\\\\";
+ break;
+ case '\n':
+ ret += "\\n";
+ break;
+ case '"':
+ ret += "\\\"";
+ break;
+ default:
+ buff[0] = *input;
+ ret += buff;
+ break;
+ }
+
+ input++;
+ }
+
+ return ret;
+}
+
void ResTable::print_value(const Package* pkg, const Res_value& value) const
{
if (value.dataType == Res_value::TYPE_NULL) {
@@ -4051,13 +4083,13 @@
const char* str8 = pkg->header->values.string8At(
value.data, &len);
if (str8 != NULL) {
- printf("(string8) \"%s\"\n", str8);
+ printf("(string8) \"%s\"\n", normalizeForOutput(str8).string());
} else {
const char16_t* str16 = pkg->header->values.stringAt(
value.data, &len);
if (str16 != NULL) {
printf("(string16) \"%s\"\n",
- String8(str16, len).string());
+ normalizeForOutput(String8(str16, len).string()).string());
} else {
printf("(string) null\n");
}
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index d3a71b3..ffc3346 100755
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -28,6 +28,9 @@
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.R;
+import com.android.internal.telephony.GsmAlphabet;
+
/**
* A GPS Network-initiated Handler class used by LocationManager.
*
@@ -182,8 +185,8 @@
return;
}
- String title = getNotifTitle(notif);
- String message = getNotifMessage(notif);
+ String title = getNotifTitle(notif, mContext);
+ String message = getNotifMessage(notif, mContext);
if (DEBUG) Log.d(TAG, "setNiNotification, notifyId: " + notif.notificationId +
", title: " + title +
@@ -203,7 +206,7 @@
}
mNiNotification.flags = Notification.FLAG_ONGOING_EVENT;
- mNiNotification.tickerText = getNotifTicker(notif);
+ mNiNotification.tickerText = getNotifTicker(notif, mContext);
// if not to popup dialog immediately, pending intent will open the dialog
Intent intent = !mPopupImmediately ? getDlgIntent(notif) : new Intent();
@@ -234,8 +237,8 @@
private Intent getDlgIntent(GpsNiNotification notif)
{
Intent intent = new Intent();
- String title = getDialogTitle(notif);
- String message = getDialogMessage(notif);
+ String title = getDialogTitle(notif, mContext);
+ String message = getDialogMessage(notif, mContext);
// directly bring up the NI activity
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -286,58 +289,32 @@
*/
static String decodeGSMPackedString(byte[] input)
{
- final char CHAR_CR = 0x0D;
- int nStridx = 0;
- int nPckidx = 0;
- int num_bytes = input.length;
- int cPrev = 0;
- int cCurr = 0;
- byte nShift;
- byte nextChar;
- byte[] stringBuf = new byte[input.length * 2];
- String result = "";
+ final char PADDING_CHAR = 0x00;
+ int lengthBytes = input.length;
+ int lengthSeptets = (lengthBytes * 8) / 7;
+ String decoded;
- while(nPckidx < num_bytes)
- {
- nShift = (byte) (nStridx & 0x07);
- cCurr = input[nPckidx++];
- if (cCurr < 0) cCurr += 256;
-
- /* A 7-bit character can be split at the most between two bytes of packed
- ** data.
- */
- nextChar = (byte) (( (cCurr << nShift) | (cPrev >> (8-nShift)) ) & 0x7F);
- stringBuf[nStridx++] = nextChar;
-
- /* Special case where the whole of the next 7-bit character fits inside
- ** the current byte of packed data.
- */
- if(nShift == 6)
- {
- /* If the next 7-bit character is a CR (0x0D) and it is the last
- ** character, then it indicates a padding character. Drop it.
- */
- if (nPckidx == num_bytes || (cCurr >> 1) == CHAR_CR)
- {
- break;
+ /* Special case where the last 7 bits in the last byte could hold a valid
+ * 7-bit character or a padding character. Drop the last 7-bit character
+ * if it is a padding character.
+ */
+ if (lengthBytes % 7 == 0) {
+ if (lengthBytes > 0) {
+ if ((input[lengthBytes - 1] >> 1) == PADDING_CHAR) {
+ lengthSeptets = lengthSeptets - 1;
}
-
- nextChar = (byte) (cCurr >> 1);
- stringBuf[nStridx++] = nextChar;
}
-
- cPrev = cCurr;
}
- try {
- result = new String(stringBuf, 0, nStridx, "US-ASCII");
- }
- catch (UnsupportedEncodingException e)
- {
- Log.e(TAG, e.getMessage());
+ decoded = GsmAlphabet.gsm7BitPackedToString(input, 0, lengthSeptets);
+
+ // Return "" if decoding of GSM packed string fails
+ if (null == decoded) {
+ Log.e(TAG, "Decoding of GSM packed string failed");
+ decoded = "";
}
- return result;
+ return decoded;
}
static String decodeUTF8String(byte[] input)
@@ -412,41 +389,40 @@
}
// change this to configure notification display
- static private String getNotifTicker(GpsNiNotification notif)
+ static private String getNotifTicker(GpsNiNotification notif, Context context)
{
- String ticker = String.format("Position request! ReqId: [%s] ClientName: [%s]",
+ String ticker = String.format(context.getString(R.string.gpsNotifTicker),
decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
decodeString(notif.text, mIsHexInput, notif.textEncoding));
return ticker;
}
// change this to configure notification display
- static private String getNotifTitle(GpsNiNotification notif)
+ static private String getNotifTitle(GpsNiNotification notif, Context context)
{
- String title = String.format("Position Request");
+ String title = String.format(context.getString(R.string.gpsNotifTitle));
return title;
}
// change this to configure notification display
- static private String getNotifMessage(GpsNiNotification notif)
+ static private String getNotifMessage(GpsNiNotification notif, Context context)
{
- String message = String.format(
- "NI Request received from [%s] for client [%s]!",
+ String message = String.format(context.getString(R.string.gpsNotifMessage),
decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
decodeString(notif.text, mIsHexInput, notif.textEncoding));
return message;
}
// change this to configure dialog display (for verification)
- static public String getDialogTitle(GpsNiNotification notif)
+ static public String getDialogTitle(GpsNiNotification notif, Context context)
{
- return getNotifTitle(notif);
+ return getNotifTitle(notif, context);
}
// change this to configure dialog display (for verification)
- static private String getDialogMessage(GpsNiNotification notif)
+ static private String getDialogMessage(GpsNiNotification notif, Context context)
{
- return getNotifMessage(notif);
+ return getNotifMessage(notif, context);
}
}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index d9f2302..4e87c73 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -316,7 +316,7 @@
degrees != 270) {
throw new IllegalArgumentException("Unsupported angle: " + degrees);
}
- setParameter(String.format("video-param-rotation-angle-degrees=%d", degrees));
+ setParameter("video-param-rotation-angle-degrees=" + degrees);
}
/**
@@ -435,7 +435,7 @@
if (samplingRate <= 0) {
throw new IllegalArgumentException("Audio sampling rate is not positive");
}
- setParameter(String.format("audio-param-sampling-rate=%d", samplingRate));
+ setParameter("audio-param-sampling-rate=" + samplingRate);
}
/**
@@ -450,7 +450,7 @@
if (numChannels <= 0) {
throw new IllegalArgumentException("Number of channels is not positive");
}
- setParameter(String.format("audio-param-number-of-channels=%d", numChannels));
+ setParameter("audio-param-number-of-channels=" + numChannels);
}
/**
@@ -466,7 +466,7 @@
if (bitRate <= 0) {
throw new IllegalArgumentException("Audio encoding bit rate is not positive");
}
- setParameter(String.format("audio-param-encoding-bitrate=%d", bitRate));
+ setParameter("audio-param-encoding-bitrate=" + bitRate);
}
/**
@@ -482,7 +482,7 @@
if (bitRate <= 0) {
throw new IllegalArgumentException("Video encoding bit rate is not positive");
}
- setParameter(String.format("video-param-encoding-bitrate=%d", bitRate));
+ setParameter("video-param-encoding-bitrate=" + bitRate);
}
/**
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 47e1058..ab2c6ea 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1220,8 +1220,12 @@
prescan(path);
File file = new File(path);
+
+ // lastModified is in milliseconds on Files.
+ long lastModifiedSeconds = file.lastModified() / 1000;
+
// always scan the file, so we can return the content://media Uri for existing files
- return mClient.doScanFile(path, mimeType, file.lastModified(), file.length(), true);
+ return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(), true);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
return null;
diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp
index 6f581d3..c5112a5 100644
--- a/media/libmedia/MediaScanner.cpp
+++ b/media/libmedia/MediaScanner.cpp
@@ -81,13 +81,13 @@
}
static bool fileMatchesExtension(const char* path, const char* extensions) {
- char* extension = strrchr(path, '.');
+ const char* extension = strrchr(path, '.');
if (!extension) return false;
++extension; // skip the dot
if (extension[0] == 0) return false;
while (extensions[0]) {
- char* comma = strchr(extensions, ',');
+ const char* comma = strchr(extensions, ',');
size_t length = (comma ? comma - extensions : strlen(extensions));
if (length == strlen(extension) && strncasecmp(extension, extensions, length) == 0) return true;
extensions += length;
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 615f3e8..d674547 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -11,6 +11,7 @@
AwesomePlayer.cpp \
CameraSource.cpp \
DataSource.cpp \
+ DRMExtractor.cpp \
ESDS.cpp \
FileSource.cpp \
HTTPStream.cpp \
@@ -59,7 +60,8 @@
libsonivox \
libvorbisidec \
libsurfaceflinger_client \
- libcamera_client
+ libcamera_client \
+ libdrmframework
LOCAL_STATIC_LIBRARIES := \
libstagefright_aacdec \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index d99990b..6dc61c7 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -247,7 +247,8 @@
mExtractorFlags(0),
mLastVideoBuffer(NULL),
mVideoBuffer(NULL),
- mSuspensionState(NULL) {
+ mSuspensionState(NULL),
+ mDecryptHandle(NULL) {
CHECK_EQ(mClient.connect(), OK);
DataSource::RegisterDefaultSniffers();
@@ -346,6 +347,12 @@
return UNKNOWN_ERROR;
}
+ dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient);
+ if (mDecryptHandle != NULL
+ && RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+ }
+
return setDataSource_l(extractor);
}
@@ -421,6 +428,13 @@
}
void AwesomePlayer::reset_l() {
+ if (mDecryptHandle != NULL) {
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::STOP, 0);
+ mDecryptHandle = NULL;
+ mDrmManagerClient = NULL;
+ }
+
if (mFlags & PREPARING) {
mFlags |= PREPARE_CANCELLED;
if (mConnectingDataSource != NULL) {
@@ -761,6 +775,13 @@
bool deferredAudioSeek = false;
+ if (mDecryptHandle != NULL) {
+ int64_t position;
+ getPosition(&position);
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::START, position / 1000);
+ }
+
if (mAudioSource != NULL) {
if (mAudioPlayer == NULL) {
if (mAudioSink != NULL) {
@@ -778,6 +799,11 @@
mFlags &= ~(PLAYING | FIRST_FRAME);
+ if (mDecryptHandle != NULL) {
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::STOP, 0);
+ }
+
return err;
}
@@ -905,6 +931,11 @@
mFlags &= ~PLAYING;
+ if (mDecryptHandle != NULL) {
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::PAUSE, 0);
+ }
+
return OK;
}
@@ -1022,6 +1053,13 @@
mWatchForAudioSeekComplete = true;
mWatchForAudioEOS = true;
mSeekNotificationSent = false;
+
+ if (mDecryptHandle != NULL) {
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::PAUSE, 0);
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::START, mSeekTimeUs / 1000);
+ }
}
}
@@ -1254,6 +1292,13 @@
TimeSource *ts = (mFlags & AUDIO_AT_EOS) ? &mSystemTimeSource : mTimeSource;
+ if (mDecryptHandle != NULL) {
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::PAUSE, 0);
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::START, timeUs / 1000);
+ }
+
if (mFlags & FIRST_FRAME) {
mFlags &= ~FIRST_FRAME;
@@ -1665,6 +1710,12 @@
return UNKNOWN_ERROR;
}
+ dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient);
+ if (mDecryptHandle != NULL
+ && RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+ }
+
return setDataSource_l(extractor);
}
diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp
new file mode 100644
index 0000000..aa9ad23
--- /dev/null
+++ b/media/libstagefright/DRMExtractor.cpp
@@ -0,0 +1,303 @@
+/*
+ * 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 "include/DRMExtractor.h"
+#include "include/AMRExtractor.h"
+#include "include/MP3Extractor.h"
+#include "include/MPEG4Extractor.h"
+#include "include/WAVExtractor.h"
+#include "include/OggExtractor.h"
+
+#include <arpa/inet.h>
+#include <utils/String8.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDebug.h>
+
+#include <drm/drm_framework_common.h>
+#include <utils/Errors.h>
+
+
+namespace android {
+
+DrmManagerClient* gDrmManagerClient = NULL;
+
+class DRMSource : public MediaSource {
+public:
+ DRMSource(const sp<MediaSource> &mediaSource,
+ DecryptHandle* decryptHandle, int32_t trackId, DrmBuffer* ipmpBox);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+ virtual ~DRMSource();
+
+private:
+ sp<MediaSource> mOriginalMediaSource;
+ DecryptHandle* mDecryptHandle;
+ size_t mTrackId;
+ mutable Mutex mDRMLock;
+ size_t mNALLengthSize;
+ bool mWantsNALFragments;
+
+ DRMSource(const DRMSource &);
+ DRMSource &operator=(const DRMSource &);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+DRMSource::DRMSource(const sp<MediaSource> &mediaSource,
+ DecryptHandle* decryptHandle, int32_t trackId, DrmBuffer* ipmpBox)
+ : mOriginalMediaSource(mediaSource),
+ mDecryptHandle(decryptHandle),
+ mTrackId(trackId),
+ mNALLengthSize(0),
+ mWantsNALFragments(false) {
+ gDrmManagerClient->initializeDecryptUnit(
+ mDecryptHandle, trackId, ipmpBox);
+
+ const char *mime;
+ bool success = getFormat()->findCString(kKeyMIMEType, &mime);
+ CHECK(success);
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ CHECK(getFormat()->findData(kKeyAVCC, &type, &data, &size));
+
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size >= 7);
+ CHECK_EQ(ptr[0], 1); // configurationVersion == 1
+
+ // The number of bytes used to encode the length of a NAL unit.
+ mNALLengthSize = 1 + (ptr[4] & 3);
+ }
+}
+
+DRMSource::~DRMSource() {
+ Mutex::Autolock autoLock(mDRMLock);
+ gDrmManagerClient->finalizeDecryptUnit(mDecryptHandle, mTrackId);
+}
+
+status_t DRMSource::start(MetaData *params) {
+ int32_t val;
+ if (params && params->findInt32(kKeyWantsNALFragments, &val)
+ && val != 0) {
+ mWantsNALFragments = true;
+ } else {
+ mWantsNALFragments = false;
+ }
+
+ return mOriginalMediaSource->start(params);
+}
+
+status_t DRMSource::stop() {
+ return mOriginalMediaSource->stop();
+}
+
+sp<MetaData> DRMSource::getFormat() {
+ return mOriginalMediaSource->getFormat();
+}
+
+status_t DRMSource::read(MediaBuffer **buffer, const ReadOptions *options) {
+ Mutex::Autolock autoLock(mDRMLock);
+ status_t err;
+ if ((err = mOriginalMediaSource->read(buffer, options)) != OK) {
+ return err;
+ }
+
+ size_t len = (*buffer)->range_length();
+
+ char *src = (char *)(*buffer)->data() + (*buffer)->range_offset();
+
+ DrmBuffer encryptedDrmBuffer(src, len);
+ DrmBuffer decryptedDrmBuffer;
+ decryptedDrmBuffer.length = len;
+ decryptedDrmBuffer.data = new char[len];
+ DrmBuffer *pDecryptedDrmBuffer = &decryptedDrmBuffer;
+
+ if ((err = gDrmManagerClient->decrypt(mDecryptHandle, mTrackId,
+ &encryptedDrmBuffer, &pDecryptedDrmBuffer)) != DRM_NO_ERROR) {
+
+ if (decryptedDrmBuffer.data) {
+ delete [] decryptedDrmBuffer.data;
+ decryptedDrmBuffer.data = NULL;
+ }
+
+ if (err == DRM_ERROR_LICENSE_EXPIRED) {
+ return ERROR_NO_LICENSE;
+ } else {
+ return ERROR_IO;
+ }
+ }
+ CHECK(pDecryptedDrmBuffer == &decryptedDrmBuffer);
+
+ const char *mime;
+ CHECK(getFormat()->findCString(kKeyMIMEType, &mime));
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) && !mWantsNALFragments) {
+ uint8_t *dstData = (uint8_t*)src;
+ size_t srcOffset = 0;
+ size_t dstOffset = 0;
+
+ len = decryptedDrmBuffer.length;
+ while (srcOffset < len) {
+ CHECK(srcOffset + mNALLengthSize <= len);
+ size_t nalLength = 0;
+ const uint8_t* data = (const uint8_t*)(&decryptedDrmBuffer.data[srcOffset]);
+
+ switch (mNALLengthSize) {
+ case 1:
+ nalLength = *data;
+ break;
+ case 2:
+ nalLength = U16_AT(data);
+ break;
+ case 3:
+ nalLength = ((size_t)data[0] << 16) | U16_AT(&data[1]);
+ break;
+ case 4:
+ nalLength = U32_AT(data);
+ break;
+ default:
+ CHECK(!"Should not be here.");
+ break;
+ }
+
+ srcOffset += mNALLengthSize;
+
+ if (srcOffset + nalLength > len) {
+ if (decryptedDrmBuffer.data) {
+ delete [] decryptedDrmBuffer.data;
+ decryptedDrmBuffer.data = NULL;
+ }
+
+ return ERROR_MALFORMED;
+ }
+
+ if (nalLength == 0) {
+ continue;
+ }
+
+ CHECK(dstOffset + 4 <= (*buffer)->size());
+
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 1;
+ memcpy(&dstData[dstOffset], &decryptedDrmBuffer.data[srcOffset], nalLength);
+ srcOffset += nalLength;
+ dstOffset += nalLength;
+ }
+
+ CHECK_EQ(srcOffset, len);
+ (*buffer)->set_range((*buffer)->range_offset(), dstOffset);
+
+ } else {
+ memcpy(src, decryptedDrmBuffer.data, decryptedDrmBuffer.length);
+ (*buffer)->set_range((*buffer)->range_offset(), decryptedDrmBuffer.length);
+ }
+
+ if (decryptedDrmBuffer.data) {
+ delete [] decryptedDrmBuffer.data;
+ decryptedDrmBuffer.data = NULL;
+ }
+
+ return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+DRMExtractor::DRMExtractor(const sp<DataSource> &source, const char* mime)
+ : mDataSource(source),
+ mDecryptHandle(NULL) {
+ mOriginalExtractor = MediaExtractor::Create(source, mime);
+ mOriginalExtractor->setDrmFlag(true);
+
+ DrmManagerClient *client;
+ source->getDrmInfo(&mDecryptHandle, &client);
+}
+
+DRMExtractor::~DRMExtractor() {
+}
+
+size_t DRMExtractor::countTracks() {
+ return mOriginalExtractor->countTracks();
+}
+
+sp<MediaSource> DRMExtractor::getTrack(size_t index) {
+ sp<MediaSource> originalMediaSource = mOriginalExtractor->getTrack(index);
+ originalMediaSource->getFormat()->setInt32(kKeyIsDRM, 1);
+
+ int32_t trackID;
+ CHECK(getTrackMetaData(index, 0)->findInt32(kKeyTrackID, &trackID));
+
+ DrmBuffer ipmpBox;
+ ipmpBox.data = mOriginalExtractor->getDrmTrackInfo(trackID, &(ipmpBox.length));
+ CHECK(ipmpBox.length > 0);
+
+ return new DRMSource(originalMediaSource, mDecryptHandle, trackID, &ipmpBox);
+}
+
+sp<MetaData> DRMExtractor::getTrackMetaData(size_t index, uint32_t flags) {
+ return mOriginalExtractor->getTrackMetaData(index, flags);
+}
+
+sp<MetaData> DRMExtractor::getMetaData() {
+ return mOriginalExtractor->getMetaData();
+}
+
+static Mutex gDRMSnifferMutex;
+bool SniffDRM(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *) {
+ {
+ Mutex::Autolock autoLock(gDRMSnifferMutex);
+ if (gDrmManagerClient == NULL) {
+ gDrmManagerClient = new DrmManagerClient();
+ }
+ }
+
+ DecryptHandle *decryptHandle = source->DrmInitialization(gDrmManagerClient);
+
+ if (decryptHandle != NULL) {
+ if (decryptHandle->decryptApiType == DecryptApiType::CONTAINER_BASED) {
+ *mimeType = String8("drm+container_based+");
+ } else if (decryptHandle->decryptApiType == DecryptApiType::ELEMENTARY_STREAM_BASED) {
+ *mimeType = String8("drm+es_based+");
+ }
+
+ *mimeType += decryptHandle->mimeType;
+ *confidence = 10.0f;
+
+ return true;
+ }
+
+ return false;
+}
+} //namespace android
+
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 49eac62..0b8997c 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -22,6 +22,7 @@
#include "include/MPEG2TSExtractor.h"
#include "include/NuCachedSource2.h"
#include "include/NuHTTPDataSource.h"
+#include "include/DRMExtractor.h"
#include "matroska/MatroskaExtractor.h"
@@ -31,6 +32,8 @@
#include <media/stagefright/MediaErrors.h>
#include <utils/String8.h>
+#include <cutils/properties.h>
+
namespace android {
bool DataSource::getUInt16(off_t offset, uint16_t *x) {
@@ -104,6 +107,12 @@
RegisterSniffer(SniffAMR);
RegisterSniffer(SniffMPEG2TS);
RegisterSniffer(SniffMP3);
+
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("drm.service.enabled", value, NULL)
+ && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+ RegisterSniffer(SniffDRM);
+ }
}
// static
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index dd2579b..b46d8d0 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -21,14 +21,26 @@
FileSource::FileSource(const char *filename)
: mFile(fopen(filename, "rb")),
+ mFd(fileno(mFile)),
mOffset(0),
- mLength(-1) {
+ mLength(-1),
+ mDecryptHandle(NULL),
+ mDrmManagerClient(NULL),
+ mDrmBufOffset(0),
+ mDrmBufSize(0),
+ mDrmBuf(NULL){
}
FileSource::FileSource(int fd, int64_t offset, int64_t length)
: mFile(fdopen(fd, "rb")),
+ mFd(fd),
mOffset(offset),
- mLength(length) {
+ mLength(length),
+ mDecryptHandle(NULL),
+ mDrmManagerClient(NULL),
+ mDrmBufOffset(0),
+ mDrmBufSize(0),
+ mDrmBuf(NULL){
CHECK(offset >= 0);
CHECK(length >= 0);
}
@@ -38,6 +50,14 @@
fclose(mFile);
mFile = NULL;
}
+
+ if (mDrmBuf != NULL) {
+ delete[] mDrmBuf;
+ mDrmBuf = NULL;
+ }
+ if (mDecryptHandle != NULL) {
+ mDrmManagerClient->closeDecryptSession(mDecryptHandle);
+ }
}
status_t FileSource::initCheck() const {
@@ -61,13 +81,18 @@
}
}
- int err = fseeko(mFile, offset + mOffset, SEEK_SET);
- if (err < 0) {
- LOGE("seek to %lld failed", offset + mOffset);
- return UNKNOWN_ERROR;
- }
+ if (mDecryptHandle != NULL && DecryptApiType::CONTAINER_BASED
+ == mDecryptHandle->decryptApiType) {
+ return readAtDRM(offset, data, size);
+ } else {
+ int err = fseeko(mFile, offset + mOffset, SEEK_SET);
+ if (err < 0) {
+ LOGE("seek to %lld failed", offset + mOffset);
+ return UNKNOWN_ERROR;
+ }
- return fread(data, 1, size, mFile);
+ return fread(data, 1, size, mFile);
+ }
}
status_t FileSource::getSize(off_t *size) {
@@ -87,4 +112,53 @@
return OK;
}
+DecryptHandle* FileSource::DrmInitialization(DrmManagerClient* client) {
+ mDrmManagerClient = client;
+ if (mDecryptHandle == NULL) {
+ mDecryptHandle = mDrmManagerClient->openDecryptSession(
+ mFd, mOffset, mLength);
+ }
+
+ if (mDecryptHandle == NULL) {
+ mDrmManagerClient = NULL;
+ }
+
+ return mDecryptHandle;
+}
+
+void FileSource::getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {
+ *handle = mDecryptHandle;
+
+ *client = mDrmManagerClient;
+}
+
+ssize_t FileSource::readAtDRM(off_t offset, void *data, size_t size) {
+ size_t DRM_CACHE_SIZE = 1024;
+ if (mDrmBuf == NULL) {
+ mDrmBuf = new unsigned char[DRM_CACHE_SIZE];
+ }
+
+ if (mDrmBuf != NULL && mDrmBufSize > 0 && (offset + mOffset) >= mDrmBufOffset
+ && (offset + mOffset + size) <= (mDrmBufOffset + mDrmBufSize)) {
+ /* Use buffered data */
+ memcpy(data, (void*)(mDrmBuf+(offset+mOffset-mDrmBufOffset)), size);
+ return size;
+ } else if (size <= DRM_CACHE_SIZE) {
+ /* Buffer new data */
+ mDrmBufOffset = offset + mOffset;
+ mDrmBufSize = mDrmManagerClient->pread(mDecryptHandle, mDrmBuf,
+ DRM_CACHE_SIZE, offset + mOffset);
+ if (mDrmBufSize > 0) {
+ int64_t dataRead = 0;
+ dataRead = size > mDrmBufSize ? mDrmBufSize : size;
+ memcpy(data, (void*)mDrmBuf, dataRead);
+ return dataRead;
+ } else {
+ return mDrmBufSize;
+ }
+ } else {
+ /* Too big chunk to cache. Call DRM directly */
+ return mDrmManagerClient->pread(mDecryptHandle, data, size, offset + mOffset);
+ }
+}
} // namespace android
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 34064c8c..5497322 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -264,7 +264,9 @@
mHasVideo(false),
mFirstTrack(NULL),
mLastTrack(NULL),
- mFileMetaData(new MetaData) {
+ mFileMetaData(new MetaData),
+ mFirstSINF(NULL),
+ mIsDrm(false) {
}
MPEG4Extractor::~MPEG4Extractor() {
@@ -276,6 +278,15 @@
track = next;
}
mFirstTrack = mLastTrack = NULL;
+
+ SINF *sinf = mFirstSINF;
+ while (sinf) {
+ SINF *next = sinf->next;
+ delete sinf->IPMPData;
+ delete sinf;
+ sinf = next;
+ }
+ mFirstSINF = NULL;
}
sp<MetaData> MPEG4Extractor::getMetaData() {
@@ -370,6 +381,178 @@
return err;
}
+void MPEG4Extractor::setDrmFlag(bool flag) {
+ mIsDrm = flag;
+}
+
+char* MPEG4Extractor::getDrmTrackInfo(size_t trackID, int *len) {
+ if (mFirstSINF == NULL) {
+ return NULL;
+ }
+
+ SINF *sinf = mFirstSINF;
+ while (sinf && (trackID != sinf->trackID)) {
+ sinf = sinf->next;
+ }
+
+ if (sinf == NULL) {
+ return NULL;
+ }
+
+ *len = sinf->len;
+ return sinf->IPMPData;
+}
+
+// Reads an encoded integer 7 bits at a time until it encounters the high bit clear.
+int32_t readSize(off_t offset,
+ const sp<DataSource> DataSource, uint8_t *numOfBytes) {
+ uint32_t size = 0;
+ uint8_t data;
+ bool moreData = true;
+ *numOfBytes = 0;
+
+ while (moreData) {
+ if (DataSource->readAt(offset, &data, 1) < 1) {
+ return -1;
+ }
+ offset ++;
+ moreData = (data >= 128) ? true : false;
+ size = (size << 7) | (data & 0x7f); // Take last 7 bits
+ (*numOfBytes) ++;
+ }
+
+ return size;
+}
+
+status_t MPEG4Extractor::parseDrmSINF(off_t *offset, off_t data_offset) {
+ uint8_t updateIdTag;
+ if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
+ return ERROR_IO;
+ }
+ data_offset ++;
+
+ if (0x01/*OBJECT_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t numOfBytes;
+ int32_t size = readSize(data_offset, mDataSource, &numOfBytes);
+ if (size < 0) {
+ return ERROR_IO;
+ }
+ int32_t classSize = size;
+ data_offset += numOfBytes;
+
+ while(size >= 11 ) {
+ uint8_t descriptorTag;
+ if (mDataSource->readAt(data_offset, &descriptorTag, 1) < 1) {
+ return ERROR_IO;
+ }
+ data_offset ++;
+
+ if (0x11/*OBJECT_DESCRIPTOR_ID_TAG*/ != descriptorTag) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[8];
+ //ObjectDescriptorID and ObjectDescriptor url flag
+ if (mDataSource->readAt(data_offset, buffer, 2) < 2) {
+ return ERROR_IO;
+ }
+ data_offset += 2;
+
+ if ((buffer[1] >> 5) & 0x0001) { //url flag is set
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(data_offset, buffer, 8) < 8) {
+ return ERROR_IO;
+ }
+ data_offset += 8;
+
+ if ((0x0F/*ES_ID_REF_TAG*/ != buffer[1])
+ || ( 0x0A/*IPMP_DESCRIPTOR_POINTER_ID_TAG*/ != buffer[5])) {
+ return ERROR_MALFORMED;
+ }
+
+ SINF *sinf = new SINF;
+ sinf->trackID = U16_AT(&buffer[3]);
+ sinf->IPMPDescriptorID = buffer[7];
+ sinf->next = mFirstSINF;
+ mFirstSINF = sinf;
+
+ size -= (8 + 2 + 1);
+ }
+
+ if (size != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
+ return ERROR_IO;
+ }
+ data_offset ++;
+
+ if(0x05/*IPMP_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
+ return ERROR_MALFORMED;
+ }
+
+ size = readSize(data_offset, mDataSource, &numOfBytes);
+ if (size < 0) {
+ return ERROR_IO;
+ }
+ classSize = size;
+ data_offset += numOfBytes;
+
+ while (size > 0) {
+ uint8_t tag;
+ int32_t dataLen;
+ if (mDataSource->readAt(data_offset, &tag, 1) < 1) {
+ return ERROR_IO;
+ }
+ data_offset ++;
+
+ if (0x0B/*IPMP_DESCRIPTOR_ID_TAG*/ == tag) {
+ uint8_t id;
+ dataLen = readSize(data_offset, mDataSource, &numOfBytes);
+ if (dataLen < 0) {
+ return ERROR_IO;
+ } else if (dataLen < 4) {
+ return ERROR_MALFORMED;
+ }
+ data_offset += numOfBytes;
+
+ if (mDataSource->readAt(data_offset, &id, 1) < 1) {
+ return ERROR_IO;
+ }
+ data_offset ++;
+
+ SINF *sinf = mFirstSINF;
+ while (sinf && (sinf->IPMPDescriptorID != id)) {
+ sinf = sinf->next;
+ }
+ if (sinf == NULL) {
+ return ERROR_MALFORMED;
+ }
+ sinf->len = dataLen - 3;
+ sinf->IPMPData = new char[sinf->len];
+
+ if (mDataSource->readAt(data_offset + 2, sinf->IPMPData, sinf->len) < sinf->len) {
+ return ERROR_IO;
+ }
+ data_offset += sinf->len;
+
+ size -= (dataLen + numOfBytes + 1);
+ }
+ }
+
+ if (size != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ return UNKNOWN_ERROR; // Return a dummy error.
+}
+
static void MakeFourCCString(uint32_t x, char *s) {
s[0] = x >> 24;
s[1] = (x >> 16) & 0xff;
@@ -572,7 +755,11 @@
} else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
mHaveMetadata = true;
- return UNKNOWN_ERROR; // Return a dummy error.
+ if (!mIsDrm) {
+ return UNKNOWN_ERROR; // Return a dummy error.
+ } else {
+ return OK;
+ }
}
break;
}
@@ -1020,6 +1207,20 @@
break;
}
+ case FOURCC('m', 'd', 'a', 't'):
+ {
+ if (!mIsDrm) {
+ *offset += chunk_size;
+ break;
+ }
+
+ if (chunk_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ return parseDrmSINF(offset, data_offset);
+ }
+
default:
{
*offset += chunk_size;
@@ -1069,6 +1270,8 @@
duration = U32_AT(&buffer[20]);
}
+ mLastTrack->meta->setInt32(kKeyTrackID, id);
+
size_t matrixOffset = dynSize + 16;
int32_t a00 = U32_AT(&buffer[matrixOffset]);
int32_t a01 = U32_AT(&buffer[matrixOffset + 4]);
@@ -1712,9 +1915,15 @@
} else {
// Whole NAL units are returned but each fragment is prefixed by
// the start code (0x00 00 00 01).
-
- ssize_t num_bytes_read =
- mDataSource->readAt(offset, mSrcBuffer, size);
+ ssize_t num_bytes_read = 0;
+ int32_t drm = 0;
+ bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
+ if (usesDRM) {
+ num_bytes_read =
+ mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
+ } else {
+ num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
+ }
if (num_bytes_read < (ssize_t)size) {
mBuffer->release();
@@ -1723,40 +1932,46 @@
return ERROR_IO;
}
- uint8_t *dstData = (uint8_t *)mBuffer->data();
- size_t srcOffset = 0;
- size_t dstOffset = 0;
+ if (usesDRM) {
+ CHECK(mBuffer != NULL);
+ mBuffer->set_range(0, size);
- while (srcOffset < size) {
- CHECK(srcOffset + mNALLengthSize <= size);
- size_t nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
- srcOffset += mNALLengthSize;
+ } else {
+ uint8_t *dstData = (uint8_t *)mBuffer->data();
+ size_t srcOffset = 0;
+ size_t dstOffset = 0;
- if (srcOffset + nalLength > size) {
- mBuffer->release();
- mBuffer = NULL;
+ while (srcOffset < size) {
+ CHECK(srcOffset + mNALLengthSize <= size);
+ size_t nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
+ srcOffset += mNALLengthSize;
- return ERROR_MALFORMED;
+ if (srcOffset + nalLength > size) {
+ mBuffer->release();
+ mBuffer = NULL;
+
+ return ERROR_MALFORMED;
+ }
+
+ if (nalLength == 0) {
+ continue;
+ }
+
+ CHECK(dstOffset + 4 <= mBuffer->size());
+
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 1;
+ memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
+ srcOffset += nalLength;
+ dstOffset += nalLength;
}
-
- if (nalLength == 0) {
- continue;
- }
-
- CHECK(dstOffset + 4 <= mBuffer->size());
-
- dstData[dstOffset++] = 0;
- dstData[dstOffset++] = 0;
- dstData[dstOffset++] = 0;
- dstData[dstOffset++] = 1;
- memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
- srcOffset += nalLength;
- dstOffset += nalLength;
+ CHECK_EQ(srcOffset, size);
+ CHECK(mBuffer != NULL);
+ mBuffer->set_range(0, dstOffset);
}
- CHECK_EQ(srcOffset, size);
- CHECK(mBuffer != NULL);
- mBuffer->set_range(0, dstOffset);
mBuffer->meta_data()->clear();
mBuffer->meta_data()->setInt64(
kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 8a5fb11..965c370 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -24,6 +24,7 @@
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
#include "include/MPEG2TSExtractor.h"
+#include "include/DRMExtractor.h"
#include "matroska/MatroskaExtractor.h"
@@ -63,6 +64,18 @@
mime, confidence);
}
+ if (!strncmp(mime, "drm", 3)) {
+ const char *originalMime = strrchr(mime, '+') + 1;
+
+ if (!strncmp(mime, "drm+es_based", 12)) {
+ return new DRMExtractor(source, originalMime);
+ } else if (!strncmp(mime, "drm+container_based", 19)) {
+ mime = originalMime;
+ } else {
+ return NULL;
+ }
+ }
+
if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
|| !strcasecmp(mime, "audio/mp4")) {
return new MPEG4Extractor(source);
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 6d4fbcb..af247d5 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -58,7 +58,7 @@
path->setTo(slashPos);
}
- char *colonPos = strchr(host->string(), ':');
+ const char *colonPos = strchr(host->string(), ':');
if (colonPos != NULL) {
unsigned long x;
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 10c6e41..7a600d7 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -127,10 +127,11 @@
|| !strcasecmp(extension, ".rtttl")
|| !strcasecmp(extension, ".rtx")
|| !strcasecmp(extension, ".ota")) {
- return HandleMIDI(path, &client);
- }
-
- if (mRetriever->setDataSource(path) == OK) {
+ status_t status = HandleMIDI(path, &client);
+ if (status != OK) {
+ return status;
+ }
+ } else if (mRetriever->setDataSource(path) == OK) {
const char *value;
if ((value = mRetriever->extractMetadata(
METADATA_KEY_MIMETYPE)) != NULL) {
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index a68c641..90f3d6d 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -96,7 +96,7 @@
out->setTo(baseURL);
out->append(url);
} else {
- char *slashPos = strrchr(baseURL, '/');
+ const char *slashPos = strrchr(baseURL, '/');
if (slashPos > &baseURL[6]) {
out->setTo(baseURL, slashPos - baseURL);
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index c059e60..e352928 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -26,6 +26,7 @@
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/TimeSource.h>
#include <utils/threads.h>
+#include <drm/DrmManagerClient.h>
namespace android {
@@ -41,6 +42,9 @@
struct ARTPSession;
struct UDPPusher;
+class DrmManagerClinet;
+class DecryptHandle;
+
struct AwesomeRenderer : public RefBase {
AwesomeRenderer() {}
@@ -216,6 +220,9 @@
}
} *mSuspensionState;
+ DrmManagerClient *mDrmManagerClient;
+ DecryptHandle *mDecryptHandle;
+
status_t setDataSource_l(
const char *uri,
const KeyedVector<String8, String8> *headers = NULL);
diff --git a/media/libstagefright/include/DRMExtractor.h b/media/libstagefright/include/DRMExtractor.h
new file mode 100644
index 0000000..cafc812
--- /dev/null
+++ b/media/libstagefright/include/DRMExtractor.h
@@ -0,0 +1,61 @@
+/*
+ * 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 DRM_EXTRACTOR_H_
+
+#define DRM_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+#include <drm/DrmManagerClient.h>
+
+namespace android {
+
+struct AMessage;
+class DataSource;
+class SampleTable;
+class String8;
+class DecryptHandle;
+
+class DRMExtractor : public MediaExtractor {
+public:
+ DRMExtractor(const sp<DataSource> &source, const char *mime);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+ virtual sp<MetaData> getMetaData();
+
+protected:
+ virtual ~DRMExtractor();
+
+private:
+ sp<DataSource> mDataSource;
+
+ sp<MediaExtractor> mOriginalExtractor;
+ DecryptHandle* mDecryptHandle;
+
+ DRMExtractor(const DRMExtractor &);
+ DRMExtractor &operator=(const DRMExtractor &);
+};
+
+bool SniffDRM(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *);
+
+} // namespace android
+
+#endif // DRM_EXTRACTOR_H_
+
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 2610b0e..bc2e4dc 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -39,6 +39,10 @@
virtual sp<MetaData> getMetaData();
+ // for DRM
+ virtual void setDrmFlag(bool flag);
+ virtual char* getDrmTrackInfo(size_t trackID, int *len);
+
protected:
virtual ~MPEG4Extractor();
@@ -71,6 +75,19 @@
static status_t verifyTrack(Track *track);
+ struct SINF {
+ SINF *next;
+ uint16_t trackID;
+ uint8_t IPMPDescriptorID;
+ ssize_t len;
+ char *IPMPData;
+ };
+
+ SINF *mFirstSINF;
+
+ bool mIsDrm;
+ status_t parseDrmSINF(off_t *offset, off_t data_offset);
+
status_t parseTrackHeader(off_t data_offset, off_t data_size);
MPEG4Extractor(const MPEG4Extractor &);
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index aa2618e..f03f7a2 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -201,7 +201,7 @@
AString format;
getFormat(index, &format);
- char *lastSpacePos = strrchr(format.c_str(), ' ');
+ const char *lastSpacePos = strrchr(format.c_str(), ' ');
CHECK(lastSpacePos != NULL);
char *end;
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 7bf534d..72a2fdb 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -1197,7 +1197,7 @@
out->setTo(baseURL);
out->append(url);
} else {
- char *slashPos = strrchr(baseURL, '/');
+ const char *slashPos = strrchr(baseURL, '/');
if (slashPos > &baseURL[6]) {
out->setTo(baseURL, slashPos - baseURL);
diff --git a/media/tests/omxjpegdecoder/Android.mk b/media/tests/omxjpegdecoder/Android.mk
index 7802efd..81c6167 100644
--- a/media/tests/omxjpegdecoder/Android.mk
+++ b/media/tests/omxjpegdecoder/Android.mk
@@ -22,11 +22,6 @@
SkOmxPixelRef.cpp \
StreamSource.cpp
-
-# add external/skia/src/images/SkImageDecoder_libjpeg.cpp
-LOCAL_SRC_FILES += \
- ../../../../../external/skia/src/images/SkImageDecoder_libjpeg.cpp
-
LOCAL_SHARED_LIBRARIES := \
libcutils \
libskia \
diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java
index 07a3a53..d1476d2 100644
--- a/obex/javax/obex/ServerOperation.java
+++ b/obex/javax/obex/ServerOperation.java
@@ -397,11 +397,13 @@
&& (headerID != ObexHelper.OBEX_OPCODE_GET_FINAL)) {
if (length > 3) {
- byte[] temp = new byte[length];
+ byte[] temp = new byte[length - 3];
+ // First three bytes already read, compensating for this
bytesReceived = mInput.read(temp);
- while (bytesReceived != length) {
- bytesReceived += mInput.read(temp, bytesReceived, length - bytesReceived);
+ while (bytesReceived != temp.length) {
+ bytesReceived += mInput.read(temp, bytesReceived,
+ temp.length - bytesReceived);
}
}
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 662a1fa..5c09dcc 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -480,13 +480,13 @@
copybit_device_t* const copybit = blitengine;
if (copybit) {
copybit_image_t simg;
- simg.w = src->width;
+ simg.w = src->stride;
simg.h = src->height;
simg.format = src->format;
simg.handle = const_cast<native_handle_t*>(src->handle);
copybit_image_t dimg;
- dimg.w = dst->width;
+ dimg.w = dst->stride;
dimg.h = dst->height;
dimg.format = dst->format;
dimg.handle = const_cast<native_handle_t*>(dst->handle);
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 7e0c169..d26d13e 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -1843,3 +1843,8 @@
}
return setError(EGL_BAD_DISPLAY, NULL);
}
+
+EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) { return 0; }
+EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list) { return 0; }
+EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) { return 0; }
+
diff --git a/opengl/tests/gl_jni/jni/gl_code.cpp b/opengl/tests/gl_jni/jni/gl_code.cpp
index f031c79..ef66841 100644
--- a/opengl/tests/gl_jni/jni/gl_code.cpp
+++ b/opengl/tests/gl_jni/jni/gl_code.cpp
@@ -181,4 +181,3 @@
{
background = 1.0f - background;
}
-
diff --git a/packages/WAPPushManager/Android.mk b/packages/WAPPushManager/Android.mk
new file mode 100644
index 0000000..c1d8f4b
--- /dev/null
+++ b/packages/WAPPushManager/Android.mk
@@ -0,0 +1,20 @@
+# Copyright 2007-2008 The Android Open Source Project
+
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := WAPPushManager
+
+LOCAL_STATIC_JAVA_LIBRARIES += android-common
+
+LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags
+
+include $(BUILD_PACKAGE)
+
+# This finds and builds the test apk as well, so a single make does both.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/WAPPushManager/AndroidManifest.xml b/packages/WAPPushManager/AndroidManifest.xml
new file mode 100644
index 0000000..89e9d6a
--- /dev/null
+++ b/packages/WAPPushManager/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.smspush">
+
+ <permission android:name="com.android.smspush.WAPPUSH_MANAGER_BIND"
+ android:protectionLevel="signatureOrSystem" />
+
+ <original-package android:name="com.android.smspush" />
+ <application>
+ <service android:name=".WapPushManager"
+ android:permission="com.android.smspush.WAPPUSH_MANAGER_BIND"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.internal.telephony.IWapPushManager"></action>
+ </intent-filter>
+ </service>
+ </application>
+
+
+</manifest>
diff --git a/packages/WAPPushManager/CleanSpec.mk b/packages/WAPPushManager/CleanSpec.mk
new file mode 100644
index 0000000..b84e1b6
--- /dev/null
+++ b/packages/WAPPushManager/CleanSpec.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/tests/BrowserTestPlugin/MODULE_LICENSE_APACHE2 b/packages/WAPPushManager/MODULE_LICENSE_APACHE2
similarity index 100%
rename from tests/BrowserTestPlugin/MODULE_LICENSE_APACHE2
rename to packages/WAPPushManager/MODULE_LICENSE_APACHE2
diff --git a/tests/BrowserTestPlugin/NOTICE b/packages/WAPPushManager/NOTICE
similarity index 98%
rename from tests/BrowserTestPlugin/NOTICE
rename to packages/WAPPushManager/NOTICE
index 9df2554..c5b1efa 100644
--- a/tests/BrowserTestPlugin/NOTICE
+++ b/packages/WAPPushManager/NOTICE
@@ -1,5 +1,5 @@
- Copyright (c) 2005-2009, The Android Open Source Project
+ Copyright (c) 2005-2008, The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/packages/WAPPushManager/proguard.flags b/packages/WAPPushManager/proguard.flags
new file mode 100644
index 0000000..d09887b
--- /dev/null
+++ b/packages/WAPPushManager/proguard.flags
@@ -0,0 +1,18 @@
+
+#apply method is dynamically referenced by the reflection class.
+-keep class android.app.ContextImpl$SharedPreferencesImpl$EditorImpl {
+ void apply();
+}
+-keep class android.content.SharedPreferences$Editor {
+ void apply();
+}
+
+#WapPushManager is referenced only by AndroidManifest.xml
+-keep class com.android.smspush.WapPushManager {
+#CTS module method
+ public boolean isDataExist(java.lang.String, java.lang.String,
+ java.lang.String, java.lang.String);
+ public boolean verifyData(java.lang.String, java.lang.String,
+ java.lang.String, java.lang.String, int, boolean, boolean);
+}
+
diff --git a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
new file mode 100644
index 0000000..96e0377
--- /dev/null
+++ b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush;
+
+import android.app.Service;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.telephony.WapPushManagerParams;
+
+/**
+ * The WapPushManager service is implemented to process incoming
+ * WAP Push messages and to maintain the Receiver Application/Application
+ * ID mapping. The WapPushManager runs as a system service, and only the
+ * WapPushManager can update the WAP Push message and Receiver Application
+ * mapping (Application ID Table). When the device receives an SMS WAP Push
+ * message, the WapPushManager looks up the Receiver Application name in
+ * Application ID Table. If an application is found, the application is
+ * launched using its full component name instead of broadcasting an implicit
+ * Intent. If a Receiver Application is not found in the Application ID
+ * Table or the WapPushManager returns a process-further value, the
+ * telephony stack will process the message using existing message processing
+ * flow, and broadcast an implicit Intent.
+ */
+public class WapPushManager extends Service {
+
+ private static final String LOG_TAG = "WAP PUSH";
+ private static final String DATABASE_NAME = "wappush.db";
+ private static final String APPID_TABLE_NAME = "appid_tbl";
+
+ /**
+ * Version number must be incremented when table structure is changed.
+ */
+ private static final int WAP_PUSH_MANAGER_VERSION = 1;
+ private static final boolean DEBUG_SQL = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ /**
+ * Inner class that deals with application ID table
+ */
+ private class WapPushManDBHelper extends SQLiteOpenHelper {
+ WapPushManDBHelper(Context context) {
+ super(context, DATABASE_NAME, null, WAP_PUSH_MANAGER_VERSION);
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "helper instance created.");
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "db onCreate.");
+ String sql = "CREATE TABLE " + APPID_TABLE_NAME + " ("
+ + "id INTEGER PRIMARY KEY, "
+ + "x_wap_application TEXT, "
+ + "content_type TEXT, "
+ + "package_name TEXT, "
+ + "class_name TEXT, "
+ + "app_type INTEGER, "
+ + "need_signature INTEGER, "
+ + "further_processing INTEGER, "
+ + "install_order INTEGER "
+ + ")";
+
+ if (DEBUG_SQL) Log.v(LOG_TAG, "sql: " + sql);
+ db.execSQL(sql);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db,
+ int oldVersion, int newVersion) {
+ // TODO: when table structure is changed, need to dump and restore data.
+ /*
+ db.execSQL(
+ "drop table if exists "+APPID_TABLE_NAME);
+ onCreate(db);
+ */
+ Log.w(LOG_TAG, "onUpgrade is not implemented yet. do nothing.");
+ }
+
+ protected class queryData {
+ public String packageName;
+ public String className;
+ int appType;
+ int needSignature;
+ int furtherProcessing;
+ int installOrder;
+ }
+
+ /**
+ * Query the latest receiver application info with supplied application ID and
+ * content type.
+ * @param app_id application ID to look up
+ * @param content_type content type to look up
+ */
+ protected queryData queryLastApp(SQLiteDatabase db,
+ String app_id, String content_type) {
+ String sql = "select install_order, package_name, class_name, "
+ + " app_type, need_signature, further_processing"
+ + " from " + APPID_TABLE_NAME
+ + " where x_wap_application=\'" + app_id + "\'"
+ + " and content_type=\'" + content_type + "\'"
+ + " order by install_order desc";
+ if (DEBUG_SQL) Log.v(LOG_TAG, "sql: " + sql);
+ Cursor cur = db.rawQuery(sql, null);
+ queryData ret = null;
+
+ if (cur.moveToNext()) {
+ ret = new queryData();
+ ret.installOrder = cur.getInt(cur.getColumnIndex("install_order"));
+ ret.packageName = cur.getString(cur.getColumnIndex("package_name"));
+ ret.className = cur.getString(cur.getColumnIndex("class_name"));
+ ret.appType = cur.getInt(cur.getColumnIndex("app_type"));
+ ret.needSignature = cur.getInt(cur.getColumnIndex("need_signature"));
+ ret.furtherProcessing = cur.getInt(cur.getColumnIndex("further_processing"));
+ }
+ cur.close();
+ return ret;
+ }
+
+ }
+
+ /**
+ * The exported API implementations class
+ */
+ private class IWapPushManagerStub extends IWapPushManager.Stub {
+ public Context mContext;
+
+ public IWapPushManagerStub() {
+
+ }
+
+ /**
+ * Compare the package signature with WapPushManager package
+ */
+ protected boolean signatureCheck(String package_name) {
+ PackageManager pm = mContext.getPackageManager();
+ int match = pm.checkSignatures(mContext.getPackageName(), package_name);
+
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "compare signature " + mContext.getPackageName()
+ + " and " + package_name + ", match=" + match);
+
+ return match == PackageManager.SIGNATURE_MATCH;
+ }
+
+ /**
+ * Returns the status value of the message processing.
+ * The message will be processed as follows:
+ * 1.Look up Application ID Table with x-wap-application-id + content type
+ * 2.Check the signature of package name that is found in the
+ * Application ID Table by using PackageManager.checkSignature
+ * 3.Trigger the Application
+ * 4.Returns the process status value.
+ */
+ public int processMessage(String app_id, String content_type, Intent intent)
+ throws RemoteException {
+ Log.d(LOG_TAG, "wpman processMsg " + app_id + ":" + content_type);
+
+ WapPushManDBHelper dbh = getDatabase(mContext);
+ SQLiteDatabase db = dbh.getReadableDatabase();
+ WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, app_id, content_type);
+ db.close();
+
+ if (lastapp == null) {
+ Log.w(LOG_TAG, "no receiver app found for " + app_id + ":" + content_type);
+ return WapPushManagerParams.APP_QUERY_FAILED;
+ }
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "starting " + lastapp.packageName
+ + "/" + lastapp.className);
+
+ if (lastapp.needSignature != 0) {
+ if (!signatureCheck(lastapp.packageName)) {
+ return WapPushManagerParams.SIGNATURE_NO_MATCH;
+ }
+ }
+
+ if (lastapp.appType == WapPushManagerParams.APP_TYPE_ACTIVITY) {
+ //Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClassName(lastapp.packageName, lastapp.className);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ try {
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(LOG_TAG, "invalid name " +
+ lastapp.packageName + "/" + lastapp.className);
+ return WapPushManagerParams.INVALID_RECEIVER_NAME;
+ }
+ } else {
+ intent.setClassName(mContext, lastapp.className);
+ intent.setComponent(new ComponentName(lastapp.packageName,
+ lastapp.className));
+ if (mContext.startService(intent) == null) {
+ Log.w(LOG_TAG, "invalid name " +
+ lastapp.packageName + "/" + lastapp.className);
+ return WapPushManagerParams.INVALID_RECEIVER_NAME;
+ }
+ }
+
+ return WapPushManagerParams.MESSAGE_HANDLED
+ | (lastapp.furtherProcessing == 1 ?
+ WapPushManagerParams.FURTHER_PROCESSING : 0);
+ }
+
+ protected boolean appTypeCheck(int app_type) {
+ if (app_type == WapPushManagerParams.APP_TYPE_ACTIVITY ||
+ app_type == WapPushManagerParams.APP_TYPE_SERVICE) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if adding the package succeeded.
+ */
+ public boolean addPackage(String x_app_id, String content_type,
+ String package_name, String class_name,
+ int app_type, boolean need_signature, boolean further_processing) {
+ WapPushManDBHelper dbh = getDatabase(mContext);
+ SQLiteDatabase db = dbh.getWritableDatabase();
+ WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
+ boolean ret = false;
+ boolean insert = false;
+ int sq = 0;
+
+ if (!appTypeCheck(app_type)) {
+ Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be "
+ + WapPushManagerParams.APP_TYPE_ACTIVITY + " or "
+ + WapPushManagerParams.APP_TYPE_SERVICE);
+ return false;
+ }
+
+ if (lastapp == null) {
+ insert = true;
+ sq = 0;
+ } else if (!lastapp.packageName.equals(package_name) ||
+ !lastapp.className.equals(class_name)) {
+ insert = true;
+ sq = lastapp.installOrder + 1;
+ }
+
+ if (insert) {
+ ContentValues values = new ContentValues();
+
+ values.put("x_wap_application", x_app_id);
+ values.put("content_type", content_type);
+ values.put("package_name", package_name);
+ values.put("class_name", class_name);
+ values.put("app_type", app_type);
+ values.put("need_signature", need_signature ? 1 : 0);
+ values.put("further_processing", further_processing ? 1 : 0);
+ values.put("install_order", sq);
+ db.insert(APPID_TABLE_NAME, null, values);
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "add:" + x_app_id + ":" + content_type
+ + " " + package_name + "." + class_name
+ + ", newsq:" + sq);
+ ret = true;
+ }
+
+ db.close();
+
+ return ret;
+ }
+
+ /**
+ * Returns true if updating the package succeeded.
+ */
+ public boolean updatePackage(String x_app_id, String content_type,
+ String package_name, String class_name,
+ int app_type, boolean need_signature, boolean further_processing) {
+
+ if (!appTypeCheck(app_type)) {
+ Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be "
+ + WapPushManagerParams.APP_TYPE_ACTIVITY + " or "
+ + WapPushManagerParams.APP_TYPE_SERVICE);
+ return false;
+ }
+
+ WapPushManDBHelper dbh = getDatabase(mContext);
+ SQLiteDatabase db = dbh.getWritableDatabase();
+ WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
+
+ if (lastapp == null) {
+ db.close();
+ return false;
+ }
+
+ ContentValues values = new ContentValues();
+ String where = "x_wap_application=\'" + x_app_id + "\'"
+ + " and content_type=\'" + content_type + "\'"
+ + " and install_order=" + lastapp.installOrder;
+
+ values.put("package_name", package_name);
+ values.put("class_name", class_name);
+ values.put("app_type", app_type);
+ values.put("need_signature", need_signature ? 1 : 0);
+ values.put("further_processing", further_processing ? 1 : 0);
+
+ int num = db.update(APPID_TABLE_NAME, values, where, null);
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "update:" + x_app_id + ":" + content_type + " "
+ + package_name + "." + class_name
+ + ", sq:" + lastapp.installOrder);
+
+ db.close();
+
+ return num > 0;
+ }
+
+ /**
+ * Returns true if deleting the package succeeded.
+ */
+ public boolean deletePackage(String x_app_id, String content_type,
+ String package_name, String class_name) {
+ WapPushManDBHelper dbh = getDatabase(mContext);
+ SQLiteDatabase db = dbh.getWritableDatabase();
+ String where = "x_wap_application=\'" + x_app_id + "\'"
+ + " and content_type=\'" + content_type + "\'"
+ + " and package_name=\'" + package_name + "\'"
+ + " and class_name=\'" + class_name + "\'";
+ int num_removed = db.delete(APPID_TABLE_NAME, where, null);
+
+ db.close();
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "deleted " + num_removed + " rows:"
+ + x_app_id + ":" + content_type + " "
+ + package_name + "." + class_name);
+ return num_removed > 0;
+ }
+ };
+
+
+ /**
+ * Linux IPC Binder
+ */
+ private final IWapPushManagerStub mBinder = new IWapPushManagerStub();
+
+ /**
+ * Default constructor
+ */
+ public WapPushManager() {
+ super();
+ mBinder.mContext = this;
+ }
+
+ @Override
+ public IBinder onBind(Intent arg0) {
+ return mBinder;
+ }
+
+ /**
+ * Application ID database instance
+ */
+ private WapPushManDBHelper mDbHelper = null;
+ protected WapPushManDBHelper getDatabase(Context context) {
+ if (mDbHelper == null) {
+ if (LOCAL_LOGV) Log.v(LOG_TAG, "create new db inst.");
+ mDbHelper = new WapPushManDBHelper(context);
+ }
+ return mDbHelper;
+ }
+
+
+ /**
+ * This method is used for testing
+ */
+ public boolean verifyData(String x_app_id, String content_type,
+ String package_name, String class_name,
+ int app_type, boolean need_signature, boolean further_processing) {
+ WapPushManDBHelper dbh = getDatabase(this);
+ SQLiteDatabase db = dbh.getReadableDatabase();
+ WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
+
+ db.close();
+
+ if (lastapp == null) return false;
+
+ if (lastapp.packageName.equals(package_name)
+ && lastapp.className.equals(class_name)
+ && lastapp.appType == app_type
+ && lastapp.needSignature == (need_signature ? 1 : 0)
+ && lastapp.furtherProcessing == (further_processing ? 1 : 0)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This method is used for testing
+ */
+ public boolean isDataExist(String x_app_id, String content_type,
+ String package_name, String class_name) {
+ WapPushManDBHelper dbh = getDatabase(this);
+ SQLiteDatabase db = dbh.getReadableDatabase();
+ boolean ret = dbh.queryLastApp(db, x_app_id, content_type) != null;
+
+ db.close();
+ return ret;
+ }
+
+}
+
diff --git a/packages/WAPPushManager/tests/Android.mk b/packages/WAPPushManager/tests/Android.mk
new file mode 100644
index 0000000..0a95b52
--- /dev/null
+++ b/packages/WAPPushManager/tests/Android.mk
@@ -0,0 +1,38 @@
+# Copyright 2008, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += \
+ src/com/android/smspush/unitTests/IDataVerify.aidl
+
+
+# Notice that we don't have to include the src files of Email because, by
+# running the tests using an instrumentation targeting Eamil, we
+# automatically get all of its classes loaded into our environment.
+
+LOCAL_PACKAGE_NAME := WAPPushManagerTests
+
+LOCAL_INSTRUMENTATION_FOR := WAPPushManager
+
+include $(BUILD_PACKAGE)
+
diff --git a/packages/WAPPushManager/tests/AndroidManifest.xml b/packages/WAPPushManager/tests/AndroidManifest.xml
new file mode 100644
index 0000000..da7634f
--- /dev/null
+++ b/packages/WAPPushManager/tests/AndroidManifest.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.smspush.unitTests">
+
+
+ <uses-permission android:name="com.android.smspush.WAPPUSH_MANAGER_BIND" />
+ <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
+ <!--testing.../-->
+ <application android:icon="@drawable/icon" android:label="wappush test">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name=".ClientTest"
+ android:label="wappush test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <receiver android:name=".DrmReceiver" android:enabled="true">
+ <intent-filter>
+ <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
+ <data android:mimeType="application/vnd.oma.drm.rights+xml" />
+ <data android:value="application/vnd.oma.drm.rights+wbxml" />
+ </intent-filter>
+ </receiver>
+
+ <service android:enabled="true" android:name=".ReceiverService"
+ android:exported="true"/>
+
+ <activity android:name=".ReceiverActivity"
+ android:exported="true" android:label="test receiver" />
+
+ <service android:name=".DataVerify"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.smspush.unitTests.IDataVerify" />
+ </intent-filter>
+ </service>
+
+ </application>
+
+ <!--
+ This declares that this application uses the instrumentation test runner targeting
+ the package of com.android.smspush. To run the tests use the command:
+ "adb shell am instrument -w
+ com.android.smspush.unitTests/android.test.InstrumentationTestRunner"
+ -->
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.smspush"
+ android:label="Tests for WAPPushManager"/>
+
+</manifest>
+
diff --git a/packages/WAPPushManager/tests/res/drawable-hdpi/icon.png b/packages/WAPPushManager/tests/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..8074c4c
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png b/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..1095584
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png b/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/packages/WAPPushManager/tests/res/layout/main.xml b/packages/WAPPushManager/tests/res/layout/main.xml
new file mode 100644
index 0000000..c7bdbb2
--- /dev/null
+++ b/packages/WAPPushManager/tests/res/layout/main.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<AbsoluteLayout
+android:id="@+id/widget133"
+android:layout_width="fill_parent"
+android:layout_height="fill_parent"
+xmlns:android="http://schemas.android.com/apk/res/android"
+>
+<EditText
+android:id="@+id/app_id"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="10"
+android:textSize="18sp"
+android:layout_x="0px"
+android:layout_y="26px"
+>
+</EditText>
+<EditText
+android:id="@+id/cont"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="20"
+android:textSize="18sp"
+android:layout_x="47px"
+android:layout_y="26px"
+>
+</EditText>
+<EditText
+android:id="@+id/pkg"
+android:layout_width="125px"
+android:layout_height="wrap_content"
+android:text="pkg"
+android:textSize="18sp"
+android:layout_x="0px"
+android:layout_y="81px"
+>
+</EditText>
+<EditText
+android:id="@+id/cls"
+android:layout_width="173px"
+android:layout_height="wrap_content"
+android:text="cls"
+android:textSize="18sp"
+android:layout_x="147px"
+android:layout_y="81px"
+>
+</EditText>
+<Button
+android:id="@+id/addpkg"
+android:layout_width="182px"
+android:layout_height="wrap_content"
+android:text="add/update package"
+android:layout_x="15px"
+android:layout_y="225px"
+>
+</Button>
+<TextView
+android:id="@+id/widget52"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="input app_id, cont_type, pkg, cls"
+android:textSize="18sp"
+android:layout_x="0px"
+android:layout_y="0px"
+>
+</TextView>
+<Button
+android:id="@+id/procmsg"
+android:layout_width="109px"
+android:layout_height="wrap_content"
+android:text="process msg"
+android:layout_x="197px"
+android:layout_y="361px"
+>
+</Button>
+<RadioGroup
+android:id="@+id/widget137"
+android:layout_width="83px"
+android:layout_height="80px"
+android:orientation="vertical"
+android:layout_x="19px"
+android:layout_y="137px"
+>
+<RadioButton
+android:id="@+id/act"
+android:layout_width="182px"
+android:layout_height="wrap_content"
+android:text="act"
+android:checked="true"
+>
+</RadioButton>
+<RadioButton
+android:id="@+id/svc"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="svc"
+>
+</RadioButton>
+</RadioGroup>
+<Button
+android:id="@+id/delpkg"
+android:layout_width="174px"
+android:layout_height="wrap_content"
+android:text="delete pkg"
+android:layout_x="14px"
+android:layout_y="283px"
+>
+</Button>
+<EditText
+android:id="@+id/pdu"
+android:layout_width="186px"
+android:layout_height="83px"
+android:text="0006080302030aaf02905c030d6a0085070373616d706c6540646f636f6d6f2e6e652e6a700005c3072009102012345601"
+android:textSize="18sp"
+android:layout_x="10px"
+android:layout_y="341px"
+>
+</EditText>
+<CheckBox
+android:id="@+id/ftr"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="ftr_proc"
+android:layout_x="143px"
+android:layout_y="181px"
+>
+</CheckBox>
+<CheckBox
+android:id="@+id/sig"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:text="nd_sig"
+android:checked="true"
+android:layout_x="142px"
+android:layout_y="140px"
+>
+</CheckBox>
+</AbsoluteLayout>
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java
new file mode 100644
index 0000000..78fd174
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush.unitTests;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.RadioButton;
+
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.telephony.WapPushManagerParams;
+import com.android.internal.telephony.WapPushOverSms;
+import com.android.internal.util.HexDump;
+import com.android.smspush.WapPushManager;
+
+/**
+ * WapPushManager test application
+ */
+public class ClientTest extends Activity {
+ private static final String LOG_TAG = "WAP PUSH";
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ Button addpbtn = (Button) findViewById(R.id.addpkg);
+ Button procbtn = (Button) findViewById(R.id.procmsg);
+ Button delbtn = (Button) findViewById(R.id.delpkg);
+
+ Log.v(LOG_TAG, "activity created!!");
+
+ addpbtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ EditText app_id = (EditText) findViewById(R.id.app_id);
+ EditText cont = (EditText) findViewById(R.id.cont);
+ EditText pkg = (EditText) findViewById(R.id.pkg);
+ EditText cls = (EditText) findViewById(R.id.cls);
+ RadioButton act = (RadioButton) findViewById(R.id.act);
+ CheckBox sig = (CheckBox) findViewById(R.id.sig);
+ CheckBox ftr = (CheckBox) findViewById(R.id.ftr);
+
+ try {
+ if (!mWapPushMan.addPackage(
+ app_id.getText().toString(),
+ cont.getText().toString(),
+ pkg.getText().toString(),
+ cls.getText().toString(),
+ act.isChecked() ? WapPushManagerParams.APP_TYPE_ACTIVITY :
+ WapPushManagerParams.APP_TYPE_SERVICE,
+ sig.isChecked(), ftr.isChecked())) {
+
+ Log.w(LOG_TAG, "remote add pkg failed...");
+ mWapPushMan.updatePackage(
+ app_id.getText().toString(),
+ cont.getText().toString(),
+ pkg.getText().toString(),
+ cls.getText().toString(),
+ act.isChecked() ? WapPushManagerParams.APP_TYPE_ACTIVITY :
+ WapPushManagerParams.APP_TYPE_SERVICE,
+ sig.isChecked(), ftr.isChecked());
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "remote func failed...");
+ }
+ }
+ });
+
+ delbtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ EditText app_id = (EditText) findViewById(R.id.app_id);
+ EditText cont = (EditText) findViewById(R.id.cont);
+ EditText pkg = (EditText) findViewById(R.id.pkg);
+ EditText cls = (EditText) findViewById(R.id.cls);
+ // CheckBox delall = (CheckBox) findViewById(R.id.delall);
+ // Log.d(LOG_TAG, "button clicked");
+
+ try {
+ mWapPushMan.deletePackage(
+ app_id.getText().toString(),
+ cont.getText().toString(),
+ pkg.getText().toString(),
+ cls.getText().toString());
+ // delall.isChecked());
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "remote func failed...");
+ }
+ }
+ });
+
+ procbtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ EditText pdu = (EditText) findViewById(R.id.pdu);
+ EditText app_id = (EditText) findViewById(R.id.app_id);
+ EditText cont = (EditText) findViewById(R.id.cont);
+
+ // WapPushOverSms wap = new WapPushOverSms();
+ // wap.dispatchWapPdu(strToHex(pdu.getText().toString()));
+ try {
+ Intent intent = new Intent();
+ intent.putExtra("transactionId", 0);
+ intent.putExtra("pduType", 6);
+ intent.putExtra("header",
+ HexDump.hexStringToByteArray(pdu.getText().toString()));
+ intent.putExtra("data",
+ HexDump.hexStringToByteArray(pdu.getText().toString()));
+
+ mWapPushMan.processMessage(
+ app_id.getText().toString(),
+ cont.getText().toString(),
+ intent);
+ //HexDump.hexStringToByteArray(pdu.getText().toString()), 0, 6, 5, 5);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "remote func failed...");
+ }
+ }
+ });
+ }
+
+ private IWapPushManager mWapPushMan;
+ private ServiceConnection conn = new ServiceConnection() {
+ public void onServiceDisconnected(ComponentName name) {
+ mWapPushMan = null;
+ Log.v(LOG_TAG, "service disconnected.");
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mWapPushMan = IWapPushManager.Stub.asInterface(service);
+ Log.v(LOG_TAG, "service connected.");
+ }
+ };
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Log.v(LOG_TAG, "onStart bind WAPPushManager service "
+ + IWapPushManager.class.getName());
+ this.bindService(new Intent(IWapPushManager.class.getName()), conn,
+ Context.BIND_AUTO_CREATE);
+ Log.v(LOG_TAG, "bind service done.");
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ this.unbindService(conn);
+ }
+
+}
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java
new file mode 100644
index 0000000..ef491fd
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush.unitTests;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * To verify that receiver application receives correct body data.
+ */
+public class DataVerify extends Service {
+ private static final String LOG_TAG = "WAP PUSH";
+ private static final int TIME_WAIT = 100;
+ private static final int WAIT_COUNT = 100;
+ private static byte[] mLastReceivedPdu = null;
+ private static boolean sDataSet = false;
+
+ private class IDataVerifyStub extends IDataVerify.Stub {
+ public Context mContext;
+
+ public IDataVerifyStub() {
+ }
+
+ boolean arrayCompare(byte[] arr1, byte[] arr2) {
+ int i;
+
+ if (arr1 == null || arr2 == null) {
+ if (arr1 == null) {
+ Log.w(LOG_TAG, "arr1 is null");
+ } else {
+ Log.w(LOG_TAG, "arr2 is null");
+ }
+ return false;
+ }
+
+ if (arr1.length != arr2.length) {
+ return false;
+ }
+
+ for (i = 0; i < arr1.length; i++) {
+ if (arr1[i] != arr2[i]) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Compare pdu and received pdu
+ */
+ public synchronized boolean verifyData(byte[] pdu) {
+ int cnt = 0;
+
+ while (!sDataSet) {
+ // wait for the activity receive data.
+ try {
+ Thread.sleep(TIME_WAIT);
+ if (cnt++ > WAIT_COUNT) {
+ // don't wait more than 10 sec.
+ return false;
+ }
+ } catch (InterruptedException e) {}
+ }
+
+ Log.v(LOG_TAG, "verify pdu");
+ boolean ret = arrayCompare(pdu, mLastReceivedPdu);
+ return ret;
+ }
+
+ /**
+ * Clear the old data. This method must be called before starting the test
+ */
+ public void resetData() {
+ mLastReceivedPdu = null;
+ sDataSet = false;
+ }
+ }
+
+ private final IDataVerifyStub binder = new IDataVerifyStub();
+
+ /**
+ * Constructor
+ */
+ public DataVerify() {
+ }
+
+ /**
+ * Receiver application must call this method when it receives the wap push message
+ */
+ public static void SetLastReceivedPdu(byte[] pdu) {
+ mLastReceivedPdu = pdu;
+ sDataSet = true;
+ }
+
+ @Override
+ public IBinder onBind(Intent arg0) {
+ return binder;
+ }
+
+}
+
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java
new file mode 100644
index 0000000..5f5f121
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush.unitTests;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * A sample wap push receiver application for existing framework
+ * This class is listening for "application/vnd.oma.drm.rights+xml" message
+ */
+public class DrmReceiver extends BroadcastReceiver {
+ private static final String LOG_TAG = "WAP PUSH";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(LOG_TAG, "DrmReceiver received.");
+
+ byte[] body;
+ byte[] header;
+
+ body = intent.getByteArrayExtra("data");
+ header = intent.getByteArrayExtra("header");
+
+ Log.d(LOG_TAG, "header:");
+ Log.d(LOG_TAG, HexDump.dumpHexString(header));
+ Log.d(LOG_TAG, "body:");
+ Log.d(LOG_TAG, HexDump.dumpHexString(body));
+
+ DataVerify.SetLastReceivedPdu(body);
+ }
+
+}
+
+
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl
similarity index 64%
copy from telephony/java/com/android/internal/telephony/gsm/stk/StkException.java
copy to packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl
index 86de366..f0670fa 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,20 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
-
-import android.util.AndroidException;
-
+package com.android.smspush.unitTests;
/**
- * Base class for all the exceptions in STK service.
- *
- * {@hide}
+ * Interface to receiver application data verifyer class
*/
-class StkException extends AndroidException {
- public StkException() {
- super();
- }
+interface IDataVerify {
+ /**
+ * Verify data
+ */
+ boolean verifyData(in byte[] pdu);
+
+ /**
+ * Initialize data
+ */
+ void resetData();
}
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java
new file mode 100644
index 0000000..07f55ea
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush.unitTests;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * Activity type receiver application
+ */
+public class ReceiverActivity extends Activity {
+ private static final String LOG_TAG = "WAP PUSH";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d(LOG_TAG, "activity created!!");
+
+ Intent in = getIntent();
+ byte[] body;
+ byte[] header;
+
+ body = in.getByteArrayExtra("data");
+ header = in.getByteArrayExtra("header");
+
+ Log.d(LOG_TAG, "header:");
+ Log.d(LOG_TAG, HexDump.dumpHexString(header));
+ Log.d(LOG_TAG, "body:");
+ Log.d(LOG_TAG, HexDump.dumpHexString(body));
+
+ DataVerify.SetLastReceivedPdu(body);
+
+ finish();
+
+ }
+}
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java
new file mode 100644
index 0000000..b024bf5
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush.unitTests;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+/**
+ * Service type receiver application
+ */
+public class ReceiverService extends Service {
+ private static final String LOG_TAG = "WAP PUSH";
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.d(LOG_TAG, "Receiver service created");
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.d(LOG_TAG, "Receiver service started");
+
+ byte[] body;
+ byte[] header;
+ body = intent.getByteArrayExtra("data");
+ header = intent.getByteArrayExtra("header");
+
+ Log.d(LOG_TAG, "header:");
+ Log.d(LOG_TAG, HexDump.dumpHexString(header));
+ Log.d(LOG_TAG, "body:");
+ Log.d(LOG_TAG, HexDump.dumpHexString(body));
+
+ DataVerify.SetLastReceivedPdu(body);
+ return START_STICKY;
+ }
+}
+
+
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
new file mode 100644
index 0000000..9b0a36b
--- /dev/null
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
@@ -0,0 +1,2513 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.smspush.unitTests;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.provider.Telephony.Sms.Intents;
+import android.test.ServiceTestCase;
+import android.util.Log;
+import android.util.Config;
+
+import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.IWapPushManager;
+import com.android.internal.telephony.WapPushManagerParams;
+import com.android.internal.telephony.WspTypeDecoder;
+import com.android.internal.util.HexDump;
+import com.android.smspush.WapPushManager;
+
+import java.util.Random;
+
+/**
+ * This is a simple framework for a test of a Service. See {@link android.test.ServiceTestCase
+ * ServiceTestCase} for more information on how to write and extend service tests.
+ *
+ * To run this test, you can type:
+ * adb shell am instrument -w \
+ * -e class com.android.smspush.unitTests.WapPushTest \
+ * com.android.smspush.unitTests/android.test.InstrumentationTestRunner
+ */
+public class WapPushTest extends ServiceTestCase<WapPushManager> {
+ private static final String LOG_TAG = "WAP PUSH";
+ private static final boolean LOCAL_LOGV = false;
+ private static final int TIME_WAIT = 100;
+
+ protected int mAppIdValue = 0x8002;
+ protected String mAppIdName = "x-wap-application:*";
+ protected int mContentTypeValue = 0x030a;
+ protected String mContentTypeName = "application/vnd.wap.sic";
+
+ protected String mPackageName;
+ protected String mClassName;
+
+ protected byte[] mGsmHeader = {
+ (byte) 0x00, // sc address
+ (byte) 0x40, // TP-MTI
+ (byte) 0x04, // sender address length?
+ (byte) 0x81, (byte) 0x55, (byte) 0x45, // sender address?
+ (byte) 0x00, // data schema
+ (byte) 0x00, // proto ID
+ (byte) 0x01, (byte) 0x60, (byte) 0x12, (byte) 0x31,
+ (byte) 0x74, (byte) 0x34, (byte) 0x63 // time stamp
+ };
+
+ protected byte[] mUserDataHeader = {
+ (byte) 0x07, // UDH len
+ (byte) 0x06, // header len
+ (byte) 0x05, // port addressing type?
+ (byte) 0x00, // dummy
+ (byte) 0x0B, (byte) 0x84, // dest port
+ (byte) 0x23, (byte) 0xF0 // src port
+ };
+
+ protected byte[] mWspHeader;
+
+ protected byte[] mMessageBody = {
+ (byte) 0x00,
+ (byte) 0x01,
+ (byte) 0x02,
+ (byte) 0x03,
+ (byte) 0x04,
+ (byte) 0x05,
+ (byte) 0x06,
+ (byte) 0x07,
+ (byte) 0x08,
+ (byte) 0x09,
+ (byte) 0x0a,
+ (byte) 0x0b,
+ (byte) 0x0c,
+ (byte) 0x0d,
+ (byte) 0x0e,
+ (byte) 0x0f
+ };
+
+ protected int mWspHeaderStart;
+ protected int mWspHeaderLen;
+ protected int mWspContentTypeStart;
+
+ /**
+ * OMA application ID in binary form
+ * http://www.openmobilealliance.org/tech/omna/omna-push-app-id.aspx
+ */
+ final int[] OMA_APPLICATION_ID_VALUES = new int[] {
+ 0x00,
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07,
+ 0x08,
+ 0x09,
+ 0x0A,
+ 0x8000,
+ 0x8001,
+ 0x8002,
+ 0x8003,
+ 0x8004,
+ 0x8005,
+ 0x8006,
+ 0x8007,
+ 0x8008,
+ 0x8009,
+ 0x800B,
+ 0x8010
+ };
+
+ /**
+ * OMA application ID in string form
+ * http://www.openmobilealliance.org/tech/omna/omna-push-app-id.aspx
+ */
+ final String[] OMA_APPLICATION_ID_NAMES = new String[] {
+ "x-wap-application:*",
+ "x-wap-application:push.sia",
+ "x-wap-application:wml.ua",
+ "x-wap-application:wta.ua",
+ "x-wap-application:mms.ua",
+ "x-wap-application:push.syncml",
+ "x-wap-application:loc.ua",
+ "x-wap-application:syncml.dm",
+ "x-wap-application:drm.ua",
+ "x-wap-application:emn.ua",
+ "x-wap-application:wv.ua",
+ "x-wap-microsoft:localcontent.ua",
+ "x-wap-microsoft:IMclient.ua",
+ "x-wap-docomo:imode.mail.ua",
+ "x-wap-docomo:imode.mr.ua",
+ "x-wap-docomo:imode.mf.ua",
+ "x-motorola:location.ua",
+ "x-motorola:now.ua",
+ "x-motorola:otaprov.ua",
+ "x-motorola:browser.ua",
+ "x-motorola:splash.ua",
+ "x-wap-nai:mvsw.command",
+ "x-wap-openwave:iota.ua"
+ };
+
+ /**
+ * OMA content type in binary form
+ * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
+ */
+ final int[] OMA_CONTENT_TYPE_VALUES = new int[] {
+ 0x00,
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07,
+ 0x08,
+ 0x09,
+ 0x0A,
+ 0x0B,
+ 0x0C,
+ 0x0D,
+ 0x0E,
+ 0x0F,
+ 0x10,
+ 0x11,
+ 0x12,
+ 0x13,
+ 0x14,
+ 0x15,
+ 0x16,
+ 0x17,
+ 0x18,
+ 0x19,
+ 0x1A,
+ 0x1B,
+ 0x1C,
+ 0x1D,
+ 0x1E,
+ 0x1F,
+ 0x20,
+ 0x21,
+ 0x22,
+ 0x23,
+ 0x24,
+ 0x25,
+ 0x26,
+ 0x27,
+ 0x28,
+ 0x29,
+ 0x2A,
+ 0x2B,
+ 0x2C,
+ 0x2D,
+ 0x2E,
+ 0x2F,
+ 0x30,
+ 0x31,
+ 0x32,
+ 0x33,
+ 0x34,
+ 0x35,
+ 0x36,
+ 0x37,
+ 0x38,
+ 0x39,
+ 0x3A,
+ 0x3B,
+ 0x3C,
+ 0x3D,
+ 0x3E,
+ 0x3F,
+ 0x40,
+ 0x41,
+ 0x42,
+ 0x43,
+ 0x44,
+ 0x45,
+ 0x46,
+ 0x47,
+ 0x48,
+ 0x49,
+ 0x4A,
+ 0x4B,
+ 0x4C,
+ 0x4D,
+ 0x4E,
+ 0x4F,
+ 0x50,
+ 0x51,
+ 0x52,
+ 0x53,
+ 0x54,
+// 0x55,
+// 0x56,
+// 0x57,
+// 0x58,
+ 0x0201,
+ 0x0202,
+ 0x0203,
+ 0x0204,
+ 0x0205,
+ 0x0206,
+ 0x0207,
+ 0x0208,
+ 0x0209,
+ 0x020A,
+ 0x020B,
+ 0x020C,
+ 0x0300,
+ 0x0301,
+ 0x0302,
+ 0x0303,
+ 0x0304,
+ 0x0305,
+ 0x0306,
+ 0x0307,
+ 0x0308,
+ 0x0309,
+ 0x030A,
+ 0x030B,
+ 0x030C,
+ 0x030D,
+ 0x030E,
+ 0x030F,
+ 0x0310,
+ 0x0311,
+ 0x0312,
+ 0x0313,
+ 0x0314,
+ 0x0315,
+ 0x0316,
+ 0x0317,
+ 0x0318,
+ 0x0319,
+ 0x031A,
+ 0x031B
+ /*0x031C,
+ 0x031D*/
+ };
+
+ /**
+ * OMA content type in string form
+ * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
+ */
+ final String[] OMA_CONTENT_TYPE_NAMES = new String[] {
+ "*/*",
+ "text/*",
+ "text/html",
+ "text/plain",
+ "text/x-hdml",
+ "text/x-ttml",
+ "text/x-vCalendar",
+ "text/x-vCard",
+ "text/vnd.wap.wml",
+ "text/vnd.wap.wmlscript",
+ "text/vnd.wap.wta-event",
+ "multipart/*",
+ "multipart/mixed",
+ "multipart/form-data",
+ "multipart/byterantes",
+ "multipart/alternative",
+ "application/*",
+ "application/java-vm",
+ "application/x-www-form-urlencoded",
+ "application/x-hdmlc",
+ "application/vnd.wap.wmlc",
+ "application/vnd.wap.wmlscriptc",
+ "application/vnd.wap.wta-eventc",
+ "application/vnd.wap.uaprof",
+ "application/vnd.wap.wtls-ca-certificate",
+ "application/vnd.wap.wtls-user-certificate",
+ "application/x-x509-ca-cert",
+ "application/x-x509-user-cert",
+ "image/*",
+ "image/gif",
+ "image/jpeg",
+ "image/tiff",
+ "image/png",
+ "image/vnd.wap.wbmp",
+ "application/vnd.wap.multipart.*",
+ "application/vnd.wap.multipart.mixed",
+ "application/vnd.wap.multipart.form-data",
+ "application/vnd.wap.multipart.byteranges",
+ "application/vnd.wap.multipart.alternative",
+ "application/xml",
+ "text/xml",
+ "application/vnd.wap.wbxml",
+ "application/x-x968-cross-cert",
+ "application/x-x968-ca-cert",
+ "application/x-x968-user-cert",
+ "text/vnd.wap.si",
+ "application/vnd.wap.sic",
+ "text/vnd.wap.sl",
+ "application/vnd.wap.slc",
+ "text/vnd.wap.co",
+ "application/vnd.wap.coc",
+ "application/vnd.wap.multipart.related",
+ "application/vnd.wap.sia",
+ "text/vnd.wap.connectivity-xml",
+ "application/vnd.wap.connectivity-wbxml",
+ "application/pkcs7-mime",
+ "application/vnd.wap.hashed-certificate",
+ "application/vnd.wap.signed-certificate",
+ "application/vnd.wap.cert-response",
+ "application/xhtml+xml",
+ "application/wml+xml",
+ "text/css",
+ "application/vnd.wap.mms-message",
+ "application/vnd.wap.rollover-certificate",
+ "application/vnd.wap.locc+wbxml",
+ "application/vnd.wap.loc+xml",
+ "application/vnd.syncml.dm+wbxml",
+ "application/vnd.syncml.dm+xml",
+ "application/vnd.syncml.notification",
+ "application/vnd.wap.xhtml+xml",
+ "application/vnd.wv.csp.cir",
+ "application/vnd.oma.dd+xml",
+ "application/vnd.oma.drm.message",
+ "application/vnd.oma.drm.content",
+ "application/vnd.oma.drm.rights+xml",
+ "application/vnd.oma.drm.rights+wbxml",
+ "application/vnd.wv.csp+xml",
+ "application/vnd.wv.csp+wbxml",
+ "application/vnd.syncml.ds.notification",
+ "audio/*",
+ "video/*",
+ "application/vnd.oma.dd2+xml",
+ "application/mikey",
+ "application/vnd.oma.dcd",
+ "application/vnd.oma.dcdc",
+// "text/x-vMessage",
+// "application/vnd.omads-email+wbxml",
+// "text/x-vBookmark",
+// "application/vnd.syncml.dm.notification",
+ "application/vnd.uplanet.cacheop-wbxml",
+ "application/vnd.uplanet.signal",
+ "application/vnd.uplanet.alert-wbxml",
+ "application/vnd.uplanet.list-wbxml",
+ "application/vnd.uplanet.listcmd-wbxml",
+ "application/vnd.uplanet.channel-wbxml",
+ "application/vnd.uplanet.provisioning-status-uri",
+ "x-wap.multipart/vnd.uplanet.header-set",
+ "application/vnd.uplanet.bearer-choice-wbxml",
+ "application/vnd.phonecom.mmc-wbxml",
+ "application/vnd.nokia.syncset+wbxml",
+ "image/x-up-wpng",
+ "application/iota.mmc-wbxml",
+ "application/iota.mmc-xml",
+ "application/vnd.syncml+xml",
+ "application/vnd.syncml+wbxml",
+ "text/vnd.wap.emn+xml",
+ "text/calendar",
+ "application/vnd.omads-email+xml",
+ "application/vnd.omads-file+xml",
+ "application/vnd.omads-folder+xml",
+ "text/directory;profile=vCard",
+ "application/vnd.wap.emn+wbxml",
+ "application/vnd.nokia.ipdc-purchase-response",
+ "application/vnd.motorola.screen3+xml",
+ "application/vnd.motorola.screen3+gzip",
+ "application/vnd.cmcc.setting+wbxml",
+ "application/vnd.cmcc.bombing+wbxml",
+ "application/vnd.docomo.pf",
+ "application/vnd.docomo.ub",
+ "application/vnd.omaloc-supl-init",
+ "application/vnd.oma.group-usage-list+xml",
+ "application/oma-directory+xml",
+ "application/vnd.docomo.pf2",
+ "application/vnd.oma.drm.roap-trigger+wbxml",
+ "application/vnd.sbm.mid2",
+ "application/vnd.wmf.bootstrap",
+ "application/vnc.cmcc.dcd+xml",
+ "application/vnd.sbm.cid",
+ "application/vnd.oma.bcast.provisioningtrigger",
+ /*"application/vnd.docomo.dm",
+ "application/vnd.oma.scidm.messages+xml"*/
+ };
+
+ private IDataVerify mIVerify = null;
+
+ ServiceConnection mConn = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.v(LOG_TAG, "data verify interface connected.");
+ mIVerify = IDataVerify.Stub.asInterface(service);
+ }
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ };
+
+ /**
+ * Main WapPushManager test module constructor
+ */
+ public WapPushTest() {
+ super(WapPushManager.class);
+ mClassName = this.getClass().getName();
+ mPackageName = this.getClass().getPackage().getName();
+ }
+
+ /**
+ * Initialize the verifier
+ */
+ @Override
+ public void setUp() {
+ try {
+ super.setUp();
+ // get verifier
+ getContext().bindService(new Intent(IDataVerify.class.getName()),
+ mConn, Context.BIND_AUTO_CREATE);
+ } catch (Exception e) {
+ Log.w(LOG_TAG, "super exception");
+ }
+ // Log.d(LOG_TAG, "test setup");
+ }
+
+ private IWapPushManager mWapPush = null;
+ IWapPushManager getInterface() {
+ if (mWapPush != null) return mWapPush;
+ Intent startIntent = new Intent();
+ startIntent.setClass(getContext(), WapPushManager.class);
+ IBinder service = bindService(startIntent);
+
+ mWapPush = IWapPushManager.Stub.asInterface(service);
+ return mWapPush;
+ }
+
+ /*
+ * All methods need to start with 'test'.
+ * Use various assert methods to pass/fail the test case.
+ */
+ protected void utAddPackage(boolean need_sig, boolean more_proc) {
+ IWapPushManager iwapman = getInterface();
+
+ // insert new data
+ try {
+ assertTrue(iwapman.addPackage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ // verify the data
+ WapPushManager wpman = getService();
+ assertTrue(wpman.verifyData(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+ }
+
+ /**
+ * Add package test
+ */
+ public void testAddPackage1() {
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+
+ utAddPackage(true, true);
+ mAppIdValue += 10;
+ utAddPackage(true, false);
+ mContentTypeValue += 20;
+ utAddPackage(false, true);
+ mContentTypeValue += 20;
+ utAddPackage(false, false);
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+
+ // clean up data
+ try {
+ IWapPushManager iwapman = getInterface();
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ mAppIdValue += 10;
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ mContentTypeValue += 20;
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ mContentTypeValue += 20;
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /**
+ * Add duprecated package test.
+ */
+ public void testAddPackage2() {
+ try {
+ IWapPushManager iwapman = getInterface();
+
+ // set up data
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+ false, false);
+ iwapman.addPackage(Integer.toString(mAppIdValue + 10),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+ false, false);
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue + 10), mPackageName, mClassName, 0,
+ false, false);
+
+ assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+ false, false));
+ assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue + 10),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName, 0,
+ false, false));
+ assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue + 10), mPackageName, mClassName, 0,
+ false, false));
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue + 10), mPackageName, mClassName);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ }
+
+ protected void utUpdatePackage(boolean need_sig, boolean more_proc) {
+ IWapPushManager iwapman = getInterface();
+
+ // insert new data
+ try {
+ assertTrue(iwapman.updatePackage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ // verify the data
+ WapPushManager wpman = getService();
+ assertTrue(wpman.verifyData(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc));
+ }
+
+ /**
+ * Updating package test
+ */
+ public void testUpdatePackage1() {
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+
+ // set up data
+ try {
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ mAppIdValue += 10;
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ mContentTypeValue += 20;
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ mContentTypeValue += 20;
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ utUpdatePackage(false, false);
+ mAppIdValue += 10;
+ utUpdatePackage(false, true);
+ mContentTypeValue += 20;
+ utUpdatePackage(true, false);
+ mContentTypeValue += 20;
+ utUpdatePackage(true, true);
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+
+ // clean up data
+ try {
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ mAppIdValue += 10;
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ mContentTypeValue += 20;
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ mContentTypeValue += 20;
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /**
+ * Updating invalid package test
+ */
+ public void testUpdatePackage2() {
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+
+ try {
+ // set up data
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ assertFalse(iwapman.updatePackage(
+ Integer.toString(mAppIdValue + 10),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName, 0, false, false));
+ assertFalse(iwapman.updatePackage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue + 10),
+ mPackageName, mClassName, 0, false, false));
+ assertTrue(iwapman.updatePackage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName + "dummy_data", mClassName, 0, false, false));
+ assertTrue(iwapman.updatePackage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName + "dummy_data", 0, false, false));
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName,
+ mClassName + "dummy_data");
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ }
+
+ protected void utDeletePackage() {
+ IWapPushManager iwapman = getInterface();
+
+ try {
+ assertTrue(iwapman.deletePackage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ // verify the data
+ WapPushManager wpman = getService();
+ assertTrue(!wpman.isDataExist(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName));
+ }
+
+ /**
+ * Deleting package test
+ */
+ public void testDeletePackage1() {
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+
+ // set up data
+ try {
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ mAppIdValue += 10;
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ mContentTypeValue += 20;
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ mContentTypeValue += 20;
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ utDeletePackage();
+ mAppIdValue += 10;
+ utDeletePackage();
+ mContentTypeValue += 20;
+ utDeletePackage();
+ mContentTypeValue += 20;
+ utDeletePackage();
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /**
+ * Deleting invalid package test
+ */
+ public void testDeletePackage2() {
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+
+ try {
+ // set up data
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ 0, false, false);
+
+ assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName));
+ assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue + 20), mPackageName, mClassName));
+ assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+ Integer.toString(mContentTypeValue + 20), mPackageName, mClassName));
+
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ }
+
+
+ protected int encodeUint32(int uint32Val, byte[] arr, int start) {
+ int bit = 1;
+ int topbit = 0;
+ int encodeLen;
+ int tmpVal;
+
+ assertTrue(uint32Val >= 0);
+ for (int i = 0; i < 31; i++) {
+ if ((bit & uint32Val) > 0) topbit = i;
+ bit = (bit << 1);
+ }
+ encodeLen = topbit/7 + 1;
+ if (arr == null) return encodeLen;
+
+ //Log.d(LOG_TAG, "uint32Val = " + Integer.toHexString(uint32Val) + ", topbit = "
+ // + topbit + ", encodeLen = " + encodeLen);
+
+ tmpVal = uint32Val;
+ for (int i = encodeLen - 1; i >= 0; i--) {
+ long val = 0;
+ if (i < encodeLen - 1) val = 0x80;
+ val |= tmpVal & 0x7f;
+ arr[start + i] = (byte) (val & 0xFF);
+ tmpVal = (tmpVal >> 7);
+ }
+ return encodeLen;
+ }
+
+ protected int encodeShortInt(int sintVal, byte[] arr, int start) {
+ int encodeLen = 0;
+
+ if (sintVal >= 0x80) return encodeLen;
+ encodeLen = 1;
+ arr[start] = (byte) (sintVal | 0x80);
+ return encodeLen;
+ }
+
+
+ /**
+ * Generate Random WSP header with integer application ID
+ */
+ protected void createRandomWspHeader(byte[] arr, Random rd, int headerStart,
+ boolean noAppId) {
+
+ boolean appIdAdded = false;
+
+ Log.d(LOG_TAG, "headerStart = " + headerStart + ", appId = " + mAppIdValue
+ + "(" + Integer.toHexString(mAppIdValue) + ")");
+ Log.d(LOG_TAG, "random arr length:" + arr.length);
+ String typename[] = new String[] { "short int", "long int", "string", "uint32"};
+
+ while (!appIdAdded) {
+ int type;
+ int index = headerStart;
+ int len = arr.length;
+ int i;
+ boolean addAppid = false;
+ int tmpVal = 0;
+ int tmpVal2 = 0;
+
+ while (true) {
+ int add;
+
+ /*
+ * field name
+ * 0: short int
+ * 1: long int
+ * 2: text
+ * (no uint for param value)
+ */
+ type = rd.nextInt(3);
+ switch (type) {
+ case 0: // header short integer
+ if (index > 100 && !appIdAdded) addAppid = true;
+ add = 1;
+ break;
+ case 1: // header long int
+ add = 1 + rd.nextInt(29);
+ break;
+ default: // header string
+ add = 2 + rd.nextInt(10);
+ break;
+ }
+ if (index + add >= len) break;
+
+ // fill header name
+ switch (type) {
+ case 0: // header short integer
+ if (!addAppid) {
+ do {
+ arr[index] = (byte) (0x80 | rd.nextInt(128));
+ } while (arr[index] == (byte) 0xaf);
+ } else {
+ Log.d(LOG_TAG, "appId added.");
+ arr[index] = (byte) 0xaf;
+ // if noAppId case, appId fld must be decieved.
+ if (noAppId) arr[index]++;
+ }
+ break;
+ case 1: // header long int
+ arr[index] = (byte) (add - 1);
+ tmpVal2 = 0;
+ for (i = 1; i < add; i++) {
+ tmpVal = rd.nextInt(255);
+ tmpVal2 = (tmpVal2 << 8) | tmpVal;
+ arr[index + i] = (byte) tmpVal;
+ }
+ // don't set application id
+ if (tmpVal2 == 0x2f) arr[index + 1]++;
+ break;
+ default: // header string
+ for (i = 0; i < add - 1; i++) {
+ tmpVal = rd.nextInt(127);
+ if (tmpVal < 32) tmpVal= (32 + tmpVal);
+ arr[index + i] = (byte) tmpVal;
+ }
+ arr[index + i] = (byte) 0x0;
+ break;
+ }
+
+ if (LOCAL_LOGV) {
+ Log.d(LOG_TAG, "field name index:" + index);
+ Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+ if (type != 2) {
+ for (i = index; i< index + add; i++) {
+ System.out.print(Integer.toHexString(0xff & arr[i]));
+ System.out.print(' ');
+ }
+ } else {
+ System.out.print(Integer.toHexString(0xff & arr[index]));
+ System.out.print(' ');
+ String str = new String(arr, index + 1, add - 2);
+ for (i = 0; i < str.length(); i++) {
+ System.out.print(str.charAt(i));
+ System.out.print(' ');
+ }
+ }
+ System.out.print('\n');
+ }
+ index += add;
+
+
+ /*
+ * field value
+ * 0: short int
+ * 1: long int
+ * 2: text
+ * 3: uint
+ */
+ if (addAppid) {
+ type = 1;
+ } else {
+ type = rd.nextInt(4);
+ }
+ switch (type) {
+ case 0: // header short integer
+ add = 1;
+ break;
+ case 1: // header long int
+ if (addAppid) {
+ int bit = 1;
+ int topBit = 0;
+
+ for (i = 0; i < 31; i++) {
+ if ((mAppIdValue & bit) > 0) topBit = i;
+ bit = (bit << 1);
+ }
+ add = 2 + topBit/8;
+ } else {
+ add = 1 + rd.nextInt(29);
+ }
+ break;
+ case 2: // header string
+ add = 2 + rd.nextInt(10);
+ break;
+ default: // uint32
+ add = 6;
+ }
+ if (index + add >= len) break;
+
+ // fill field value
+ switch (type) {
+ case 0: // header short int
+ arr[index] = (byte) (0x80 | rd.nextInt(128));
+ break;
+ case 1: // header long int
+ if (addAppid) {
+ addAppid = false;
+ appIdAdded = true;
+
+ arr[index] = (byte) (add - 1);
+ tmpVal = mAppIdValue;
+ for (i = add; i > 1; i--) {
+ arr[index + i - 1] = (byte) (tmpVal & 0xff);
+ tmpVal = (tmpVal >> 8);
+ }
+ } else {
+ arr[index] = (byte) (add - 1);
+ for (i = 1; i < add; i++) {
+ arr[index + i] = (byte) rd.nextInt(255);
+ }
+ }
+ break;
+ case 2:// header string
+ for (i = 0; i < add - 1; i++) {
+ tmpVal = rd.nextInt(127);
+ if (tmpVal < 32) tmpVal= (32 + tmpVal);
+ arr[index + i] = (byte) tmpVal;
+ }
+ arr[index + i] = (byte) 0x0;
+ break;
+ default: // header uvarint
+ arr[index] = (byte) 31;
+ tmpVal = rd.nextInt(0x0FFFFFFF);
+ add = 1 + encodeUint32(tmpVal, null, index + 1);
+ encodeUint32(tmpVal, arr, index + 1);
+ break;
+
+ }
+
+ if (LOCAL_LOGV) {
+ Log.d(LOG_TAG, "field value index:" + index);
+ Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+ if (type != 2) {
+ for (i = index; i< index + add; i++) {
+ System.out.print(Integer.toHexString(0xff & arr[i]));
+ System.out.print(' ');
+ }
+ } else {
+ System.out.print(Integer.toHexString(0xff & arr[index]));
+ System.out.print(' ');
+ String str = new String(arr, index + 1, add - 2);
+ for (i = 0; i < str.length(); i++) {
+ System.out.print(str.charAt(i));
+ System.out.print(' ');
+ }
+ }
+ System.out.print('\n');
+ }
+ index += add;
+ }
+ if (noAppId) break;
+ }
+
+ Log.d(LOG_TAG, HexDump.dumpHexString(arr));
+ }
+
+ /**
+ * Generate Random WSP header with string application ID
+ */
+ protected void createRandomWspHeaderStrAppId(byte[] arr, Random rd, int headerStart,
+ boolean randomStr) {
+
+ boolean appIdAdded = false;
+
+ Log.d(LOG_TAG, "random arr length:" + arr.length);
+ String typename[] = new String[] { "short int", "long int", "string", "uint32"};
+
+ while (!appIdAdded) {
+ int type;
+ int index = headerStart;
+ int len = arr.length;
+ int i;
+ boolean addAppid = false;
+ int tmpVal = 0;
+ int tmpVal2 = 0;
+
+ while (true) {
+ int add;
+
+ /*
+ * field name
+ * 0: short int
+ * 1: long int
+ * 2: text
+ * (no uint for param value)
+ */
+ type = rd.nextInt(3);
+ switch (type) {
+ case 0: // header short integer
+ if (index > 100 && !appIdAdded) addAppid = true;
+ add = 1;
+ break;
+ case 1: // header long int
+ add = 1 + rd.nextInt(29);
+ break;
+ default: // header string
+ add = 2 + rd.nextInt(10);
+ break;
+ }
+ if (index + add >= len) break;
+
+ // fill header name
+ switch (type) {
+ case 0: // header short integer
+ if (!addAppid) {
+ do {
+ arr[index] = (byte) (0x80 | rd.nextInt(128));
+ } while (arr[index] == (byte) 0xaf);
+ } else {
+ Log.d(LOG_TAG, "appId added.");
+ arr[index] = (byte) 0xaf;
+ }
+ break;
+ case 1: // header long int
+ arr[index] = (byte) (add - 1);
+ tmpVal2 = 0;
+ for (i = 1; i < add; i++) {
+ tmpVal = rd.nextInt(255);
+ tmpVal2 = (tmpVal2 << 8) | tmpVal;
+ arr[index + i] = (byte) tmpVal;
+ }
+ // don't set application id
+ if (tmpVal2 == 0x2f) arr[index + 1]++;
+ break;
+ default: // header string
+ for (i = 0; i < add - 1; i++) {
+ tmpVal = rd.nextInt(127);
+ if (tmpVal < 32) tmpVal= (32 + tmpVal);
+ arr[index + i] = (byte) tmpVal;
+ }
+ arr[index + i] = (byte) 0x0;
+ break;
+ }
+
+ if (LOCAL_LOGV) {
+ Log.d(LOG_TAG, "field name index:" + index);
+ Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+ if (type != 2) {
+ for (i = index; i < index + add; i++) {
+ System.out.print(Integer.toHexString(0xff & arr[i]));
+ System.out.print(' ');
+ }
+ } else {
+ System.out.print(Integer.toHexString(0xff & arr[index]));
+ System.out.print(' ');
+ String str = new String(arr, index + 1, add - 2);
+ for (i = 0; i < str.length(); i++) {
+ System.out.print(str.charAt(i));
+ System.out.print(' ');
+ }
+ }
+ System.out.print('\n');
+ }
+ index += add;
+
+
+ /*
+ * field value
+ * 0: short int
+ * 1: long int
+ * 2: text
+ * 3: uint
+ */
+ if (addAppid) {
+ type = 2;
+ } else {
+ type = rd.nextInt(4);
+ }
+ switch (type) {
+ case 0: // header short integer
+ add = 1;
+ break;
+ case 1: // header long int
+ add = 1 + rd.nextInt(29);
+ break;
+ case 2: // header string
+ if (addAppid) {
+ if (randomStr) {
+ add = 1 + rd.nextInt(10);
+ byte[] randStr= new byte[add];
+ for (i = 0; i < add; i++) {
+ tmpVal = rd.nextInt(127);
+ if (tmpVal < 32) tmpVal= (32 + tmpVal);
+ randStr[i] = (byte) tmpVal;
+ }
+ mAppIdName = new String(randStr);
+ }
+ add = mAppIdName.length() + 1;
+ } else {
+ add = 2 + rd.nextInt(10);
+ }
+ break;
+ default: // uint32
+ add = 6;
+ }
+ if (index + add >= len) break;
+
+ // fill field value
+ switch (type) {
+ case 0: // header short int
+ arr[index] = (byte) (0x80 | rd.nextInt(128));
+ break;
+ case 1: // header long int
+ arr[index] = (byte) (add - 1);
+ for (i = 1; i < add; i++)
+ arr[index + i] = (byte) rd.nextInt(255);
+ break;
+ case 2:// header string
+ if (addAppid) {
+ addAppid = false;
+ appIdAdded = true;
+ for (i = 0; i < add - 1; i++) {
+ arr[index + i] = (byte) (mAppIdName.charAt(i));
+ }
+ Log.d(LOG_TAG, "mAppIdName added [" + mAppIdName + "]");
+ } else {
+ for (i = 0; i < add - 1; i++) {
+ tmpVal = rd.nextInt(127);
+ if (tmpVal < 32) tmpVal= (32 + tmpVal);
+ arr[index + i] = (byte) tmpVal;
+ }
+ }
+ arr[index + i] = (byte) 0x0;
+ break;
+ default: // header uvarint
+ arr[index] = (byte) 31;
+ tmpVal = rd.nextInt(0x0FFFFFFF);
+ add = 1 + encodeUint32(tmpVal, null, index + 1);
+ encodeUint32(tmpVal, arr, index + 1);
+ break;
+
+ }
+
+ if (LOCAL_LOGV) {
+ Log.d(LOG_TAG, "field value index:" + index);
+ Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add);
+ if (type != 2) {
+ for (i = index; i < index + add; i++) {
+ System.out.print(Integer.toHexString(0xff & arr[i]));
+ System.out.print(' ');
+ }
+ } else {
+ System.out.print(Integer.toHexString(0xff & arr[index]));
+ System.out.print(' ');
+ String str = new String(arr, index + 1, add - 2);
+ for (i = 0; i < str.length(); i++) {
+ System.out.print(str.charAt(i));
+ System.out.print(' ');
+ }
+ }
+ System.out.print('\n');
+ }
+ index += add;
+ }
+ }
+
+ Log.d(LOG_TAG, "headerStart = " + headerStart + ", mAppIdName = " + mAppIdName);
+ Log.d(LOG_TAG, HexDump.dumpHexString(arr));
+ }
+
+ protected byte[] createPDU(int testNum) {
+ byte[] array = null;
+ // byte[] wsp = null;
+
+ switch (testNum) {
+ // sample pdu
+ case 1:
+ byte[] array1 = {
+ (byte) 0x00, // TID
+ (byte) 0x06, // Type = wap push
+ (byte) 0x00, // Length to be set later.
+
+ // Content-Type
+ (byte) 0x03, (byte) 0x02,
+ (byte) ((mContentTypeValue >> 8) & 0xff),
+ (byte) (mContentTypeValue & 0xff),
+
+ // Application-id
+ (byte) 0xaf, (byte) 0x02,
+ (byte) ((mAppIdValue >> 8) & 0xff),
+ (byte) (mAppIdValue& 0xff)
+ };
+ array1[2] = (byte) (array1.length - 3);
+ mWspHeader = array1;
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + 7;
+ mWspHeaderLen = array1.length;
+ break;
+
+ // invalid wsp header
+ case 2:
+ byte[] array2 = {
+ (byte) 0x00, // invalid data
+ };
+ mWspHeader = array2;
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length;
+ mWspHeaderLen = array2.length;
+ break;
+
+ // random wsp header
+ case 3:
+ Random rd = new Random();
+ int arrSize = 150 + rd.nextInt(100);
+ byte[] array3 = new byte[arrSize];
+ int hdrEncodeLen;
+
+ array3[0] = (byte) 0x0;
+ array3[1] = (byte) 0x6;
+ hdrEncodeLen = encodeUint32(array3.length, null, 2);
+ hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+ array3[hdrEncodeLen + 2] = (byte) 0x3;
+ array3[hdrEncodeLen + 3] = (byte) 0x2;
+ array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+ array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+ createRandomWspHeader(array3, rd, hdrEncodeLen + 6, false);
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+ mWspHeaderLen = array3.length;
+
+ Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+ + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+ mWspHeader = array3;
+ break;
+
+ // random wsp header w/o appid
+ case 4:
+ rd = new Random();
+ arrSize = 150 + rd.nextInt(100);
+ array3 = new byte[arrSize];
+
+ array3[0] = (byte) 0x0;
+ array3[1] = (byte) 0x6;
+ hdrEncodeLen = encodeUint32(array3.length, null, 2);
+ hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+ array3[hdrEncodeLen + 2] = (byte) 0x3;
+ array3[hdrEncodeLen + 3] = (byte) 0x2;
+ array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+ array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+ createRandomWspHeader(array3, rd, hdrEncodeLen + 6, true);
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+ mWspHeaderLen = array3.length;
+
+ Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+ + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+ mWspHeader = array3;
+ break;
+
+ // random wsp header w/ random appid string
+ case 5:
+ rd = new Random();
+ arrSize = 150 + rd.nextInt(100);
+ array3 = new byte[arrSize];
+
+ array3[0] = (byte) 0x0;
+ array3[1] = (byte) 0x6;
+ hdrEncodeLen = encodeUint32(array3.length, null, 2);
+ hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+ array3[hdrEncodeLen + 2] = (byte) 0x3;
+ array3[hdrEncodeLen + 3] = (byte) 0x2;
+ array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+ array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+ createRandomWspHeaderStrAppId(array3, rd, hdrEncodeLen + 6, true);
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+ mWspHeaderLen = array3.length;
+
+ Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+ + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+ mWspHeader = array3;
+ break;
+
+ // random wsp header w/ OMA appid string
+ case 6:
+ rd = new Random();
+ arrSize = 150 + rd.nextInt(100);
+ array3 = new byte[arrSize];
+
+ array3[0] = (byte) 0x0;
+ array3[1] = (byte) 0x6;
+ hdrEncodeLen = encodeUint32(array3.length, null, 2);
+ hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+ array3[hdrEncodeLen + 2] = (byte) 0x3;
+ array3[hdrEncodeLen + 3] = (byte) 0x2;
+ array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff);
+ array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff);
+ createRandomWspHeaderStrAppId(array3, rd, hdrEncodeLen + 6, false);
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6;
+ mWspHeaderLen = array3.length;
+
+ Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue
+ + "(" + Integer.toHexString(mContentTypeValue) + ")");
+
+ mWspHeader = array3;
+ break;
+
+ // random wsp header w/ OMA content type
+ case 7:
+ rd = new Random();
+ arrSize = 150 + rd.nextInt(100);
+ array3 = new byte[arrSize];
+
+ array3[0] = (byte) 0x0;
+ array3[1] = (byte) 0x6;
+ hdrEncodeLen = encodeUint32(array3.length, null, 2);
+ hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+
+ // encode content type
+ int contentLen = mContentTypeName.length();
+ int next = 2 + hdrEncodeLen;
+ mWspContentTypeStart = mGsmHeader.length + mUserDataHeader.length + next;
+ // next += encodeUint32(contentLen, array3, next);
+ int i;
+ Log.d(LOG_TAG, "mContentTypeName = " + mContentTypeName
+ + ", contentLen = " + contentLen);
+
+ for (i = 0; i < contentLen; i++) {
+ array3[next + i] = (byte) mContentTypeName.charAt(i);
+ }
+ array3[next + i] = (byte) 0x0;
+
+ createRandomWspHeader(array3, rd, next + contentLen + 1, false);
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length
+ + next + contentLen + 1;
+ mWspHeaderLen = array3.length;
+
+ mWspHeader = array3;
+ break;
+
+ // random wsp header w/ OMA content type, OMA app ID
+ case 8:
+ rd = new Random();
+ arrSize = 150 + rd.nextInt(100);
+ array3 = new byte[arrSize];
+
+ array3[0] = (byte) 0x0;
+ array3[1] = (byte) 0x6;
+ hdrEncodeLen = encodeUint32(array3.length, null, 2);
+ hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2);
+
+ // encode content type
+ contentLen = mContentTypeName.length();
+ next = 2 + hdrEncodeLen;
+ mWspContentTypeStart = mGsmHeader.length + mUserDataHeader.length + next;
+ // next += encodeUint32(contentLen, array3, next);
+ Log.d(LOG_TAG, "mContentTypeName = " + mContentTypeName
+ + ", contentLen = " + contentLen);
+
+ for (i = 0; i < contentLen; i++) {
+ array3[next + i] = (byte) mContentTypeName.charAt(i);
+ }
+ array3[next + i] = (byte) 0x0;
+
+ createRandomWspHeaderStrAppId(array3, rd, next + contentLen + 1, false);
+ mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length
+ + next + contentLen + 1;
+ mWspHeaderLen = array3.length;
+
+ mWspHeader = array3;
+ break;
+
+ default:
+ return null;
+ }
+ array = new byte[mGsmHeader.length + mUserDataHeader.length + mWspHeader.length
+ + mMessageBody.length];
+ System.arraycopy(mGsmHeader, 0, array, 0, mGsmHeader.length);
+ System.arraycopy(mUserDataHeader, 0, array,
+ mGsmHeader.length, mUserDataHeader.length);
+ System.arraycopy(mWspHeader, 0, array,
+ mGsmHeader.length + mUserDataHeader.length, mWspHeader.length);
+ System.arraycopy(mMessageBody, 0, array,
+ mGsmHeader.length + mUserDataHeader.length + mWspHeader.length,
+ mMessageBody.length);
+ return array;
+
+ }
+
+ Intent createIntent(int pduType, int tranId) {
+ Intent intent = new Intent();
+ intent.putExtra("transactionId", tranId);
+ intent.putExtra("pduType", pduType);
+ intent.putExtra("header", mGsmHeader);
+ intent.putExtra("data", mMessageBody);
+ // intent.putExtra("contentTypeParameters", null);
+ return intent;
+ }
+
+ /**
+ * Message processing test, start activity
+ */
+ public void testProcessMsg1() {
+ byte[] pdu = createPDU(1);
+ int headerLen = pdu.length -
+ (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+ int pduType = 6;
+ int tranId = 0;
+ String originalPackageName = mPackageName;
+ String originalClassName = mClassName;
+
+ try {
+
+ mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+
+ // set up data
+ IWapPushManager iwapman = getInterface();
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+ assertTrue((iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId))
+ & WapPushManagerParams.MESSAGE_HANDLED) ==
+ WapPushManagerParams.MESSAGE_HANDLED);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mPackageName = originalPackageName;
+ mClassName = originalClassName;
+ }
+
+ /**
+ * Message processing test, start service
+ */
+ public void testProcessMsg2() {
+ byte[] pdu = createPDU(1);
+ int headerLen = pdu.length - (mGsmHeader.length +
+ mUserDataHeader.length + mMessageBody.length);
+ int pduType = 6;
+ int tranId = 0;
+ String originalPackageName = mPackageName;
+ String originalClassName = mClassName;
+
+ try {
+
+ mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+ // set up data
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+ assertTrue((iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId))
+ & WapPushManagerParams.MESSAGE_HANDLED) ==
+ WapPushManagerParams.MESSAGE_HANDLED);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mPackageName = originalPackageName;
+ mClassName = originalClassName;
+ }
+
+ /**
+ * Message processing test, no signature
+ */
+ public void testProcessMsg3() {
+ byte[] pdu = createPDU(1);
+ int headerLen = pdu.length -
+ (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+ int pduType = 6;
+ int tranId = 0;
+ String originalPackageName = mPackageName;
+ String originalClassName = mClassName;
+
+ try {
+
+ mPackageName = "com.android.development";
+ mClassName = "com.android.development.Development";
+
+ // set up data
+ IWapPushManager iwapman = getInterface();
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, true, false);
+
+ assertFalse((iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId))
+ & WapPushManagerParams.MESSAGE_HANDLED) ==
+ WapPushManagerParams.MESSAGE_HANDLED);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mPackageName = originalPackageName;
+ mClassName = originalClassName;
+ }
+
+ IDataVerify getVerifyInterface() {
+ while (mIVerify == null) {
+ // wait for the activity receive data.
+ try {
+ Thread.sleep(TIME_WAIT);
+ } catch (InterruptedException e) {}
+ }
+ return mIVerify;
+ }
+
+
+ /**
+ * Message processing test, received body data verification test
+ */
+ public void testProcessMsg4() {
+ byte[] originalMessageBody = mMessageBody;
+ mMessageBody = new byte[] {
+ (byte) 0xee,
+ (byte) 0xff,
+ (byte) 0xee,
+ (byte) 0xff,
+ (byte) 0xee,
+ (byte) 0xff,
+ (byte) 0xee,
+ (byte) 0xff,
+ (byte) 0xee,
+ (byte) 0xff,
+ (byte) 0xee,
+ (byte) 0xff,
+ };
+
+ byte[] pdu = createPDU(1);
+ int headerLen = pdu.length -
+ (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+ int pduType = 6;
+ int tranId = 0;
+ String originalPackageName = mPackageName;
+ String originalClassName = mClassName;
+
+ try {
+ IWapPushManager iwapman = getInterface();
+ IDataVerify dataverify = getVerifyInterface();
+
+ dataverify.resetData();
+
+ // set up data
+ mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+ iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId));
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ assertTrue(dataverify.verifyData(mMessageBody));
+
+ // set up data
+ dataverify.resetData();
+ mClassName = "com.android.smspush.unitTests.ReceiverService";
+ mMessageBody = new byte[] {
+ (byte) 0xaa,
+ (byte) 0xbb,
+ (byte) 0x11,
+ (byte) 0x22,
+ (byte) 0xaa,
+ (byte) 0xbb,
+ (byte) 0x11,
+ (byte) 0x22,
+ (byte) 0xaa,
+ (byte) 0xbb,
+ (byte) 0x11,
+ (byte) 0x22,
+ (byte) 0xaa,
+ (byte) 0xbb,
+ (byte) 0x11,
+ (byte) 0x22,
+ (byte) 0xaa,
+ (byte) 0xbb,
+ (byte) 0x11,
+ (byte) 0x22,
+ (byte) 0xaa,
+ (byte) 0xbb,
+ (byte) 0x11,
+ (byte) 0x22,
+ };
+ pdu = createPDU(1);
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+ iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId));
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ // Log.d(LOG_TAG, HexDump.dumpHexString(mMessageBody));
+ assertTrue(dataverify.verifyData(mMessageBody));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mPackageName = originalPackageName;
+ mClassName = originalClassName;
+ mMessageBody = originalMessageBody;
+ }
+
+ /**
+ * Message processing test, send invalid sms data
+ */
+ public void testProcessMsg5() {
+ byte[] pdu = createPDU(2);
+ int headerLen = pdu.length -
+ (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+ int pduType = 6;
+ int tranId = 0;
+ String originalPackageName = mPackageName;
+ String originalClassName = mClassName;
+
+ try {
+
+ mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+
+ // set up data
+ IWapPushManager iwapman = getInterface();
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+ assertTrue((iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId))
+ & WapPushManagerParams.MESSAGE_HANDLED) ==
+ WapPushManagerParams.MESSAGE_HANDLED);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mPackageName = originalPackageName;
+ mClassName = originalClassName;
+ }
+
+ /**
+ * Message processing test, no receiver application
+ */
+ public void testProcessMsg6() {
+ byte[] pdu = createPDU(1);
+ int headerLen = pdu.length -
+ (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length);
+ int pduType = 6;
+ int tranId = 0;
+ String originalPackageName = mPackageName;
+ String originalClassName = mClassName;
+
+ try {
+
+ mClassName = "com.android.smspush.unitTests.NoReceiver";
+
+ // set up data
+ IWapPushManager iwapman = getInterface();
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+ assertFalse((iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId))
+ & WapPushManagerParams.MESSAGE_HANDLED) ==
+ WapPushManagerParams.MESSAGE_HANDLED);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ // set up data
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+ assertFalse((iwapman.processMessage(
+ Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ createIntent(pduType, tranId))
+ & WapPushManagerParams.MESSAGE_HANDLED) ==
+ WapPushManagerParams.MESSAGE_HANDLED);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ mPackageName = originalPackageName;
+ mClassName = originalClassName;
+ }
+
+ /**
+ * WspTypeDecoder test, normal pdu
+ */
+ public void testDecoder1() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ Random rd = new Random();
+
+ for (int i = 0; i < 10; i++) {
+ mAppIdValue = rd.nextInt(0xFFFF);
+ byte[] pdu = createPDU(1);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+ mWspHeaderStart + mWspHeaderLen - 1);
+ assertTrue(res);
+
+ int index = (int) pduDecoder.getValue32();
+ res = pduDecoder.decodeXWapApplicationId(index);
+ assertTrue(res);
+
+ Log.d(LOG_TAG, "mAppIdValue: " + mAppIdValue
+ + ", val: " + pduDecoder.getValue32());
+ assertTrue(mAppIdValue == (int) pduDecoder.getValue32());
+ }
+
+ mAppIdValue = originalAppIdValue;
+ }
+
+ /**
+ * WspTypeDecoder test, no header
+ */
+ public void testDecoder2() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ Random rd = new Random();
+
+ mAppIdValue = rd.nextInt(0xFFFF);
+ byte[] pdu = createPDU(2);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+ mWspHeaderStart + mWspHeaderLen - 1);
+ assertFalse(res);
+
+ mAppIdValue = originalAppIdValue;
+ }
+
+ /**
+ * WspTypeDecoder test, decode appid test
+ */
+ public void testDecoder3() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ Random rd = new Random();
+
+ for (int i = 0; i < 100; i++) {
+ mAppIdValue = rd.nextInt(0x0FFFFFFF);
+ mContentTypeValue = rd.nextInt(0x0FFF);
+ byte[] pdu = createPDU(3);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+ mWspHeaderStart + mWspHeaderLen - 1);
+ assertTrue(res);
+
+ int index = (int) pduDecoder.getValue32();
+ res = pduDecoder.decodeXWapApplicationId(index);
+ assertTrue(res);
+
+ Log.d(LOG_TAG, "mAppIdValue: " + mAppIdValue
+ + ", val: " + pduDecoder.getValue32());
+ assertTrue(mAppIdValue == (int) pduDecoder.getValue32());
+ }
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /*
+ public void testEnc() {
+ byte[] arr = new byte[20];
+ int index = 0;
+ index += encodeUint32(0x87a5, arr, index);
+ index += encodeUint32(0x1, arr, index);
+ index += encodeUint32(0x9b, arr, index);
+ index += encodeUint32(0x10, arr, index);
+ index += encodeUint32(0xe0887, arr, index);
+ index += encodeUint32(0x791a23d0, arr, index);
+
+ Log.d(LOG_TAG, HexDump.dumpHexString(arr));
+ }
+ */
+
+ /**
+ * WspTypeDecoder test, no appid test
+ */
+ public void testDecoder4() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ Random rd = new Random();
+
+ for (int i = 0; i < 100; i++) {
+ mAppIdValue = rd.nextInt(0x0FFFFFFF);
+ mContentTypeValue = rd.nextInt(0x0FFF);
+ byte[] pdu = createPDU(4);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+ mWspHeaderStart + mWspHeaderLen - 1);
+ assertFalse(res);
+
+ }
+
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /**
+ * WspTypeDecoder test, decode string appid test
+ */
+ public void testDecoder5() {
+ boolean res;
+ String originalAppIdName = mAppIdName;
+ int originalContentTypeValue = mContentTypeValue;
+ Random rd = new Random();
+
+ for (int i = 0; i < 10; i++) {
+ mAppIdValue = rd.nextInt(0x0FFFFFFF);
+ mContentTypeValue = rd.nextInt(0x0FFF);
+ byte[] pdu = createPDU(5);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+ mWspHeaderStart + mWspHeaderLen - 1);
+ assertTrue(res);
+
+ int index = (int) pduDecoder.getValue32();
+ res = pduDecoder.decodeXWapApplicationId(index);
+ assertTrue(res);
+
+ Log.d(LOG_TAG, "mAppIdValue: [" + mAppIdName + "], val: ["
+ + pduDecoder.getValueString() + "]");
+ assertTrue(mAppIdName.equals(pduDecoder.getValueString()));
+ }
+
+ mAppIdName = originalAppIdName;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /**
+ * WspTypeDecoder test, decode string appid test
+ */
+ public void testDecoder6() {
+ boolean res;
+ String originalAppIdName = mAppIdName;
+ int originalContentTypeValue = mContentTypeValue;
+ Random rd = new Random();
+
+ for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length; i++) {
+ mAppIdName = OMA_APPLICATION_ID_NAMES[i];
+ mContentTypeValue = rd.nextInt(0x0FFF);
+ byte[] pdu = createPDU(6);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.seekXWapApplicationId(mWspHeaderStart,
+ mWspHeaderStart + mWspHeaderLen - 1);
+ assertTrue(res);
+
+ int index = (int) pduDecoder.getValue32();
+ res = pduDecoder.decodeXWapApplicationId(index);
+ assertTrue(res);
+
+ Log.d(LOG_TAG, "mAppIdValue: [" + mAppIdName + "], val: ["
+ + pduDecoder.getValueString() + "]");
+ assertTrue(mAppIdName.equals(pduDecoder.getValueString()));
+ }
+
+ mAppIdName = originalAppIdName;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+ /**
+ * WspTypeDecoder test, decode OMA content type
+ */
+ public void testDecoder7() {
+ boolean res;
+ String originalAppIdName = mAppIdName;
+ int originalContentTypeValue = mContentTypeValue;
+ Random rd = new Random();
+
+ for (int i = 0; i < OMA_CONTENT_TYPE_NAMES.length; i++) {
+ mContentTypeName = OMA_CONTENT_TYPE_NAMES[i];
+ byte[] pdu = createPDU(7);
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ res = pduDecoder.decodeContentType(mWspContentTypeStart);
+ assertTrue(res);
+
+ Log.d(LOG_TAG, "mContentTypeName: [" + mContentTypeName + "], val: ["
+ + pduDecoder.getValueString() + "]");
+ assertTrue(mContentTypeName.equals(pduDecoder.getValueString()));
+ }
+
+ mAppIdName = originalAppIdName;
+ mContentTypeValue = originalContentTypeValue;
+ }
+
+
+ /**
+ * Copied from WapPushOverSms.
+ * The code flow is not changed from the original.
+ */
+ public int dispatchWapPdu(byte[] pdu, IWapPushManager wapPushMan) {
+
+ if (Config.DEBUG) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
+
+ int index = 0;
+ int transactionId = pdu[index++] & 0xFF;
+ int pduType = pdu[index++] & 0xFF;
+ int headerLength = 0;
+
+ if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
+ (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
+ return Intents.RESULT_SMS_HANDLED;
+ }
+
+ WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu);
+
+ /**
+ * Parse HeaderLen(unsigned integer).
+ * From wap-230-wsp-20010705-a section 8.1.2
+ * The maximum size of a uintvar is 32 bits.
+ * So it will be encoded in no more than 5 octets.
+ */
+ if (pduDecoder.decodeUintvarInteger(index) == false) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Length error.");
+ return Intents.RESULT_SMS_GENERIC_ERROR;
+ }
+ headerLength = (int) pduDecoder.getValue32();
+ index += pduDecoder.getDecodedDataLength();
+
+ int headerStartIndex = index;
+
+ /**
+ * Parse Content-Type.
+ * From wap-230-wsp-20010705-a section 8.4.2.24
+ *
+ * Content-type-value = Constrained-media | Content-general-form
+ * Content-general-form = Value-length Media-type
+ * Media-type = (Well-known-media | Extension-Media) *(Parameter)
+ * Value-length = Short-length | (Length-quote Length)
+ * Short-length = <Any octet 0-30> (octet <= WAP_PDU_SHORT_LENGTH_MAX)
+ * Length-quote = <Octet 31> (WAP_PDU_LENGTH_QUOTE)
+ * Length = Uintvar-integer
+ */
+ if (pduDecoder.decodeContentType(index) == false) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
+ return Intents.RESULT_SMS_GENERIC_ERROR;
+ }
+
+ String mimeType = pduDecoder.getValueString();
+ long binaryContentType = pduDecoder.getValue32();
+ index += pduDecoder.getDecodedDataLength();
+
+ byte[] header = new byte[headerLength];
+ System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
+
+ byte[] intentData;
+
+ if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+ intentData = pdu;
+ } else {
+ int dataIndex = headerStartIndex + headerLength;
+ intentData = new byte[pdu.length - dataIndex];
+ System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
+ }
+
+ /**
+ * Seek for application ID field in WSP header.
+ * If application ID is found, WapPushManager substitute the message
+ * processing. Since WapPushManager is optional module, if WapPushManager
+ * is not found, legacy message processing will be continued.
+ */
+ if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) {
+ index = (int) pduDecoder.getValue32();
+ pduDecoder.decodeXWapApplicationId(index);
+ String wapAppId = pduDecoder.getValueString();
+ if (wapAppId == null) {
+ wapAppId = Integer.toString((int) pduDecoder.getValue32());
+ }
+
+ String contentType = ((mimeType == null) ?
+ Long.toString(binaryContentType) : mimeType);
+ if (Config.DEBUG) Log.v(LOG_TAG, "appid found: " + wapAppId + ":" + contentType);
+
+ try {
+ boolean processFurther = true;
+ // IWapPushManager wapPushMan = mWapConn.getWapPushManager();
+ if (wapPushMan == null) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "wap push manager not found!");
+ } else {
+ Intent intent = new Intent();
+ intent.putExtra("transactionId", transactionId);
+ intent.putExtra("pduType", pduType);
+ intent.putExtra("header", header);
+ intent.putExtra("data", intentData);
+ intent.putExtra("contentTypeParameters",
+ pduDecoder.getContentParameters());
+
+ int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
+ if (Config.DEBUG) Log.v(LOG_TAG, "procRet:" + procRet);
+ if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0
+ && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) {
+ processFurther = false;
+ }
+ }
+ if (!processFurther) {
+ return Intents.RESULT_SMS_HANDLED;
+ }
+ } catch (RemoteException e) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "remote func failed...");
+ }
+ }
+ if (Config.DEBUG) Log.v(LOG_TAG, "fall back to existing handler");
+
+ return Activity.RESULT_OK;
+ }
+
+ protected byte[] retrieveWspBody() {
+ byte[] array = new byte[mWspHeader.length + mMessageBody.length];
+
+ System.arraycopy(mWspHeader, 0, array, 0, mWspHeader.length);
+ System.arraycopy(mMessageBody, 0, array, mWspHeader.length, mMessageBody.length);
+ return array;
+ }
+
+ protected String getContentTypeName(int ctypeVal) {
+ int i;
+
+ for (i = 0; i < OMA_CONTENT_TYPE_VALUES.length; i++) {
+ if (ctypeVal == OMA_CONTENT_TYPE_VALUES[i]) {
+ return OMA_CONTENT_TYPE_NAMES[i];
+ }
+ }
+ return null;
+ }
+
+ protected boolean isContentTypeMapped(int ctypeVal) {
+ int i;
+
+ for (i = 0; i < OMA_CONTENT_TYPE_VALUES.length; i++) {
+ if (ctypeVal == OMA_CONTENT_TYPE_VALUES[i]) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Integration test 1, simple case
+ */
+ public void testIntegration1() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ String originalAppIdName = mAppIdName;
+ String originalContentTypeName = mContentTypeName;
+ String originalClassName = mClassName;
+ byte[] originalMessageBody = mMessageBody;
+ Random rd = new Random();
+
+ mMessageBody = new byte[100 + rd.nextInt(100)];
+ rd.nextBytes(mMessageBody);
+
+ byte[] pdu = createPDU(1);
+ byte[] wappushPdu = retrieveWspBody();
+
+
+ mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+ // Phone dummy = new DummyPhone(getContext());
+ // Phone gsm = PhoneFactory.getGsmPhone();
+ // GSMPhone gsm = new GSMPhone(getContext(), new SimulatedCommands(), null, true);
+ // WapPushOverSms dispatcher = new WapPushOverSms(dummy, null);
+
+ try {
+ // set up data
+ IWapPushManager iwapman = getInterface();
+ IDataVerify dataverify = getVerifyInterface();
+
+ dataverify.resetData();
+
+ if (isContentTypeMapped(mContentTypeValue)) {
+ // content type is mapped
+ mContentTypeName = getContentTypeName(mContentTypeValue);
+ Log.d(LOG_TAG, "mContentTypeValue mapping "
+ + mContentTypeName + ":" + mContentTypeValue);
+ } else {
+ mContentTypeName = Integer.toString(mContentTypeValue);
+ }
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ mContentTypeName, mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+ dispatchWapPdu(wappushPdu, iwapman);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ mContentTypeName, mPackageName, mClassName);
+
+ assertTrue(dataverify.verifyData(mMessageBody));
+ } catch (RemoteException e) {
+ }
+
+
+ mClassName = originalClassName;
+ mAppIdName = originalAppIdName;
+ mContentTypeName = originalContentTypeName;
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ mMessageBody = originalMessageBody;
+ }
+
+ /**
+ * Integration test 2, random mAppIdValue(int), all OMA content type
+ */
+ public void testIntegration2() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ String originalAppIdName = mAppIdName;
+ String originalContentTypeName = mContentTypeName;
+ String originalClassName = mClassName;
+ byte[] originalMessageBody = mMessageBody;
+ Random rd = new Random();
+
+ IWapPushManager iwapman = getInterface();
+ IDataVerify dataverify = getVerifyInterface();
+ mClassName = "com.android.smspush.unitTests.ReceiverActivity";
+
+ for (int i = 0; i < OMA_CONTENT_TYPE_NAMES.length; i++) {
+ mContentTypeName = OMA_CONTENT_TYPE_NAMES[i];
+ mAppIdValue = rd.nextInt(0x0FFFFFFF);
+
+ mMessageBody = new byte[100 + rd.nextInt(100)];
+ rd.nextBytes(mMessageBody);
+
+ byte[] pdu = createPDU(7);
+ byte[] wappushPdu = retrieveWspBody();
+
+ try {
+ dataverify.resetData();
+ // set up data
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ mContentTypeName, mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_ACTIVITY, false, false);
+
+ dispatchWapPdu(wappushPdu, iwapman);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ mContentTypeName, mPackageName, mClassName);
+
+ if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+ assertTrue(dataverify.verifyData(wappushPdu));
+ } else {
+ assertTrue(dataverify.verifyData(mMessageBody));
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+
+ mClassName = originalClassName;
+ mAppIdName = originalAppIdName;
+ mContentTypeName = originalContentTypeName;
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ mMessageBody = originalMessageBody;
+ }
+
+ /**
+ * Integration test 3, iterate OmaApplication ID, random binary content type
+ */
+ public void testIntegration3() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ String originalAppIdName = mAppIdName;
+ String originalContentTypeName = mContentTypeName;
+ String originalClassName = mClassName;
+ byte[] originalMessageBody = mMessageBody;
+ Random rd = new Random();
+
+ IWapPushManager iwapman = getInterface();
+ IDataVerify dataverify = getVerifyInterface();
+ mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+ for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length; i++) {
+ mAppIdName = OMA_APPLICATION_ID_NAMES[i];
+ mContentTypeValue = rd.nextInt(0x0FFF);
+
+ mMessageBody = new byte[100 + rd.nextInt(100)];
+ rd.nextBytes(mMessageBody);
+
+ byte[] pdu = createPDU(6);
+ byte[] wappushPdu = retrieveWspBody();
+
+ try {
+ dataverify.resetData();
+ // set up data
+ if (isContentTypeMapped(mContentTypeValue)) {
+ // content type is mapped to integer value
+ mContentTypeName = getContentTypeName(mContentTypeValue);
+ Log.d(LOG_TAG, "mContentTypeValue mapping "
+ + mContentTypeValue + ":" + mContentTypeName);
+ } else {
+ mContentTypeName = Integer.toString(mContentTypeValue);
+ }
+
+ iwapman.addPackage(mAppIdName,
+ mContentTypeName, mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+ dispatchWapPdu(wappushPdu, iwapman);
+
+ // clean up data
+ iwapman.deletePackage(mAppIdName,
+ mContentTypeName, mPackageName, mClassName);
+
+ if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+ assertTrue(dataverify.verifyData(wappushPdu));
+ } else {
+ assertTrue(dataverify.verifyData(mMessageBody));
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ mClassName = originalClassName;
+ mAppIdName = originalAppIdName;
+ mContentTypeName = originalContentTypeName;
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ mMessageBody = originalMessageBody;
+ }
+
+ /**
+ * Integration test 4, iterate OmaApplication ID, Oma content type
+ */
+ public void testIntegration4() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ String originalAppIdName = mAppIdName;
+ String originalContentTypeName = mContentTypeName;
+ String originalClassName = mClassName;
+ byte[] originalMessageBody = mMessageBody;
+ Random rd = new Random();
+
+ IWapPushManager iwapman = getInterface();
+ IDataVerify dataverify = getVerifyInterface();
+ mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+ for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length
+ + OMA_CONTENT_TYPE_NAMES.length; i++) {
+ mAppIdName = OMA_APPLICATION_ID_NAMES[rd.nextInt(OMA_APPLICATION_ID_NAMES.length)];
+ int contIndex = rd.nextInt(OMA_CONTENT_TYPE_NAMES.length);
+ mContentTypeName = OMA_CONTENT_TYPE_NAMES[contIndex];
+
+ mMessageBody = new byte[100 + rd.nextInt(100)];
+ rd.nextBytes(mMessageBody);
+
+ byte[] pdu = createPDU(8);
+ byte[] wappushPdu = retrieveWspBody();
+
+ try {
+ dataverify.resetData();
+ // set up data
+ iwapman.addPackage(mAppIdName,
+ mContentTypeName, mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+ dispatchWapPdu(wappushPdu, iwapman);
+
+ // clean up data
+ iwapman.deletePackage(mAppIdName,
+ mContentTypeName, mPackageName, mClassName);
+
+ if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+ assertTrue(dataverify.verifyData(wappushPdu));
+ } else {
+ assertTrue(dataverify.verifyData(mMessageBody));
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ mClassName = originalClassName;
+ mAppIdName = originalAppIdName;
+ mContentTypeName = originalContentTypeName;
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ mMessageBody = originalMessageBody;
+ }
+
+ /**
+ * Integration test 5, iterate binary OmaApplication ID, Oma binary content type
+ */
+ public void testIntegration5() {
+ boolean res;
+ int originalAppIdValue = mAppIdValue;
+ int originalContentTypeValue = mContentTypeValue;
+ String originalAppIdName = mAppIdName;
+ String originalContentTypeName = mContentTypeName;
+ String originalClassName = mClassName;
+ byte[] originalMessageBody = mMessageBody;
+ Random rd = new Random();
+
+ IWapPushManager iwapman = getInterface();
+ IDataVerify dataverify = getVerifyInterface();
+ mClassName = "com.android.smspush.unitTests.ReceiverService";
+
+ for (int i = 0; i < OMA_APPLICATION_ID_VALUES.length +
+ OMA_CONTENT_TYPE_VALUES.length; i++) {
+ mAppIdValue = OMA_APPLICATION_ID_VALUES[rd.nextInt(
+ OMA_APPLICATION_ID_VALUES.length)];
+ mContentTypeValue =
+ OMA_CONTENT_TYPE_VALUES[rd.nextInt(OMA_CONTENT_TYPE_VALUES.length)];
+
+ mMessageBody = new byte[100 + rd.nextInt(100)];
+ rd.nextBytes(mMessageBody);
+
+ byte[] pdu = createPDU(3);
+ byte[] wappushPdu = retrieveWspBody();
+
+ try {
+ dataverify.resetData();
+ // set up data
+ if (isContentTypeMapped(mContentTypeValue)) {
+ // content type is mapped to integer value
+ mContentTypeName = getContentTypeName(mContentTypeValue);
+ Log.d(LOG_TAG, "mContentTypeValue mapping "
+ + mContentTypeValue + ":" + mContentTypeName);
+ } else {
+ mContentTypeName = Integer.toString(mContentTypeValue);
+ }
+
+ iwapman.addPackage(Integer.toString(mAppIdValue),
+ mContentTypeName, mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, false, false);
+
+ dispatchWapPdu(wappushPdu, iwapman);
+
+ // clean up data
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ mContentTypeName, mPackageName, mClassName);
+
+ if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+ assertTrue(dataverify.verifyData(wappushPdu));
+ } else {
+ assertTrue(dataverify.verifyData(mMessageBody));
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ mClassName = originalClassName;
+ mAppIdName = originalAppIdName;
+ mContentTypeName = originalContentTypeName;
+ mAppIdValue = originalAppIdValue;
+ mContentTypeValue = originalContentTypeValue;
+ mMessageBody = originalMessageBody;
+ }
+
+}
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index f3d07ab..239fd76 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -163,6 +163,12 @@
*/
private Configuration mConfiguration;
+ private Runnable mRecreateRunnable = new Runnable() {
+ public void run() {
+ recreateScreens();
+ }
+ };
+
/**
* @return Whether we are stuck on the lock screen because the sim is
* missing.
@@ -244,7 +250,8 @@
public void recreateMe(Configuration config) {
mConfiguration = config;
- recreateScreens();
+ removeCallbacks(mRecreateRunnable);
+ post(mRecreateRunnable);
}
public void takeEmergencyCallAction() {
@@ -463,6 +470,12 @@
}
@Override
+ protected void onDetachedFromWindow() {
+ removeCallbacks(mRecreateRunnable);
+ super.onDetachedFromWindow();
+ }
+
+ @Override
public void wakeWhenReadyTq(int keyCode) {
if (DEBUG) Log.d(TAG, "onWakeKey");
if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen)
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 8aaa325..433f1f7 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -975,7 +975,6 @@
{
int32_t* const outTemp = state->outputTemp;
const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount;
- memset(outTemp, 0, size);
size_t numFrames = state->frameCount;
@@ -997,6 +996,7 @@
}
e0 &= ~(e1);
int32_t *out = t1.mainBuffer;
+ memset(outTemp, 0, size);
while (e1) {
const int i = 31 - __builtin_clz(e1);
e1 &= ~(1<<i);
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 4e2f1e3..4931cc7 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -124,6 +124,14 @@
public AlarmManagerService(Context context) {
mContext = context;
mDescriptor = init();
+
+ // We have to set current TimeZone info to kernel
+ // because kernel doesn't keep this after reboot
+ String tz = SystemProperties.get(TIMEZONE_PROPERTY);
+ if (tz != null) {
+ setTimeZone(tz);
+ }
+
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index d37558f..97fd804 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -30,6 +30,7 @@
import android.net.MobileDataStateTracker;
import android.net.NetworkInfo;
import android.net.NetworkStateTracker;
+import android.net.NetworkUtils;
import android.net.wifi.WifiStateTracker;
import android.net.wimax.WimaxManagerConstants;
import android.os.Binder;
@@ -56,6 +57,8 @@
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.List;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
@@ -907,6 +910,8 @@
}
/**
+ * @deprecated use requestRouteToHostAddress instead
+ *
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface.
* @param networkType the type of the network over which traffic to the
@@ -916,6 +921,25 @@
* @return {@code true} on success, {@code false} on failure
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
+ InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress);
+
+ if (inetAddress == null) {
+ return false;
+ }
+
+ return requestRouteToHostAddress(networkType, inetAddress.getAddress());
+ }
+
+ /**
+ * Ensure that a network route exists to deliver traffic to the specified
+ * host via the specified network interface.
+ * @param networkType the type of the network over which traffic to the
+ * specified host is to be routed
+ * @param hostAddress the IP address of the host to which the route is
+ * desired
+ * @return {@code true} on success, {@code false} on failure
+ */
+ public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
enforceChangePermission();
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
return false;
@@ -925,11 +949,18 @@
if (tracker == null || !tracker.getNetworkInfo().isConnected() ||
tracker.isTeardownRequested()) {
if (DBG) {
- Slog.d(TAG, "requestRouteToHost on down network (" + networkType + ") - dropped");
+ Slog.d(TAG, "requestRouteToHostAddress on down network " +
+ "(" + networkType + ") - dropped");
}
return false;
}
- return tracker.requestRouteToHost(hostAddress);
+
+ try {
+ InetAddress inetAddress = InetAddress.getByAddress(hostAddress);
+ return tracker.requestRouteToHost(inetAddress);
+ } catch (UnknownHostException e) {
+ return false;
+ }
}
/**
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index 0e45145..3981525 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -218,8 +218,14 @@
}
} while (read > 0);
- createEntry(temp, tag, flags);
+ long time = createEntry(temp, tag, flags);
temp = null;
+
+ Intent dropboxIntent = new Intent(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED);
+ dropboxIntent.putExtra(DropBoxManager.EXTRA_TAG, tag);
+ dropboxIntent.putExtra(DropBoxManager.EXTRA_TIME, time);
+ mContext.sendBroadcast(dropboxIntent, android.Manifest.permission.READ_LOGS);
+
} catch (IOException e) {
Slog.e(TAG, "Can't write: " + tag, e);
} finally {
@@ -597,7 +603,7 @@
}
/** Moves a temporary file to a final log filename and enrolls it. */
- private synchronized void createEntry(File temp, String tag, int flags) throws IOException {
+ private synchronized long createEntry(File temp, String tag, int flags) throws IOException {
long t = System.currentTimeMillis();
// Require each entry to have a unique timestamp; if there are entries
@@ -636,6 +642,7 @@
} else {
enrollEntry(new EntryFile(temp, mDropBoxDir, tag, t, flags, mBlockSize));
}
+ return t;
}
/**
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index a191549..e6e3e5f 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1144,6 +1144,17 @@
// Post a unmount message.
ShutdownCallBack ucb = new ShutdownCallBack(path, observer);
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
+ } else if (observer != null) {
+ /*
+ * Observer is waiting for onShutDownComplete when we are done.
+ * Since nothing will be done send notification directly so shutdown
+ * sequence can continue.
+ */
+ try {
+ observer.onShutDownComplete(StorageResultCode.OperationSucceeded);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException when shutting down");
+ }
}
}
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index cf87a9d..88d94c2 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -97,11 +97,12 @@
LocalSocketAddress.Namespace.RESERVED);
socket.connect(address);
- mCallbacks.onDaemonConnected();
InputStream inputStream = socket.getInputStream();
mOutputStream = socket.getOutputStream();
+ mCallbacks.onDaemonConnected();
+
byte[] buffer = new byte[BUFFER_SIZE];
int start = 0;
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index b26dac7..540389e 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -133,11 +133,11 @@
private boolean mBatteryFull;
private NotificationRecord mLedNotification;
- private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on
- private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00; // Charging - orange solid on
- private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on
- private static final int BATTERY_BLINK_ON = 125;
- private static final int BATTERY_BLINK_OFF = 2875;
+ private static int mBatteryLowARGB;
+ private static int mBatteryMediumARGB;
+ private static int mBatteryFullARGB;
+ private static int mBatteryLedOn;
+ private static int mBatteryLedOff;
private static String idDebugString(Context baseContext, String packageName, int id) {
Context c = null;
@@ -450,6 +450,17 @@
mDefaultNotificationLedOff = resources.getInteger(
com.android.internal.R.integer.config_defaultNotificationLedOff);
+ mBatteryLowARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLowARGB);
+ mBatteryMediumARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
+ mBatteryFullARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryFullARGB);
+ mBatteryLedOn = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLedOn);
+ mBatteryLedOff = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLedOff);
+
// Don't start allowing notifications until the setup wizard has run once.
// After that, including subsequent boots, init with notifications turned on.
// This works on the first boot because the setup wizard will toggle this
@@ -1065,17 +1076,17 @@
// Battery low always shows, other states only show if charging.
if (mBatteryLow) {
if (mBatteryCharging) {
- mBatteryLight.setColor(BATTERY_LOW_ARGB);
+ mBatteryLight.setColor(mBatteryLowARGB);
} else {
// Flash when battery is low and not charging
- mBatteryLight.setFlashing(BATTERY_LOW_ARGB, LightsService.LIGHT_FLASH_TIMED,
- BATTERY_BLINK_ON, BATTERY_BLINK_OFF);
+ mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
+ mBatteryLedOn, mBatteryLedOff);
}
} else if (mBatteryCharging) {
if (mBatteryFull) {
- mBatteryLight.setColor(BATTERY_FULL_ARGB);
+ mBatteryLight.setColor(mBatteryFullARGB);
} else {
- mBatteryLight.setColor(BATTERY_MEDIUM_ARGB);
+ mBatteryLight.setColor(mBatteryMediumARGB);
}
} else {
mBatteryLight.turnOff();
diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java
index 020f9ed..43dbcc0 100644
--- a/services/java/com/android/server/ProcessStats.java
+++ b/services/java/com/android/server/ProcessStats.java
@@ -811,7 +811,7 @@
break;
}
}
- return new String(mBuffer, 0, 0, i);
+ return new String(mBuffer, 0, i);
}
} catch (java.io.FileNotFoundException e) {
} catch (java.io.IOException e) {
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 859c85c..997e750 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -587,6 +587,8 @@
mIWindowManager.removeWindowToken(mWallpaperConnection.mToken);
} catch (RemoteException e) {
}
+ mWallpaperConnection.mService = null;
+ mWallpaperConnection.mEngine = null;
mWallpaperConnection = null;
}
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index efd0bb4..667b544 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -522,9 +522,8 @@
} catch (InterruptedException e) {
}
}
+ return thr.mService;
}
-
- return thr.mService;
}
static class WMThread extends Thread {
@@ -1482,6 +1481,7 @@
WindowState wb = localmWindows.get(foundI-1);
if (wb.mBaseLayer < maxLayer &&
wb.mAttachedWindow != foundW &&
+ wb.mAttachedWindow != foundW.mAttachedWindow &&
(wb.mAttrs.type != TYPE_APPLICATION_STARTING ||
wb.mToken != foundW.mToken)) {
// This window is not related to the previous one in any
@@ -5421,6 +5421,7 @@
int deviceId = ev.getDeviceId();
int scancode = ev.getScanCode();
int source = ev.getSource();
+ int flags = ev.getFlags();
if (source == InputDevice.SOURCE_UNKNOWN) {
source = InputDevice.SOURCE_KEYBOARD;
@@ -5430,7 +5431,7 @@
if (downTime == 0) downTime = eventTime;
KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
- deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM, source);
+ deviceId, scancode, flags | KeyEvent.FLAG_FROM_SYSTEM, source);
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -7601,7 +7602,8 @@
WindowState win = allAppWindows.get(i);
if (win == startingWindow || win.mAppFreezing
|| win.mViewVisibility != View.VISIBLE
- || win.mAttrs.type == TYPE_APPLICATION_STARTING) {
+ || win.mAttrs.type == TYPE_APPLICATION_STARTING
+ || win.mDestroying) {
continue;
}
if (DEBUG_VISIBILITY) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index ddd99f4..0e38e10 100755
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -55,6 +55,7 @@
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IIntentReceiver;
@@ -1116,7 +1117,7 @@
d.setCancelable(false);
d.setTitle("System UIDs Inconsistent");
d.setMessage("UIDs on the system are inconsistent, you need to wipe your data partition or your device will be unstable.");
- d.setButton("I'm Feeling Lucky",
+ d.setButton(DialogInterface.BUTTON_POSITIVE, "I'm Feeling Lucky",
mHandler.obtainMessage(IM_FEELING_LUCKY_MSG));
mUidAlert = d;
d.show();
@@ -3678,10 +3679,12 @@
String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
if (pkgs != null) {
for (String pkg : pkgs) {
- if (forceStopPackageLocked(pkg, -1, false, false, false)) {
- setResultCode(Activity.RESULT_OK);
- return;
- }
+ synchronized (ActivityManagerService.this) {
+ if (forceStopPackageLocked(pkg, -1, false, false, false)) {
+ setResultCode(Activity.RESULT_OK);
+ return;
+ }
+ }
}
}
}
@@ -4382,12 +4385,15 @@
perm.modeFlags |= modeFlags;
if (owner == null) {
perm.globalModeFlags |= modeFlags;
- } else if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
- perm.readOwners.add(owner);
- owner.addReadPermission(perm);
- } else if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
- perm.writeOwners.add(owner);
- owner.addWritePermission(perm);
+ } else {
+ if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ perm.readOwners.add(owner);
+ owner.addReadPermission(perm);
+ }
+ if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ perm.writeOwners.add(owner);
+ owner.addWritePermission(perm);
+ }
}
}
@@ -6435,7 +6441,28 @@
sr.crashCount++;
}
}
-
+
+ // If the crashing process is what we consider to be the "home process" and it has been
+ // replaced by a third-party app, clear the package preferred activities from packages
+ // with a home activity running in the process to prevent a repeatedly crashing app
+ // from blocking the user to manually clear the list.
+ if (app == mHomeProcess && mHomeProcess.activities.size() > 0
+ && (mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Iterator it = mHomeProcess.activities.iterator();
+ while (it.hasNext()) {
+ ActivityRecord r = (ActivityRecord)it.next();
+ if (r.isHomeActivity) {
+ Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
+ try {
+ ActivityThread.getPackageManager()
+ .clearPackagePreferredActivities(r.packageName);
+ } catch (RemoteException c) {
+ // pm is in same process, this will never happen.
+ }
+ }
+ }
+ }
+
mProcessCrashTimes.put(app.info.processName, app.info.uid, now);
return true;
}
@@ -6789,6 +6816,9 @@
sb.append("Subject: ").append(subject).append("\n");
}
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
+ if (Debug.isDebuggerConnected()) {
+ sb.append("Debugger: Connected\n");
+ }
sb.append("\n");
// Do the rest in a worker thread to avoid blocking the caller on I/O
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index f52d322..463493b 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -2139,7 +2139,7 @@
// being started, which means not bringing it to the front
// if the caller is not itself in the front.
ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop);
- if (curTop.task != taskTop.task) {
+ if (curTop != null && curTop.task != taskTop.task) {
r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
boolean callerAtFront = sourceRecord == null
|| curTop.task == sourceRecord.task;
diff --git a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
index 8e9818d..9fb48b3 100644
--- a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
+++ b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.content.Context;
+import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
@@ -49,7 +50,7 @@
text.append(" is waiting for the debugger to attach.");
setMessage(text.toString());
- setButton("Force Close", mHandler.obtainMessage(1, app));
+ setButton(DialogInterface.BUTTON_POSITIVE, "Force Close", mHandler.obtainMessage(1, app));
setTitle("Waiting For Debugger");
getWindow().setTitle("Waiting For Debugger: " + app.info.processName);
}
diff --git a/services/java/com/android/server/am/FactoryErrorDialog.java b/services/java/com/android/server/am/FactoryErrorDialog.java
index 2e25474..b19bb5ca 100644
--- a/services/java/com/android/server/am/FactoryErrorDialog.java
+++ b/services/java/com/android/server/am/FactoryErrorDialog.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.content.Context;
+import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
@@ -26,7 +27,8 @@
setCancelable(false);
setTitle(context.getText(com.android.internal.R.string.factorytest_failed));
setMessage(msg);
- setButton(context.getText(com.android.internal.R.string.factorytest_reboot),
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getText(com.android.internal.R.string.factorytest_reboot),
mHandler.obtainMessage(0));
getWindow().setTitle("Factory Error");
}
diff --git a/services/jni/com_android_server_BatteryService.cpp b/services/jni/com_android_server_BatteryService.cpp
index 8e7cadc..d4513e9 100644
--- a/services/jni/com_android_server_BatteryService.cpp
+++ b/services/jni/com_android_server_BatteryService.cpp
@@ -67,6 +67,7 @@
jint healthDead;
jint healthOverVoltage;
jint healthUnspecifiedFailure;
+ jint healthCold;
};
static BatteryManagerConstants gConstants;
@@ -104,6 +105,7 @@
static jint getBatteryHealth(const char* status)
{
switch (status[0]) {
+ case 'C': return gConstants.healthCold; // Cold
case 'D': return gConstants.healthDead; // Dead
case 'G': return gConstants.healthGood; // Good
case 'O': {
@@ -162,7 +164,7 @@
jboolean value = false;
if (readFromFile(path, buf, SIZE) > 0) {
- if (buf[0] == '1') {
+ if (buf[0] != '0') {
value = true;
}
}
@@ -390,6 +392,9 @@
gConstants.healthUnspecifiedFailure = env->GetStaticIntField(clazz,
env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNSPECIFIED_FAILURE", "I"));
+ gConstants.healthCold = env->GetStaticIntField(clazz,
+ env->GetStaticFieldID(clazz, "BATTERY_HEALTH_COLD", "I"));
+
return jniRegisterNativeMethods(env, "com/android/server/BatteryService", sMethods, NELEM(sMethods));
}
diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
index a75e41d..6d4ad9a 100755
--- a/services/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -217,82 +217,10 @@
create_thread_callback,
};
-static const GpsInterface* get_gps_interface() {
+static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
int err;
hw_module_t* module;
- const GpsInterface* interface = NULL;
- err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
- if (err == 0) {
- hw_device_t* device;
- err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
- if (err == 0) {
- gps_device_t* gps_device = (gps_device_t *)device;
- interface = gps_device->get_gps_interface(gps_device);
- }
- }
-
- return interface;
-}
-
-static const GpsInterface* GetGpsInterface(JNIEnv* env, jobject obj) {
- // this must be set before calling into the HAL library
- if (!mCallbacksObj)
- mCallbacksObj = env->NewGlobalRef(obj);
-
- if (!sGpsInterface) {
- sGpsInterface = get_gps_interface();
- if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0) {
- sGpsInterface = NULL;
- return NULL;
- }
- }
- return sGpsInterface;
-}
-
-static const AGpsInterface* GetAGpsInterface(JNIEnv* env, jobject obj)
-{
- const GpsInterface* interface = GetGpsInterface(env, obj);
- if (!interface)
- return NULL;
-
- if (!sAGpsInterface) {
- sAGpsInterface = (const AGpsInterface*)interface->get_extension(AGPS_INTERFACE);
- if (sAGpsInterface)
- sAGpsInterface->init(&sAGpsCallbacks);
- }
- return sAGpsInterface;
-}
-
-static const GpsNiInterface* GetNiInterface(JNIEnv* env, jobject obj)
-{
- const GpsInterface* interface = GetGpsInterface(env, obj);
- if (!interface)
- return NULL;
-
- if (!sGpsNiInterface) {
- sGpsNiInterface = (const GpsNiInterface*)interface->get_extension(GPS_NI_INTERFACE);
- if (sGpsNiInterface)
- sGpsNiInterface->init(&sGpsNiCallbacks);
- }
- return sGpsNiInterface;
-}
-
-static const AGpsRilInterface* GetAGpsRilInterface(JNIEnv* env, jobject obj)
-{
- const GpsInterface* interface = GetGpsInterface(env, obj);
- if (!interface)
- return NULL;
-
- if (!sAGpsRilInterface) {
- sAGpsRilInterface = (const AGpsRilInterface*)interface->get_extension(AGPS_RIL_INTERFACE);
- if (sAGpsRilInterface)
- sAGpsRilInterface->init(&sAGpsRilCallbacks);
- }
- return sAGpsRilInterface;
-}
-
-static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");
method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");
@@ -300,40 +228,73 @@
method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V");
method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
- method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification", "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
+ method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification",
+ "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
method_requestRefLocation = env->GetMethodID(clazz,"requestRefLocation","(I)V");
method_requestSetID = env->GetMethodID(clazz,"requestSetID","(I)V");
+
+ err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
+ if (err == 0) {
+ hw_device_t* device;
+ err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
+ if (err == 0) {
+ gps_device_t* gps_device = (gps_device_t *)device;
+ sGpsInterface = gps_device->get_gps_interface(gps_device);
+ }
+ }
+ if (sGpsInterface) {
+ sGpsXtraInterface =
+ (const GpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE);
+ sAGpsInterface =
+ (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
+ sGpsNiInterface =
+ (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
+ sGpsDebugInterface =
+ (const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);
+ sAGpsRilInterface =
+ (const AGpsRilInterface*)sGpsInterface->get_extension(AGPS_RIL_INTERFACE);
+ }
}
static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) {
- return (sGpsInterface != NULL || get_gps_interface() != NULL);
+ return (sGpsInterface != NULL);
}
static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
{
- const GpsInterface* interface = GetGpsInterface(env, obj);
- if (!interface)
+ // this must be set before calling into the HAL library
+ if (!mCallbacksObj)
+ mCallbacksObj = env->NewGlobalRef(obj);
+
+ // fail if the main interface fails to initialize
+ if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)
return false;
- if (!sGpsDebugInterface)
- sGpsDebugInterface = (const GpsDebugInterface*)interface->get_extension(GPS_DEBUG_INTERFACE);
+ // if XTRA initialization fails we will disable it by sGpsXtraInterface to null,
+ // but continue to allow the rest of the GPS interface to work.
+ if (sGpsXtraInterface && sGpsXtraInterface->init(&sGpsXtraCallbacks) != 0)
+ sGpsXtraInterface = NULL;
+ if (sAGpsInterface)
+ sAGpsInterface->init(&sAGpsCallbacks);
+ if (sGpsNiInterface)
+ sGpsNiInterface->init(&sGpsNiCallbacks);
+ if (sAGpsRilInterface)
+ sAGpsRilInterface->init(&sAGpsRilCallbacks);
return true;
}
static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject obj)
{
- const GpsInterface* interface = GetGpsInterface(env, obj);
- if (interface)
- interface->cleanup();
+ if (sGpsInterface)
+ sGpsInterface->cleanup();
}
static jboolean android_location_GpsLocationProvider_set_position_mode(JNIEnv* env, jobject obj,
jint mode, jint recurrence, jint min_interval, jint preferred_accuracy, jint preferred_time)
{
- const GpsInterface* interface = GetGpsInterface(env, obj);
- if (interface)
- return (interface->set_position_mode(mode, recurrence, min_interval, preferred_accuracy,
+ if (sGpsInterface)
+ return (sGpsInterface->set_position_mode(mode, recurrence, min_interval, preferred_accuracy,
preferred_time) == 0);
else
return false;
@@ -341,27 +302,24 @@
static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj)
{
- const GpsInterface* interface = GetGpsInterface(env, obj);
- if (interface)
- return (interface->start() == 0);
+ if (sGpsInterface)
+ return (sGpsInterface->start() == 0);
else
return false;
}
static jboolean android_location_GpsLocationProvider_stop(JNIEnv* env, jobject obj)
{
- const GpsInterface* interface = GetGpsInterface(env, obj);
- if (interface)
- return (interface->stop() == 0);
+ if (sGpsInterface)
+ return (sGpsInterface->stop() == 0);
else
return false;
}
static void android_location_GpsLocationProvider_delete_aiding_data(JNIEnv* env, jobject obj, jint flags)
{
- const GpsInterface* interface = GetGpsInterface(env, obj);
- if (interface)
- interface->delete_aiding_data(flags);
+ if (sGpsInterface)
+ sGpsInterface->delete_aiding_data(flags);
}
static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj,
@@ -399,8 +357,8 @@
jobject obj, jint type, jint mcc, jint mnc, jint lac, jint cid)
{
AGpsRefLocation location;
- const AGpsRilInterface* interface = GetAGpsRilInterface(env, obj);
- if (!interface) {
+
+ if (!sAGpsRilInterface) {
LOGE("no AGPS RIL interface in agps_set_reference_location_cellid");
return;
}
@@ -419,15 +377,15 @@
return;
break;
}
- interface->set_ref_location(&location, sizeof(location));
+ sAGpsRilInterface->set_ref_location(&location, sizeof(location));
}
static void android_location_GpsLocationProvider_agps_send_ni_message(JNIEnv* env,
jobject obj, jbyteArray ni_msg, jint size)
{
size_t sz;
- const AGpsRilInterface* interface = GetAGpsRilInterface(env, obj);
- if (!interface) {
+
+ if (!sAGpsRilInterface) {
LOGE("no AGPS RIL interface in send_ni_message");
return;
}
@@ -435,21 +393,20 @@
return;
sz = (size_t)size;
jbyte* b = env->GetByteArrayElements(ni_msg, 0);
- interface->ni_message((uint8_t *)b,sz);
+ sAGpsRilInterface->ni_message((uint8_t *)b,sz);
env->ReleaseByteArrayElements(ni_msg,b,0);
}
static void android_location_GpsLocationProvider_agps_set_id(JNIEnv *env,
jobject obj, jint type, jstring setid_string)
{
- const AGpsRilInterface* interface = GetAGpsRilInterface(env, obj);
- if (!interface) {
+ if (!sAGpsRilInterface) {
LOGE("no AGPS RIL interface in agps_set_id");
return;
}
const char *setid = env->GetStringUTFChars(setid_string, NULL);
- interface->set_set_id(type, setid);
+ sAGpsRilInterface->set_set_id(type, setid);
env->ReleaseStringUTFChars(setid_string, setid);
}
@@ -469,40 +426,30 @@
static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobject obj,
jlong time, jlong timeReference, jint uncertainty)
{
- const GpsInterface* interface = GetGpsInterface(env, obj);
- if (interface)
- interface->inject_time(time, timeReference, uncertainty);
+ if (sGpsInterface)
+ sGpsInterface->inject_time(time, timeReference, uncertainty);
}
static void android_location_GpsLocationProvider_inject_location(JNIEnv* env, jobject obj,
jdouble latitude, jdouble longitude, jfloat accuracy)
{
- const GpsInterface* interface = GetGpsInterface(env, obj);
- if (interface)
- interface->inject_location(latitude, longitude, accuracy);
+ if (sGpsInterface)
+ sGpsInterface->inject_location(latitude, longitude, accuracy);
}
static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, jobject obj)
{
- if (!sGpsXtraInterface) {
- const GpsInterface* interface = GetGpsInterface(env, obj);
- if (!interface)
- return false;
- sGpsXtraInterface = (const GpsXtraInterface*)interface->get_extension(GPS_XTRA_INTERFACE);
- if (sGpsXtraInterface) {
- int result = sGpsXtraInterface->init(&sGpsXtraCallbacks);
- if (result) {
- sGpsXtraInterface = NULL;
- }
- }
- }
-
return (sGpsXtraInterface != NULL);
}
static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj,
jbyteArray data, jint length)
{
+ if (!sGpsXtraInterface) {
+ LOGE("no XTRA interface in inject_xtra_data");
+ return;
+ }
+
jbyte* bytes = (jbyte *)env->GetPrimitiveArrayCritical(data, 0);
sGpsXtraInterface->inject_xtra_data((char *)bytes, length);
env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
@@ -510,8 +457,7 @@
static void android_location_GpsLocationProvider_agps_data_conn_open(JNIEnv* env, jobject obj, jstring apn)
{
- const AGpsInterface* interface = GetAGpsInterface(env, obj);
- if (!interface) {
+ if (!sAGpsInterface) {
LOGE("no AGPS interface in agps_data_conn_open");
return;
}
@@ -520,53 +466,49 @@
return;
}
const char *apnStr = env->GetStringUTFChars(apn, NULL);
- interface->data_conn_open(apnStr);
+ sAGpsInterface->data_conn_open(apnStr);
env->ReleaseStringUTFChars(apn, apnStr);
}
static void android_location_GpsLocationProvider_agps_data_conn_closed(JNIEnv* env, jobject obj)
{
- const AGpsInterface* interface = GetAGpsInterface(env, obj);
- if (!interface) {
+ if (!sAGpsInterface) {
LOGE("no AGPS interface in agps_data_conn_open");
return;
}
- interface->data_conn_closed();
+ sAGpsInterface->data_conn_closed();
}
static void android_location_GpsLocationProvider_agps_data_conn_failed(JNIEnv* env, jobject obj)
{
- const AGpsInterface* interface = GetAGpsInterface(env, obj);
- if (!interface) {
+ if (!sAGpsInterface) {
LOGE("no AGPS interface in agps_data_conn_open");
return;
}
- interface->data_conn_failed();
+ sAGpsInterface->data_conn_failed();
}
static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jobject obj,
jint type, jstring hostname, jint port)
{
- const AGpsInterface* interface = GetAGpsInterface(env, obj);
- if (!interface) {
+ if (!sAGpsInterface) {
LOGE("no AGPS interface in agps_data_conn_open");
return;
}
const char *c_hostname = env->GetStringUTFChars(hostname, NULL);
- interface->set_server(type, c_hostname, port);
+ sAGpsInterface->set_server(type, c_hostname, port);
env->ReleaseStringUTFChars(hostname, c_hostname);
}
static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* env, jobject obj,
jint notifId, jint response)
{
- const GpsNiInterface* interface = GetNiInterface(env, obj);
- if (!interface) {
+ if (!sGpsNiInterface) {
LOGE("no NI interface in send_ni_response");
return;
}
- interface->respond(notifId, response);
+ sGpsNiInterface->respond(notifId, response);
}
static jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* env, jobject obj)
@@ -586,14 +528,14 @@
static void android_location_GpsLocationProvider_update_network_state(JNIEnv* env, jobject obj,
jboolean connected, int type, jboolean roaming, jstring extraInfo)
{
- const AGpsRilInterface* interface = GetAGpsRilInterface(env, obj);
- if (interface && interface->update_network_state) {
+
+ if (sAGpsRilInterface && sAGpsRilInterface->update_network_state) {
if (extraInfo) {
const char *extraInfoStr = env->GetStringUTFChars(extraInfo, NULL);
- interface->update_network_state(connected, type, roaming, extraInfoStr);
+ sAGpsRilInterface->update_network_state(connected, type, roaming, extraInfoStr);
env->ReleaseStringUTFChars(extraInfo, extraInfoStr);
} else {
- interface->update_network_state(connected, type, roaming, NULL);
+ sAGpsRilInterface->update_network_state(connected, type, roaming, NULL);
}
}
}
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index edc00f1..23506cf 100644
--- a/services/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -509,7 +509,7 @@
const ISurface::BufferHeap& buffers(mBufferHeap);
uint32_t w = mLayer.mTransformedBounds.width();
uint32_t h = mLayer.mTransformedBounds.height();
- if (buffers.w * h != buffers.h * w) {
+ if (mLayer.getOrientation() & (Transform::ROT_90 | Transform::ROT_270)) {
int t = w; w = h; h = t;
}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 5542c42..8e4f6fc 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -55,6 +55,12 @@
public static final char WILD = 'N';
/*
+ * Calling Line Identification Restriction (CLIR)
+ */
+ private static final String CLIR_ON = "*31#+";
+ private static final String CLIR_OFF = "#31#+";
+
+ /*
* TOA = TON + NPI
* See TS 24.008 section 10.5.4.7 for details.
* These are the only really useful TOA values
@@ -179,8 +185,6 @@
* Please note that the GSM wild character is allowed in the result.
* This must be resolved before dialing.
*
- * Allows + only in the first position in the result string.
- *
* Returns null if phoneNumber == null
*/
public static String
@@ -203,6 +207,11 @@
}
}
+ int pos = addPlusChar(phoneNumber);
+ if (pos >= 0 && ret.length() > pos) {
+ ret.insert(pos, '+');
+ }
+
return ret.toString();
}
@@ -304,6 +313,28 @@
}
}
+ /** GSM codes
+ * Finds if a GSM code includes the international prefix (+).
+ *
+ * @param number the number to dial.
+ *
+ * @return the position where the + char will be inserted, -1 if the GSM code was not found.
+ */
+ private static int
+ addPlusChar(String number) {
+ int pos = -1;
+
+ if (number.startsWith(CLIR_OFF)) {
+ pos = CLIR_OFF.length() - 1;
+ }
+
+ if (number.startsWith(CLIR_ON)) {
+ pos = CLIR_ON.length() - 1;
+ }
+
+ return pos;
+ }
+
/**
* Extracts the post-dial sequence of DTMF control digits, pauses, and
* waits. Strips separators. This string may be empty, but will not be null
@@ -1119,7 +1150,7 @@
&& text.charAt(2) == '1') {
formatType = FORMAT_JAPAN;
} else {
- return;
+ formatType = FORMAT_UNKNOWN;
}
}
@@ -1130,6 +1161,9 @@
case FORMAT_JAPAN:
formatJapaneseNumber(text);
return;
+ case FORMAT_UNKNOWN:
+ removeDashes(text);
+ return;
}
}
@@ -1165,14 +1199,7 @@
CharSequence saved = text.subSequence(0, length);
// Strip the dashes first, as we're going to add them back
- int p = 0;
- while (p < text.length()) {
- if (text.charAt(p) == '-') {
- text.delete(p, p + 1);
- } else {
- p++;
- }
- }
+ removeDashes(text);
length = text.length();
// When scanning the number we record where dashes need to be added,
@@ -1276,6 +1303,22 @@
JapanesePhoneNumberFormatter.format(text);
}
+ /**
+ * Removes all dashes from the number.
+ *
+ * @param text the number to clear from dashes
+ */
+ private static void removeDashes(Editable text) {
+ int p = 0;
+ while (p < text.length()) {
+ if (text.charAt(p) == '-') {
+ text.delete(p, p + 1);
+ } else {
+ p++;
+ }
+ }
+ }
+
// Three and four digit phone numbers for either special services,
// or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
// not match.
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
new file mode 100644
index 0000000..3543275
--- /dev/null
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -0,0 +1,267 @@
+/*
+ * 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.telephony;
+
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.gsm.SmsCbHeader;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Describes an SMS-CB message.
+ *
+ * {@hide}
+ */
+public class SmsCbMessage {
+
+ /**
+ * Cell wide immediate geographical scope
+ */
+ public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0;
+
+ /**
+ * PLMN wide geographical scope
+ */
+ public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1;
+
+ /**
+ * Location / service area wide geographical scope
+ */
+ public static final int GEOGRAPHICAL_SCOPE_LA_WIDE = 2;
+
+ /**
+ * Cell wide geographical scope
+ */
+ public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3;
+
+ /**
+ * Create an instance of this class from a received PDU
+ *
+ * @param pdu PDU bytes
+ * @return An instance of this class, or null if invalid pdu
+ */
+ public static SmsCbMessage createFromPdu(byte[] pdu) {
+ try {
+ return new SmsCbMessage(pdu);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
+ */
+ private static final String[] LANGUAGE_CODES_GROUP_0 = {
+ "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu",
+ "pl", null
+ };
+
+ /**
+ * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
+ */
+ private static final String[] LANGUAGE_CODES_GROUP_2 = {
+ "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null,
+ null, null
+ };
+
+ private static final char CARRIAGE_RETURN = 0x0d;
+
+ private SmsCbHeader mHeader;
+
+ private String mLanguage;
+
+ private String mBody;
+
+ private SmsCbMessage(byte[] pdu) throws IllegalArgumentException {
+ mHeader = new SmsCbHeader(pdu);
+ parseBody(pdu);
+ }
+
+ /**
+ * Return the geographical scope of this message, one of
+ * {@link #GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE},
+ * {@link #GEOGRAPHICAL_SCOPE_PLMN_WIDE},
+ * {@link #GEOGRAPHICAL_SCOPE_LA_WIDE},
+ * {@link #GEOGRAPHICAL_SCOPE_CELL_WIDE}
+ *
+ * @return Geographical scope
+ */
+ public int getGeographicalScope() {
+ return mHeader.geographicalScope;
+ }
+
+ /**
+ * Get the ISO-639-1 language code for this message, or null if unspecified
+ *
+ * @return Language code
+ */
+ public String getLanguageCode() {
+ return mLanguage;
+ }
+
+ /**
+ * Get the body of this message, or null if no body available
+ *
+ * @return Body, or null
+ */
+ public String getMessageBody() {
+ return mBody;
+ }
+
+ /**
+ * Get the message identifier of this message (0-65535)
+ *
+ * @return Message identifier
+ */
+ public int getMessageIdentifier() {
+ return mHeader.messageIdentifier;
+ }
+
+ /**
+ * Get the message code of this message (0-1023)
+ *
+ * @return Message code
+ */
+ public int getMessageCode() {
+ return mHeader.messageCode;
+ }
+
+ /**
+ * Get the update number of this message (0-15)
+ *
+ * @return Update number
+ */
+ public int getUpdateNumber() {
+ return mHeader.updateNumber;
+ }
+
+ private void parseBody(byte[] pdu) {
+ int encoding;
+ boolean hasLanguageIndicator = false;
+
+ // Extract encoding and language from DCS, as defined in 3gpp TS 23.038,
+ // section 5.
+ switch ((mHeader.dataCodingScheme & 0xf0) >> 4) {
+ case 0x00:
+ encoding = SmsMessage.ENCODING_7BIT;
+ mLanguage = LANGUAGE_CODES_GROUP_0[mHeader.dataCodingScheme & 0x0f];
+ break;
+
+ case 0x01:
+ hasLanguageIndicator = true;
+ if ((mHeader.dataCodingScheme & 0x0f) == 0x01) {
+ encoding = SmsMessage.ENCODING_16BIT;
+ } else {
+ encoding = SmsMessage.ENCODING_7BIT;
+ }
+ break;
+
+ case 0x02:
+ encoding = SmsMessage.ENCODING_7BIT;
+ mLanguage = LANGUAGE_CODES_GROUP_2[mHeader.dataCodingScheme & 0x0f];
+ break;
+
+ case 0x03:
+ encoding = SmsMessage.ENCODING_7BIT;
+ break;
+
+ case 0x04:
+ case 0x05:
+ switch ((mHeader.dataCodingScheme & 0x0c) >> 2) {
+ case 0x01:
+ encoding = SmsMessage.ENCODING_8BIT;
+ break;
+
+ case 0x02:
+ encoding = SmsMessage.ENCODING_16BIT;
+ break;
+
+ case 0x00:
+ default:
+ encoding = SmsMessage.ENCODING_7BIT;
+ break;
+ }
+ break;
+
+ case 0x06:
+ case 0x07:
+ // Compression not supported
+ case 0x09:
+ // UDH structure not supported
+ case 0x0e:
+ // Defined by the WAP forum not supported
+ encoding = SmsMessage.ENCODING_UNKNOWN;
+ break;
+
+ case 0x0f:
+ if (((mHeader.dataCodingScheme & 0x04) >> 2) == 0x01) {
+ encoding = SmsMessage.ENCODING_8BIT;
+ } else {
+ encoding = SmsMessage.ENCODING_7BIT;
+ }
+ break;
+
+ default:
+ // Reserved values are to be treated as 7-bit
+ encoding = SmsMessage.ENCODING_7BIT;
+ break;
+ }
+
+ switch (encoding) {
+ case SmsMessage.ENCODING_7BIT:
+ mBody = GsmAlphabet.gsm7BitPackedToString(pdu, SmsCbHeader.PDU_HEADER_LENGTH,
+ (pdu.length - SmsCbHeader.PDU_HEADER_LENGTH) * 8 / 7);
+
+ if (hasLanguageIndicator && mBody != null && mBody.length() > 2) {
+ mLanguage = mBody.substring(0, 2);
+ mBody = mBody.substring(3);
+ }
+ break;
+
+ case SmsMessage.ENCODING_16BIT:
+ int offset = SmsCbHeader.PDU_HEADER_LENGTH;
+
+ if (hasLanguageIndicator && pdu.length >= SmsCbHeader.PDU_HEADER_LENGTH + 2) {
+ mLanguage = GsmAlphabet.gsm7BitPackedToString(pdu,
+ SmsCbHeader.PDU_HEADER_LENGTH, 2);
+ offset += 2;
+ }
+
+ try {
+ mBody = new String(pdu, offset, (pdu.length & 0xfffe) - offset, "utf-16");
+ } catch (UnsupportedEncodingException e) {
+ // Eeeek
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (mBody != null) {
+ // Remove trailing carriage return
+ for (int i = mBody.length() - 1; i >= 0; i--) {
+ if (mBody.charAt(i) != CARRIAGE_RETURN) {
+ mBody = mBody.substring(0, i + 1);
+ break;
+ }
+ }
+ } else {
+ mBody = "";
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index f5e9751..0ecd854 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -341,7 +341,67 @@
}
return createMessageListFromRawRecords(records);
- }
+ }
+
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages. All received messages will be broadcast in an
+ * intent with the action "android.provider.telephony.SMS_CB_RECEIVED".
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ * @see #disableCellBroadcast(int)
+ *
+ * {@hide}
+ */
+ public boolean enableCellBroadcast(int messageIdentifier) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ success = iccISms.enableCellBroadcast(messageIdentifier);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Disable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages.
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcast(int)
+ *
+ * {@hide}
+ */
+ public boolean disableCellBroadcast(int messageIdentifier) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ success = iccISms.disableCellBroadcast(messageIdentifier);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
/**
* Create a list of <code>SmsMessage</code>s from a list of RawSmsData
diff --git a/telephony/java/com/android/internal/telephony/AdnRecord.java b/telephony/java/com/android/internal/telephony/AdnRecord.java
index 1bf2d3c..a01b00d 100644
--- a/telephony/java/com/android/internal/telephony/AdnRecord.java
+++ b/telephony/java/com/android/internal/telephony/AdnRecord.java
@@ -283,7 +283,7 @@
private void
parseRecord(byte[] record) {
try {
- alphaTag = IccUtils.adnStringFieldToString(
+ alphaTag = IccUtils.adnStringFieldToStringKsc5601Support(
record, 0, record.length - FOOTER_SIZE_BYTES);
int footerOffset = record.length - FOOTER_SIZE_BYTES;
diff --git a/telephony/java/com/android/internal/telephony/AdnRecordCache.java b/telephony/java/com/android/internal/telephony/AdnRecordCache.java
index c8c0658..a175d49 100644
--- a/telephony/java/com/android/internal/telephony/AdnRecordCache.java
+++ b/telephony/java/com/android/internal/telephony/AdnRecordCache.java
@@ -186,7 +186,12 @@
}
ArrayList<AdnRecord> oldAdnList;
- oldAdnList = getRecordsIfLoaded(efid);
+
+ if (efid == EF_PBR) {
+ oldAdnList = mUsimPhoneBookManager.loadEfFilesFromUsim();
+ } else {
+ oldAdnList = getRecordsIfLoaded(efid);
+ }
if (oldAdnList == null) {
sendErrorResponse(response, "Adn list not exist for EF:" + efid);
@@ -208,6 +213,17 @@
return;
}
+ if (efid == EF_PBR) {
+ AdnRecord foundAdn = oldAdnList.get(index-1);
+ efid = foundAdn.efid;
+ extensionEF = foundAdn.extRecord;
+ index = foundAdn.recordNumber;
+
+ newAdn.efid = efid;
+ newAdn.extRecord = extensionEF;
+ newAdn.recordNumber = index;
+ }
+
Message pendingResponse = userWriteResponse.get(efid);
if (pendingResponse != null) {
@@ -331,6 +347,7 @@
if (ar.exception == null) {
adnLikeFiles.get(efid).set(index - 1, adn);
+ mUsimPhoneBookManager.invalidateCache();
}
Message response = userWriteResponse.get(efid);
diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java
index b962375..815fbfb 100644
--- a/telephony/java/com/android/internal/telephony/BaseCommands.java
+++ b/telephony/java/com/android/internal/telephony/BaseCommands.java
@@ -73,10 +73,10 @@
protected Registrant mSmsOnSimRegistrant;
protected Registrant mSmsStatusRegistrant;
protected Registrant mSsnRegistrant;
- protected Registrant mStkSessionEndRegistrant;
- protected Registrant mStkProCmdRegistrant;
- protected Registrant mStkEventRegistrant;
- protected Registrant mStkCallSetUpRegistrant;
+ protected Registrant mCatSessionEndRegistrant;
+ protected Registrant mCatProCmdRegistrant;
+ protected Registrant mCatEventRegistrant;
+ protected Registrant mCatCallSetUpRegistrant;
protected Registrant mIccSmsFullRegistrant;
protected Registrant mEmergencyCallbackModeRegistrant;
protected Registrant mIccRefreshRegistrant;
@@ -395,36 +395,36 @@
mSsnRegistrant.clear();
}
- public void setOnStkSessionEnd(Handler h, int what, Object obj) {
- mStkSessionEndRegistrant = new Registrant (h, what, obj);
+ public void setOnCatSessionEnd(Handler h, int what, Object obj) {
+ mCatSessionEndRegistrant = new Registrant (h, what, obj);
}
- public void unSetOnStkSessionEnd(Handler h) {
- mStkSessionEndRegistrant.clear();
+ public void unSetOnCatSessionEnd(Handler h) {
+ mCatSessionEndRegistrant.clear();
}
- public void setOnStkProactiveCmd(Handler h, int what, Object obj) {
- mStkProCmdRegistrant = new Registrant (h, what, obj);
+ public void setOnCatProactiveCmd(Handler h, int what, Object obj) {
+ mCatProCmdRegistrant = new Registrant (h, what, obj);
}
- public void unSetOnStkProactiveCmd(Handler h) {
- mStkProCmdRegistrant.clear();
+ public void unSetOnCatProactiveCmd(Handler h) {
+ mCatProCmdRegistrant.clear();
}
- public void setOnStkEvent(Handler h, int what, Object obj) {
- mStkEventRegistrant = new Registrant (h, what, obj);
+ public void setOnCatEvent(Handler h, int what, Object obj) {
+ mCatEventRegistrant = new Registrant (h, what, obj);
}
- public void unSetOnStkEvent(Handler h) {
- mStkEventRegistrant.clear();
+ public void unSetOnCatEvent(Handler h) {
+ mCatEventRegistrant.clear();
}
- public void setOnStkCallSetUp(Handler h, int what, Object obj) {
- mStkCallSetUpRegistrant = new Registrant (h, what, obj);
+ public void setOnCatCallSetUp(Handler h, int what, Object obj) {
+ mCatCallSetUpRegistrant = new Registrant (h, what, obj);
}
- public void unSetOnStkCallSetUp(Handler h) {
- mStkCallSetUpRegistrant.clear();
+ public void unSetOnCatCallSetUp(Handler h) {
+ mCatCallSetUpRegistrant.clear();
}
public void setOnIccSmsFull(Handler h, int what, Object obj) {
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 5de9aa9..b8bf8af 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -374,48 +374,48 @@
void unSetOnSuppServiceNotification(Handler h);
/**
- * Sets the handler for Session End Notifications for STK.
+ * Sets the handler for Session End Notifications for CAT.
* Unlike the register* methods, there's only one notification handler
*
* @param h Handler for notification message.
* @param what User-defined message code.
* @param obj User object.
*/
- void setOnStkSessionEnd(Handler h, int what, Object obj);
- void unSetOnStkSessionEnd(Handler h);
+ void setOnCatSessionEnd(Handler h, int what, Object obj);
+ void unSetOnCatSessionEnd(Handler h);
/**
- * Sets the handler for Proactive Commands for STK.
+ * Sets the handler for Proactive Commands for CAT.
* Unlike the register* methods, there's only one notification handler
*
* @param h Handler for notification message.
* @param what User-defined message code.
* @param obj User object.
*/
- void setOnStkProactiveCmd(Handler h, int what, Object obj);
- void unSetOnStkProactiveCmd(Handler h);
+ void setOnCatProactiveCmd(Handler h, int what, Object obj);
+ void unSetOnCatProactiveCmd(Handler h);
/**
- * Sets the handler for Event Notifications for STK.
+ * Sets the handler for Event Notifications for CAT.
* Unlike the register* methods, there's only one notification handler
*
* @param h Handler for notification message.
* @param what User-defined message code.
* @param obj User object.
*/
- void setOnStkEvent(Handler h, int what, Object obj);
- void unSetOnStkEvent(Handler h);
+ void setOnCatEvent(Handler h, int what, Object obj);
+ void unSetOnCatEvent(Handler h);
/**
- * Sets the handler for Call Set Up Notifications for STK.
+ * Sets the handler for Call Set Up Notifications for CAT.
* Unlike the register* methods, there's only one notification handler
*
* @param h Handler for notification message.
* @param what User-defined message code.
* @param obj User object.
*/
- void setOnStkCallSetUp(Handler h, int what, Object obj);
- void unSetOnStkCallSetUp(Handler h);
+ void setOnCatCallSetUp(Handler h, int what, Object obj);
+ void unSetOnCatCallSetUp(Handler h);
/**
* Enables/disbables supplementary service related notifications from
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 65bad96..90de5e1 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -144,4 +144,30 @@
in List<String> parts, in List<PendingIntent> sentIntents,
in List<PendingIntent> deliveryIntents);
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #disableCellBroadcast(int)
+ */
+ boolean enableCellBroadcast(int messageIdentifier);
+
+ /**
+ * Disable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcast(int)
+ */
+ boolean disableCellBroadcast(int messageIdentifier);
+
}
diff --git a/telephony/java/com/android/internal/telephony/IWapPushManager.aidl b/telephony/java/com/android/internal/telephony/IWapPushManager.aidl
new file mode 100644
index 0000000..d5ecb94
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IWapPushManager.aidl
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.content.Intent;
+
+interface IWapPushManager {
+ /**
+ * Processes WAP push message and triggers the receiver application registered
+ * in the application ID table.
+ */
+ int processMessage(String app_id, String content_type, in Intent intent);
+
+ /**
+ * Add receiver application into the application ID table.
+ * Returns true if inserting the information is successfull. Inserting the duplicated
+ * record in the application ID table is not allowed. Use update/delete method.
+ */
+ boolean addPackage(String x_app_id, String content_type,
+ String package_name, String class_name,
+ int app_type, boolean need_signature, boolean further_processing);
+
+ /**
+ * Updates receiver application that is last added.
+ * Returns true if updating the information is successfull.
+ */
+ boolean updatePackage(String x_app_id, String content_type,
+ String package_name, String class_name,
+ int app_type, boolean need_signature, boolean further_processing);
+
+ /**
+ * Delites receiver application information.
+ * Returns true if deleting is successfull.
+ */
+ boolean deletePackage(String x_app_id, String content_type,
+ String package_name, String class_name);
+}
+
diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java
index d3a34ec..e270ce9 100644
--- a/telephony/java/com/android/internal/telephony/IccCard.java
+++ b/telephony/java/com/android/internal/telephony/IccCard.java
@@ -487,6 +487,12 @@
CommandsInterface.SERVICE_CLASS_DATA +
CommandsInterface.SERVICE_CLASS_FAX;
+ if (!mPhone.mIsTheCurrentActivePhone) {
+ Log.e(mLogTag, "Received message " + msg + "[" + msg.what
+ + "] while being destroyed. Ignoring.");
+ return;
+ }
+
switch (msg.what) {
case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
mState = null;
@@ -626,7 +632,13 @@
index = mIccCardStatus.getGsmUmtsSubscriptionAppIndex();
}
- IccCardApplication app = mIccCardStatus.getApplication(index);
+ IccCardApplication app;
+ if (index >= 0 && index < IccCardStatus.CARD_MAX_APPS) {
+ app = mIccCardStatus.getApplication(index);
+ } else {
+ Log.e(mLogTag, "[IccCard] Invalid Subscription Application index:" + index);
+ return IccCard.State.ABSENT;
+ }
if (app == null) {
Log.e(mLogTag, "[IccCard] Subscription Application in not present");
@@ -672,12 +684,11 @@
* @return true if a ICC card is present
*/
public boolean hasIccCard() {
- boolean isIccPresent;
- if (mPhone.getPhoneName().equals("GSM")) {
- return mIccCardStatus.getCardState().isCardPresent();
- } else {
- // TODO: Make work with a CDMA device with a RUIM card.
+ if (mIccCardStatus == null) {
return false;
+ } else {
+ // Returns ICC card status for both GSM and CDMA mode
+ return mIccCardStatus.getCardState().isCardPresent();
}
}
diff --git a/telephony/java/com/android/internal/telephony/IccConstants.java b/telephony/java/com/android/internal/telephony/IccConstants.java
index acc9197..b12d2d4 100644
--- a/telephony/java/com/android/internal/telephony/IccConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccConstants.java
@@ -52,6 +52,7 @@
static final int EF_SPN_CPHS = 0x6f14;
static final int EF_SPN_SHORT_CPHS = 0x6f18;
static final int EF_INFO_CPHS = 0x6f16;
+ static final int EF_CSP_CPHS = 0x6f15;
// CDMA RUIM file ids from 3GPP2 C.S0023-0
static final int EF_CST = 0x6f32;
diff --git a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
index 9f8e57f..2f22d74 100644
--- a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -62,8 +62,8 @@
logd("GET_RECORD_SIZE Size " + recordSize[0] +
" total " + recordSize[1] +
" #record " + recordSize[2]);
- mLock.notifyAll();
}
+ mLock.notifyAll();
}
break;
case EVENT_UPDATE_DONE:
@@ -144,6 +144,9 @@
if (DBG) logd("updateAdnRecordsInEfBySearch: efid=" + efid +
" ("+ oldTag + "," + oldPhoneNumber + ")"+ "==>" +
" ("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2);
+
+ efid = updateEfForIccType(efid);
+
synchronized(mLock) {
checkThread();
success = false;
diff --git a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
index 1910a9c..5049249 100644
--- a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
+++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
@@ -68,4 +68,12 @@
parts, sentIntents, deliveryIntents);
}
+ public boolean enableCellBroadcast(int messageIdentifier) throws android.os.RemoteException {
+ return mIccSmsInterfaceManager.enableCellBroadcast(messageIdentifier);
+ }
+
+ public boolean disableCellBroadcast(int messageIdentifier) throws android.os.RemoteException {
+ return mIccSmsInterfaceManager.disableCellBroadcast(messageIdentifier);
+ }
+
}
diff --git a/telephony/java/com/android/internal/telephony/IccUtils.java b/telephony/java/com/android/internal/telephony/IccUtils.java
index 71936f1..5bd7523 100644
--- a/telephony/java/com/android/internal/telephony/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/IccUtils.java
@@ -51,6 +51,8 @@
ret.append((char)('0' + v));
v = (data[i] >> 4) & 0xf;
+ // Some PLMNs have 'f' as high nibble, ignore it
+ if (v == 0xf) continue;
if (v > 9) break;
ret.append((char)('0' + v));
}
@@ -148,6 +150,47 @@
*/
public static String
adnStringFieldToString(byte[] data, int offset, int length) {
+ String s = adnStringFieldToStringUcs2Helper(data, offset, length);
+ if (s == null) {
+ s = adnStringFieldToStringGsm8BitHelper(data, offset, length);
+ }
+ return s;
+ }
+
+ /**
+ * Almost identical to the method {@link #adnStringFieldToString}.
+ *
+ * Exception:
+ * If the SIM is Korean (MCC equals "450"), KSC5601 encoding will be
+ * assumed (instead of GSM8Bit). This could lead to unintended consequences,
+ * if the ADN alphaTag was saved with GSM8Bit. This is considered an
+ * acceptable risk.
+ */
+ public static String
+ adnStringFieldToStringKsc5601Support(byte[] data, int offset, int length) {
+ String s = adnStringFieldToStringUcs2Helper(data, offset, length);
+
+ if (s == null) {
+ if (SimRegionCache.getRegion() == SimRegionCache.MCC_KOREAN) {
+ try {
+ int len = offset;
+ byte stop = (byte)0xFF;
+ while (len < length && data[len] != stop) {
+ len++;
+ }
+ return new String(data, offset, len, "KSC5601");
+ } catch (UnsupportedEncodingException e) {
+ Log.e(LOG_TAG, "implausible UnsupportedEncodingException", e);
+ }
+ }
+
+ return adnStringFieldToStringGsm8BitHelper(data, offset, length);
+ }
+ return s;
+ }
+
+ private static String
+ adnStringFieldToStringUcs2Helper(byte[] data, int offset, int length) {
if (length >= 1) {
if (data[offset] == (byte) 0x80) {
int ucslen = (length - 1) / 2;
@@ -223,6 +266,11 @@
return ret.toString();
}
+ return null;
+ }
+
+ private static String
+ adnStringFieldToStringGsm8BitHelper(byte[] data, int offset, int length) {
return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length);
}
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index c7d4ead..2638057 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -1717,4 +1717,15 @@
void unsetOnEcbModeExitResponse(Handler h);
+ /**
+ * TODO: Adding a function for each property is not good.
+ * A fucntion of type getPhoneProp(propType) where propType is an
+ * enum of GSM+CDMA+LTE props would be a better approach.
+ *
+ * Get "Restriction of menu options for manual PLMN selection" bit
+ * status from EF_CSP data, this belongs to "Value Added Services Group".
+ * @return true if this bit is set or EF_CSP data is unavailable,
+ * false otherwise
+ */
+ boolean isCspPlmnEnabled();
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 9c6b432..f15d5b2 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -1020,6 +1020,13 @@
}
}
+ public boolean isCspPlmnEnabled() {
+ // This function should be overridden by the class GSMPhone.
+ // Not implemented in CDMAPhone.
+ logUnexpectedGsmMethodCall("isCspPlmnEnabled");
+ return false;
+ }
+
/**
* Common error logger method for unexpected calls to CDMA-only methods.
*/
@@ -1028,4 +1035,12 @@
Log.e(LOG_TAG, "Error! " + name + "() in PhoneBase should not be " +
"called, CDMAPhone inactive.");
}
+
+ /**
+ * Common error logger method for unexpected calls to GSM/WCDMA-only methods.
+ */
+ private void logUnexpectedGsmMethodCall(String name) {
+ Log.e(LOG_TAG, "Error! " + name + "() in PhoneBase should not be " +
+ "called, GSMPhone inactive.");
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index 6f08868..77f1e6c 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -843,4 +843,8 @@
public void unsetOnEcbModeExitResponse(Handler h){
mActivePhone.unsetOnEcbModeExitResponse(h);
}
+
+ public boolean isCspPlmnEnabled() {
+ return mActivePhone.isCspPlmnEnabled();
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 09a4506..e059555 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -141,6 +141,7 @@
this.mNext = sPool;
sPool = this;
sPoolSize++;
+ mResult = null;
}
}
}
@@ -370,6 +371,11 @@
rr.onError(GENERIC_FAILURE, null);
rr.release();
}
+ } finally {
+ // Note: We are "Done" only if there are no outstanding
+ // requests or replies. Thus this code path will only release
+ // the wake lock on errors.
+ releaseWakeLockIfDone();
}
if (!alreadySubtracted && mRequestMessagesPending > 0) {
@@ -1989,26 +1995,30 @@
sendScreenState(true);
}
- private void setRadioStateFromRILInt(int state) {
- RadioState newState;
+ private RadioState getRadioStateFromInt(int stateInt) {
+ RadioState state;
/* RIL_RadioState ril.h */
- switch(state) {
- case 0: newState = RadioState.RADIO_OFF; break;
- case 1: newState = RadioState.RADIO_UNAVAILABLE; break;
- case 2: newState = RadioState.SIM_NOT_READY; break;
- case 3: newState = RadioState.SIM_LOCKED_OR_ABSENT; break;
- case 4: newState = RadioState.SIM_READY; break;
- case 5: newState = RadioState.RUIM_NOT_READY; break;
- case 6: newState = RadioState.RUIM_READY; break;
- case 7: newState = RadioState.RUIM_LOCKED_OR_ABSENT; break;
- case 8: newState = RadioState.NV_NOT_READY; break;
- case 9: newState = RadioState.NV_READY; break;
+ switch(stateInt) {
+ case 0: state = RadioState.RADIO_OFF; break;
+ case 1: state = RadioState.RADIO_UNAVAILABLE; break;
+ case 2: state = RadioState.SIM_NOT_READY; break;
+ case 3: state = RadioState.SIM_LOCKED_OR_ABSENT; break;
+ case 4: state = RadioState.SIM_READY; break;
+ case 5: state = RadioState.RUIM_NOT_READY; break;
+ case 6: state = RadioState.RUIM_READY; break;
+ case 7: state = RadioState.RUIM_LOCKED_OR_ABSENT; break;
+ case 8: state = RadioState.NV_NOT_READY; break;
+ case 9: state = RadioState.NV_READY; break;
default:
throw new RuntimeException(
- "Unrecognized RIL_RadioState: " +state);
+ "Unrecognized RIL_RadioState: " + stateInt);
}
+ return state;
+ }
+
+ private void switchToRadioState(RadioState newState) {
if (mInitialRadioStateChange) {
if (newState.isOn()) {
@@ -2067,6 +2077,12 @@
send(RILRequest rr) {
Message msg;
+ if (mSocket == null) {
+ rr.onError(RADIO_NOT_AVAILABLE, null);
+ rr.release();
+ return;
+ }
+
msg = mSender.obtainMessage(EVENT_SEND, rr);
acquireWakeLock();
@@ -2398,7 +2414,7 @@
case RIL_UNSOL_RESTRICTED_STATE_CHANGED: ret = responseInts(p); break;
case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: ret = responseVoid(p); break;
case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: ret = responseCdmaSms(p); break;
- case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: ret = responseString(p); break;
+ case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: ret = responseRaw(p); break;
case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: ret = responseVoid(p); break;
case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break;
case RIL_UNSOL_CDMA_CALL_WAITING: ret = responseCdmaCallWaiting(p); break;
@@ -2420,9 +2436,10 @@
switch(response) {
case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
/* has bonus radio state int */
- setRadioStateFromRILInt(p.readInt());
+ RadioState newState = getRadioStateFromInt(p.readInt());
+ if (RILJ_LOGD) unsljLogMore(response, newState.toString());
- if (RILJ_LOGD) unsljLogMore(response, mState.toString());
+ switchToRadioState(newState);
break;
case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
if (RILJ_LOGD) unsljLog(response);
@@ -2540,8 +2557,8 @@
case RIL_UNSOL_STK_SESSION_END:
if (RILJ_LOGD) unsljLog(response);
- if (mStkSessionEndRegistrant != null) {
- mStkSessionEndRegistrant.notifyRegistrant(
+ if (mCatSessionEndRegistrant != null) {
+ mCatSessionEndRegistrant.notifyRegistrant(
new AsyncResult (null, ret, null));
}
break;
@@ -2549,8 +2566,8 @@
case RIL_UNSOL_STK_PROACTIVE_COMMAND:
if (RILJ_LOGD) unsljLogRet(response, ret);
- if (mStkProCmdRegistrant != null) {
- mStkProCmdRegistrant.notifyRegistrant(
+ if (mCatProCmdRegistrant != null) {
+ mCatProCmdRegistrant.notifyRegistrant(
new AsyncResult (null, ret, null));
}
break;
@@ -2558,8 +2575,8 @@
case RIL_UNSOL_STK_EVENT_NOTIFY:
if (RILJ_LOGD) unsljLogRet(response, ret);
- if (mStkEventRegistrant != null) {
- mStkEventRegistrant.notifyRegistrant(
+ if (mCatEventRegistrant != null) {
+ mCatEventRegistrant.notifyRegistrant(
new AsyncResult (null, ret, null));
}
break;
@@ -2567,8 +2584,8 @@
case RIL_UNSOL_STK_CALL_SETUP:
if (RILJ_LOGD) unsljLogRet(response, ret);
- if (mStkCallSetUpRegistrant != null) {
- mStkCallSetUpRegistrant.notifyRegistrant(
+ if (mCatCallSetUpRegistrant != null) {
+ mCatCallSetUpRegistrant.notifyRegistrant(
new AsyncResult (null, ret, null));
}
break;
@@ -3469,6 +3486,8 @@
RILRequest rr = RILRequest.obtain(
RILConstants.RIL_REQUEST_QUERY_TTY_MODE, response);
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
send(rr);
}
@@ -3482,6 +3501,9 @@
rr.mp.writeInt(1);
rr.mp.writeInt(ttyMode);
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " : " + ttyMode);
+
send(rr);
}
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index 0b12b34..ad34550 100755
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -113,6 +113,9 @@
/** Radio is ON */
static final protected int EVENT_RADIO_ON = 12;
+ /** New broadcast SMS */
+ static final protected int EVENT_NEW_BROADCAST_SMS = 13;
+
protected Phone mPhone;
protected Context mContext;
protected ContentResolver mResolver;
@@ -393,6 +396,10 @@
obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
}
break;
+
+ case EVENT_NEW_BROADCAST_SMS:
+ handleBroadcastSms((AsyncResult)msg.obj);
+ break;
}
}
@@ -1004,4 +1011,17 @@
}
}
};
+
+ protected abstract void handleBroadcastSms(AsyncResult ar);
+
+ protected void dispatchBroadcastPdus(byte[][] pdus) {
+ Intent intent = new Intent("android.provider.telephony.SMS_CB_RECEIVED");
+ intent.putExtra("pdus", pdus);
+
+ if (Config.LOGD)
+ Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus");
+
+ dispatch(intent, "android.permission.RECEIVE_SMS");
+ }
+
}
diff --git a/telephony/java/com/android/internal/telephony/SimRegionCache.java b/telephony/java/com/android/internal/telephony/SimRegionCache.java
new file mode 100644
index 0000000..2cf6d25
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/SimRegionCache.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.os.SystemProperties;
+
+public class SimRegionCache {
+ public static final int MCC_UNSET = Integer.MIN_VALUE;
+ public static final int MCC_KOREAN = 450;
+
+ private static int regionFromMcc = MCC_UNSET;
+
+ /**
+ * Returns the region as read from the MCC of the SIM card.
+ * If the property {@link TelephonyProperties#
+ * PROPERTY_ICC_OPERATOR_NUMERIC}
+ * returns null or an empty String, the value is {@link #MCC_UNSET}
+ *
+ * @return the cached region, if set.
+ */
+ public static int getRegion() {
+ if (regionFromMcc == MCC_UNSET) {
+ String plmn = SystemProperties.get(
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC,
+ null);
+
+ if (plmn != null && plmn.length() >= 3) {
+ try {
+ regionFromMcc = Integer.parseInt(plmn.substring(0, 3));
+ } catch(Exception e) {
+ // Nothing that can be done here.
+ }
+ }
+ }
+ return regionFromMcc;
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/WapPushManagerParams.java b/telephony/java/com/android/internal/telephony/WapPushManagerParams.java
new file mode 100644
index 0000000..11e5ff9
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/WapPushManagerParams.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+/**
+ * WapPushManager constant value definitions
+ */
+public class WapPushManagerParams {
+ /**
+ * Application type activity
+ */
+ public static final int APP_TYPE_ACTIVITY = 0;
+
+ /**
+ * Application type service
+ */
+ public static final int APP_TYPE_SERVICE = 1;
+
+ /**
+ * Process Message return value
+ * Message is handled
+ */
+ public static final int MESSAGE_HANDLED = 0x1;
+
+ /**
+ * Process Message return value
+ * Application ID or content type was not found in the application ID table
+ */
+ public static final int APP_QUERY_FAILED = 0x2;
+
+ /**
+ * Process Message return value
+ * Receiver application signature check failed
+ */
+ public static final int SIGNATURE_NO_MATCH = 0x4;
+
+ /**
+ * Process Message return value
+ * Receiver application was not found
+ */
+ public static final int INVALID_RECEIVER_NAME = 0x8;
+
+ /**
+ * Process Message return value
+ * Unknown exception
+ */
+ public static final int EXCEPTION_CAUGHT = 0x10;
+
+ /**
+ * Process Message return value
+ * Need further processing after WapPushManager message processing
+ */
+ public static final int FURTHER_PROCESSING = 0x8000;
+
+}
+
diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
index 168b63b..7704667 100644
--- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
@@ -14,16 +14,20 @@
* limitations under the License.
*/
+
package com.android.internal.telephony;
import android.app.Activity;
import android.content.Context;
+import android.content.ComponentName;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
import android.util.Config;
import android.util.Log;
-
+import android.os.IBinder;
+import android.os.RemoteException;
/**
* WAP push handler class.
@@ -43,11 +47,83 @@
*/
private final int WAKE_LOCK_TIMEOUT = 5000;
+ private final int BIND_RETRY_INTERVAL = 1000;
+ /**
+ * A handle to WapPushManager interface
+ */
+ private WapPushConnection mWapConn = null;
+ private class WapPushConnection implements ServiceConnection {
+ private IWapPushManager mWapPushMan;
+ private Context mOwner;
+
+ public WapPushConnection(Context ownerContext) {
+ mOwner = ownerContext;
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mWapPushMan = IWapPushManager.Stub.asInterface(service);
+ if (Config.DEBUG) Log.v(LOG_TAG, "wappush manager connected to " +
+ mOwner.hashCode());
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mWapPushMan = null;
+ if (Config.DEBUG) Log.v(LOG_TAG, "wappush manager disconnected.");
+ // WapPushManager must be always attached.
+ rebindWapPushManager();
+ }
+
+ /**
+ * bind WapPushManager
+ */
+ public void bindWapPushManager() {
+ if (mWapPushMan != null) return;
+
+ final ServiceConnection wapPushConnection = this;
+
+ mOwner.bindService(new Intent(IWapPushManager.class.getName()),
+ wapPushConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ /**
+ * rebind WapPushManager
+ * This method is called when WapPushManager is disconnected unexpectedly.
+ */
+ private void rebindWapPushManager() {
+ if (mWapPushMan != null) return;
+
+ final ServiceConnection wapPushConnection = this;
+ new Thread() {
+ public void run() {
+ while (mWapPushMan == null) {
+ mOwner.bindService(new Intent(IWapPushManager.class.getName()),
+ wapPushConnection, Context.BIND_AUTO_CREATE);
+ try {
+ Thread.sleep(BIND_RETRY_INTERVAL);
+ } catch (InterruptedException e) {
+ if (Config.DEBUG) Log.v(LOG_TAG, "sleep interrupted.");
+ }
+ }
+ }
+ }.start();
+ }
+
+ /**
+ * Returns interface to WapPushManager
+ */
+ public IWapPushManager getWapPushManager() {
+ return mWapPushMan;
+ }
+ }
+
public WapPushOverSms(Phone phone, SMSDispatcher smsDispatcher) {
mSmsDispatcher = smsDispatcher;
mContext = phone.getContext();
+ mWapConn = new WapPushConnection(mContext);
+ mWapConn.bindWapPushManager();
}
+
/**
* Dispatches inbound messages that are in the WAP PDU format. See
* wap-230-wsp-20010705-a section 8 for details on the WAP PDU format.
@@ -59,7 +135,7 @@
*/
public int dispatchWapPdu(byte[] pdu) {
- if (Config.LOGD) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
+ if (Config.DEBUG) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
int index = 0;
int transactionId = pdu[index++] & 0xFF;
@@ -68,7 +144,7 @@
if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
(pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
- if (Config.LOGD) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
+ if (Config.DEBUG) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
return Intents.RESULT_SMS_HANDLED;
}
@@ -81,7 +157,7 @@
* So it will be encoded in no more than 5 octets.
*/
if (pduDecoder.decodeUintvarInteger(index) == false) {
- if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Length error.");
+ if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Length error.");
return Intents.RESULT_SMS_GENERIC_ERROR;
}
headerLength = (int)pduDecoder.getValue32();
@@ -102,141 +178,99 @@
* Length = Uintvar-integer
*/
if (pduDecoder.decodeContentType(index) == false) {
- if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
+ if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
return Intents.RESULT_SMS_GENERIC_ERROR;
}
- int binaryContentType;
+
String mimeType = pduDecoder.getValueString();
- if (mimeType == null) {
- binaryContentType = (int)pduDecoder.getValue32();
- // TODO we should have more generic way to map binaryContentType code to mimeType.
- switch (binaryContentType) {
- case WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_XML:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_WBXML:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_PUSH_SI:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SI;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_PUSH_SL:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SL;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_MMS:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_VND_DOCOMO_PF:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_VND_DOCOMO_PF;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_SUPL_INIT:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_SUPL_INIT;
- break;
- default:
- if (Config.LOGD) {
- Log.w(LOG_TAG,
- "Received PDU. Unsupported Content-Type = " + binaryContentType);
- }
- return Intents.RESULT_SMS_HANDLED;
- }
- } else {
- if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_XML;
- } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_WBXML;
- } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SI)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_SI;
- } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SL)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_SL;
- } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO;
- } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_MMS;
- } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_VND_DOCOMO_PF)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_VND_DOCOMO_PF;
- } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_SUPL_INIT)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_SUPL_INIT;
- } else {
- if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Unknown Content-Type = " + mimeType);
- return Intents.RESULT_SMS_HANDLED;
- }
- }
+ long binaryContentType = pduDecoder.getValue32();
index += pduDecoder.getDecodedDataLength();
- boolean dispatchedByApplication = false;
- switch (binaryContentType) {
- case WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO:
- dispatchWapPdu_PushCO(pdu, transactionId, pduType, headerStartIndex, headerLength);
- dispatchedByApplication = true;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_MMS:
- dispatchWapPdu_MMS(pdu, transactionId, pduType, headerStartIndex, headerLength);
- dispatchedByApplication = true;
- break;
- default:
- break;
- }
- if (dispatchedByApplication == false) {
- dispatchWapPdu_default(pdu, transactionId, pduType, mimeType,
- headerStartIndex, headerLength);
- }
- return Activity.RESULT_OK;
- }
-
- private void dispatchWapPdu_default(byte[] pdu, int transactionId, int pduType,
- String mimeType, int headerStartIndex, int headerLength) {
byte[] header = new byte[headerLength];
System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
- int dataIndex = headerStartIndex + headerLength;
- byte[] data;
- data = new byte[pdu.length - dataIndex];
- System.arraycopy(pdu, dataIndex, data, 0, data.length);
+ byte[] intentData;
+
+ if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+ intentData = pdu;
+ } else {
+ int dataIndex = headerStartIndex + headerLength;
+ intentData = new byte[pdu.length - dataIndex];
+ System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
+ }
+
+ /**
+ * Seek for application ID field in WSP header.
+ * If application ID is found, WapPushManager substitute the message
+ * processing. Since WapPushManager is optional module, if WapPushManager
+ * is not found, legacy message processing will be continued.
+ */
+ if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) {
+ index = (int) pduDecoder.getValue32();
+ pduDecoder.decodeXWapApplicationId(index);
+ String wapAppId = pduDecoder.getValueString();
+ if (wapAppId == null) {
+ wapAppId = Integer.toString((int) pduDecoder.getValue32());
+ }
+
+ String contentType = ((mimeType == null) ?
+ Long.toString(binaryContentType) : mimeType);
+ if (Config.DEBUG) Log.v(LOG_TAG, "appid found: " + wapAppId + ":" + contentType);
+
+ try {
+ boolean processFurther = true;
+ IWapPushManager wapPushMan = mWapConn.getWapPushManager();
+
+ if (wapPushMan == null) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "wap push manager not found!");
+ } else {
+ Intent intent = new Intent();
+ intent.putExtra("transactionId", transactionId);
+ intent.putExtra("pduType", pduType);
+ intent.putExtra("header", header);
+ intent.putExtra("data", intentData);
+ intent.putExtra("contentTypeParameters",
+ pduDecoder.getContentParameters());
+
+ int procRet = wapPushMan.processMessage(wapAppId, contentType, intent);
+ if (Config.DEBUG) Log.v(LOG_TAG, "procRet:" + procRet);
+ if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0
+ && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) {
+ processFurther = false;
+ }
+ }
+ if (!processFurther) {
+ return Intents.RESULT_SMS_HANDLED;
+ }
+ } catch (RemoteException e) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "remote func failed...");
+ }
+ }
+ if (Config.DEBUG) Log.v(LOG_TAG, "fall back to existing handler");
+
+ if (mimeType == null) {
+ if (Config.DEBUG) Log.w(LOG_TAG, "Header Content-Type error.");
+ return Intents.RESULT_SMS_GENERIC_ERROR;
+ }
+
+ String permission;
+
+ if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_MMS)) {
+ permission = "android.permission.RECEIVE_MMS";
+ } else {
+ permission = "android.permission.RECEIVE_WAP_PUSH";
+ }
Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION);
intent.setType(mimeType);
intent.putExtra("transactionId", transactionId);
intent.putExtra("pduType", pduType);
intent.putExtra("header", header);
- intent.putExtra("data", data);
+ intent.putExtra("data", intentData);
+ intent.putExtra("contentTypeParameters", pduDecoder.getContentParameters());
- mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH");
- }
+ mSmsDispatcher.dispatch(intent, permission);
- private void dispatchWapPdu_PushCO(byte[] pdu, int transactionId, int pduType,
- int headerStartIndex, int headerLength) {
- byte[] header = new byte[headerLength];
- System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
-
- Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION);
- intent.setType(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO);
- intent.putExtra("transactionId", transactionId);
- intent.putExtra("pduType", pduType);
- intent.putExtra("header", header);
- intent.putExtra("data", pdu);
-
- mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH");
- }
-
- private void dispatchWapPdu_MMS(byte[] pdu, int transactionId, int pduType,
- int headerStartIndex, int headerLength) {
- byte[] header = new byte[headerLength];
- System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
- int dataIndex = headerStartIndex + headerLength;
- byte[] data = new byte[pdu.length - dataIndex];
- System.arraycopy(pdu, dataIndex, data, 0, data.length);
-
- Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION);
- intent.setType(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS);
- intent.putExtra("transactionId", transactionId);
- intent.putExtra("pduType", pduType);
- intent.putExtra("header", header);
- intent.putExtra("data", data);
-
- mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_MMS");
+ return Activity.RESULT_OK;
}
}
-
diff --git a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
index 5dc89f0..c8dd718 100644
--- a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
+++ b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
@@ -16,11 +16,12 @@
package com.android.internal.telephony;
+import java.util.HashMap;
/**
- * Implement the WSP data type decoder.
+ * Implement the WSP data type decoder.
*
- * @hide
+ * @hide
*/
public class WspTypeDecoder {
@@ -30,37 +31,177 @@
public static final int PDU_TYPE_PUSH = 0x06;
public static final int PDU_TYPE_CONFIRMED_PUSH = 0x07;
- // TODO we should have mapping between those binary code and mime type string.
- // see http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
+ private final static HashMap<Integer, String> WELL_KNOWN_MIME_TYPES =
+ new HashMap<Integer, String>();
- public static final int CONTENT_TYPE_B_DRM_RIGHTS_XML = 0x4a;
- public static final int CONTENT_TYPE_B_DRM_RIGHTS_WBXML = 0x4b;
- public static final int CONTENT_TYPE_B_PUSH_SI = 0x2e;
- public static final int CONTENT_TYPE_B_PUSH_SL = 0x30;
- public static final int CONTENT_TYPE_B_PUSH_CO = 0x32;
- public static final int CONTENT_TYPE_B_MMS = 0x3e;
- public static final int CONTENT_TYPE_B_VND_DOCOMO_PF = 0x0310;
- public static final int CONTENT_TYPE_B_SUPL_INIT = 0x312;
-
- public static final String CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML =
- "application/vnd.oma.drm.rights+xml";
- public static final String CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML =
- "application/vnd.oma.drm.rights+wbxml";
- public static final String CONTENT_MIME_TYPE_B_PUSH_SI = "application/vnd.wap.sic";
- public static final String CONTENT_MIME_TYPE_B_PUSH_SL = "application/vnd.wap.slc";
- public static final String CONTENT_MIME_TYPE_B_PUSH_CO = "application/vnd.wap.coc";
- public static final String CONTENT_MIME_TYPE_B_MMS = "application/vnd.wap.mms-message";
- public static final String CONTENT_MIME_TYPE_B_VND_DOCOMO_PF = "application/vnd.docomo.pf";
- public static final String CONTENT_MIME_TYPE_B_SUPL_INIT = "application/vnd.omaloc-supl-init";
+ private final static HashMap<Integer, String> WELL_KNOWN_PARAMETERS =
+ new HashMap<Integer, String>();
public static final int PARAMETER_ID_X_WAP_APPLICATION_ID = 0x2f;
+ private static final int Q_VALUE = 0x00;
+ static {
+ WELL_KNOWN_MIME_TYPES.put(0x00, "*/*");
+ WELL_KNOWN_MIME_TYPES.put(0x01, "text/*");
+ WELL_KNOWN_MIME_TYPES.put(0x02, "text/html");
+ WELL_KNOWN_MIME_TYPES.put(0x03, "text/plain");
+ WELL_KNOWN_MIME_TYPES.put(0x04, "text/x-hdml");
+ WELL_KNOWN_MIME_TYPES.put(0x05, "text/x-ttml");
+ WELL_KNOWN_MIME_TYPES.put(0x06, "text/x-vCalendar");
+ WELL_KNOWN_MIME_TYPES.put(0x07, "text/x-vCard");
+ WELL_KNOWN_MIME_TYPES.put(0x08, "text/vnd.wap.wml");
+ WELL_KNOWN_MIME_TYPES.put(0x09, "text/vnd.wap.wmlscript");
+ WELL_KNOWN_MIME_TYPES.put(0x0A, "text/vnd.wap.wta-event");
+ WELL_KNOWN_MIME_TYPES.put(0x0B, "multipart/*");
+ WELL_KNOWN_MIME_TYPES.put(0x0C, "multipart/mixed");
+ WELL_KNOWN_MIME_TYPES.put(0x0D, "multipart/form-data");
+ WELL_KNOWN_MIME_TYPES.put(0x0E, "multipart/byterantes");
+ WELL_KNOWN_MIME_TYPES.put(0x0F, "multipart/alternative");
+ WELL_KNOWN_MIME_TYPES.put(0x10, "application/*");
+ WELL_KNOWN_MIME_TYPES.put(0x11, "application/java-vm");
+ WELL_KNOWN_MIME_TYPES.put(0x12, "application/x-www-form-urlencoded");
+ WELL_KNOWN_MIME_TYPES.put(0x13, "application/x-hdmlc");
+ WELL_KNOWN_MIME_TYPES.put(0x14, "application/vnd.wap.wmlc");
+ WELL_KNOWN_MIME_TYPES.put(0x15, "application/vnd.wap.wmlscriptc");
+ WELL_KNOWN_MIME_TYPES.put(0x16, "application/vnd.wap.wta-eventc");
+ WELL_KNOWN_MIME_TYPES.put(0x17, "application/vnd.wap.uaprof");
+ WELL_KNOWN_MIME_TYPES.put(0x18, "application/vnd.wap.wtls-ca-certificate");
+ WELL_KNOWN_MIME_TYPES.put(0x19, "application/vnd.wap.wtls-user-certificate");
+ WELL_KNOWN_MIME_TYPES.put(0x1A, "application/x-x509-ca-cert");
+ WELL_KNOWN_MIME_TYPES.put(0x1B, "application/x-x509-user-cert");
+ WELL_KNOWN_MIME_TYPES.put(0x1C, "image/*");
+ WELL_KNOWN_MIME_TYPES.put(0x1D, "image/gif");
+ WELL_KNOWN_MIME_TYPES.put(0x1E, "image/jpeg");
+ WELL_KNOWN_MIME_TYPES.put(0x1F, "image/tiff");
+ WELL_KNOWN_MIME_TYPES.put(0x20, "image/png");
+ WELL_KNOWN_MIME_TYPES.put(0x21, "image/vnd.wap.wbmp");
+ WELL_KNOWN_MIME_TYPES.put(0x22, "application/vnd.wap.multipart.*");
+ WELL_KNOWN_MIME_TYPES.put(0x23, "application/vnd.wap.multipart.mixed");
+ WELL_KNOWN_MIME_TYPES.put(0x24, "application/vnd.wap.multipart.form-data");
+ WELL_KNOWN_MIME_TYPES.put(0x25, "application/vnd.wap.multipart.byteranges");
+ WELL_KNOWN_MIME_TYPES.put(0x26, "application/vnd.wap.multipart.alternative");
+ WELL_KNOWN_MIME_TYPES.put(0x27, "application/xml");
+ WELL_KNOWN_MIME_TYPES.put(0x28, "text/xml");
+ WELL_KNOWN_MIME_TYPES.put(0x29, "application/vnd.wap.wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x2A, "application/x-x968-cross-cert");
+ WELL_KNOWN_MIME_TYPES.put(0x2B, "application/x-x968-ca-cert");
+ WELL_KNOWN_MIME_TYPES.put(0x2C, "application/x-x968-user-cert");
+ WELL_KNOWN_MIME_TYPES.put(0x2D, "text/vnd.wap.si");
+ WELL_KNOWN_MIME_TYPES.put(0x2E, "application/vnd.wap.sic");
+ WELL_KNOWN_MIME_TYPES.put(0x2F, "text/vnd.wap.sl");
+ WELL_KNOWN_MIME_TYPES.put(0x30, "application/vnd.wap.slc");
+ WELL_KNOWN_MIME_TYPES.put(0x31, "text/vnd.wap.co");
+ WELL_KNOWN_MIME_TYPES.put(0x32, "application/vnd.wap.coc");
+ WELL_KNOWN_MIME_TYPES.put(0x33, "application/vnd.wap.multipart.related");
+ WELL_KNOWN_MIME_TYPES.put(0x34, "application/vnd.wap.sia");
+ WELL_KNOWN_MIME_TYPES.put(0x35, "text/vnd.wap.connectivity-xml");
+ WELL_KNOWN_MIME_TYPES.put(0x36, "application/vnd.wap.connectivity-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x37, "application/pkcs7-mime");
+ WELL_KNOWN_MIME_TYPES.put(0x38, "application/vnd.wap.hashed-certificate");
+ WELL_KNOWN_MIME_TYPES.put(0x39, "application/vnd.wap.signed-certificate");
+ WELL_KNOWN_MIME_TYPES.put(0x3A, "application/vnd.wap.cert-response");
+ WELL_KNOWN_MIME_TYPES.put(0x3B, "application/xhtml+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x3C, "application/wml+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x3D, "text/css");
+ WELL_KNOWN_MIME_TYPES.put(0x3E, "application/vnd.wap.mms-message");
+ WELL_KNOWN_MIME_TYPES.put(0x3F, "application/vnd.wap.rollover-certificate");
+ WELL_KNOWN_MIME_TYPES.put(0x40, "application/vnd.wap.locc+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x41, "application/vnd.wap.loc+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x42, "application/vnd.syncml.dm+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x43, "application/vnd.syncml.dm+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x44, "application/vnd.syncml.notification");
+ WELL_KNOWN_MIME_TYPES.put(0x45, "application/vnd.wap.xhtml+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x46, "application/vnd.wv.csp.cir");
+ WELL_KNOWN_MIME_TYPES.put(0x47, "application/vnd.oma.dd+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x48, "application/vnd.oma.drm.message");
+ WELL_KNOWN_MIME_TYPES.put(0x49, "application/vnd.oma.drm.content");
+ WELL_KNOWN_MIME_TYPES.put(0x4A, "application/vnd.oma.drm.rights+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x4B, "application/vnd.oma.drm.rights+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x4C, "application/vnd.wv.csp+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x4D, "application/vnd.wv.csp+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x4E, "application/vnd.syncml.ds.notification");
+ WELL_KNOWN_MIME_TYPES.put(0x4F, "audio/*");
+ WELL_KNOWN_MIME_TYPES.put(0x50, "video/*");
+ WELL_KNOWN_MIME_TYPES.put(0x51, "application/vnd.oma.dd2+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x52, "application/mikey");
+ WELL_KNOWN_MIME_TYPES.put(0x53, "application/vnd.oma.dcd");
+ WELL_KNOWN_MIME_TYPES.put(0x54, "application/vnd.oma.dcdc");
+
+ WELL_KNOWN_MIME_TYPES.put(0x0201, "application/vnd.uplanet.cacheop-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0202, "application/vnd.uplanet.signal");
+ WELL_KNOWN_MIME_TYPES.put(0x0203, "application/vnd.uplanet.alert-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0204, "application/vnd.uplanet.list-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0205, "application/vnd.uplanet.listcmd-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0206, "application/vnd.uplanet.channel-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0207, "application/vnd.uplanet.provisioning-status-uri");
+ WELL_KNOWN_MIME_TYPES.put(0x0208, "x-wap.multipart/vnd.uplanet.header-set");
+ WELL_KNOWN_MIME_TYPES.put(0x0209, "application/vnd.uplanet.bearer-choice-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x020A, "application/vnd.phonecom.mmc-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x020B, "application/vnd.nokia.syncset+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x020C, "image/x-up-wpng");
+ WELL_KNOWN_MIME_TYPES.put(0x0300, "application/iota.mmc-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0301, "application/iota.mmc-xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0302, "application/vnd.syncml+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0303, "application/vnd.syncml+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0304, "text/vnd.wap.emn+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0305, "text/calendar");
+ WELL_KNOWN_MIME_TYPES.put(0x0306, "application/vnd.omads-email+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0307, "application/vnd.omads-file+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0308, "application/vnd.omads-folder+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0309, "text/directory;profile=vCard");
+ WELL_KNOWN_MIME_TYPES.put(0x030A, "application/vnd.wap.emn+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x030B, "application/vnd.nokia.ipdc-purchase-response");
+ WELL_KNOWN_MIME_TYPES.put(0x030C, "application/vnd.motorola.screen3+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x030D, "application/vnd.motorola.screen3+gzip");
+ WELL_KNOWN_MIME_TYPES.put(0x030E, "application/vnd.cmcc.setting+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x030F, "application/vnd.cmcc.bombing+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0310, "application/vnd.docomo.pf");
+ WELL_KNOWN_MIME_TYPES.put(0x0311, "application/vnd.docomo.ub");
+ WELL_KNOWN_MIME_TYPES.put(0x0312, "application/vnd.omaloc-supl-init");
+ WELL_KNOWN_MIME_TYPES.put(0x0313, "application/vnd.oma.group-usage-list+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0314, "application/oma-directory+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0315, "application/vnd.docomo.pf2");
+ WELL_KNOWN_MIME_TYPES.put(0x0316, "application/vnd.oma.drm.roap-trigger+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0317, "application/vnd.sbm.mid2");
+ WELL_KNOWN_MIME_TYPES.put(0x0318, "application/vnd.wmf.bootstrap");
+ WELL_KNOWN_MIME_TYPES.put(0x0319, "application/vnc.cmcc.dcd+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x031A, "application/vnd.sbm.cid");
+ WELL_KNOWN_MIME_TYPES.put(0x031B, "application/vnd.oma.bcast.provisioningtrigger");
+
+ WELL_KNOWN_PARAMETERS.put(0x00, "Q");
+ WELL_KNOWN_PARAMETERS.put(0x01, "Charset");
+ WELL_KNOWN_PARAMETERS.put(0x02, "Level");
+ WELL_KNOWN_PARAMETERS.put(0x03, "Type");
+ WELL_KNOWN_PARAMETERS.put(0x07, "Differences");
+ WELL_KNOWN_PARAMETERS.put(0x08, "Padding");
+ WELL_KNOWN_PARAMETERS.put(0x09, "Type");
+ WELL_KNOWN_PARAMETERS.put(0x0E, "Max-Age");
+ WELL_KNOWN_PARAMETERS.put(0x10, "Secure");
+ WELL_KNOWN_PARAMETERS.put(0x11, "SEC");
+ WELL_KNOWN_PARAMETERS.put(0x12, "MAC");
+ WELL_KNOWN_PARAMETERS.put(0x13, "Creation-date");
+ WELL_KNOWN_PARAMETERS.put(0x14, "Modification-date");
+ WELL_KNOWN_PARAMETERS.put(0x15, "Read-date");
+ WELL_KNOWN_PARAMETERS.put(0x16, "Size");
+ WELL_KNOWN_PARAMETERS.put(0x17, "Name");
+ WELL_KNOWN_PARAMETERS.put(0x18, "Filename");
+ WELL_KNOWN_PARAMETERS.put(0x19, "Start");
+ WELL_KNOWN_PARAMETERS.put(0x1A, "Start-info");
+ WELL_KNOWN_PARAMETERS.put(0x1B, "Comment");
+ WELL_KNOWN_PARAMETERS.put(0x1C, "Domain");
+ WELL_KNOWN_PARAMETERS.put(0x1D, "Path");
+ }
+
+ public static final String CONTENT_TYPE_B_PUSH_CO = "application/vnd.wap.coc";
+ public static final String CONTENT_TYPE_B_MMS = "application/vnd.wap.mms-message";
byte[] wspData;
int dataLength;
long unsigned32bit;
String stringValue;
+ HashMap<String, String> contentParameters;
+
public WspTypeDecoder(byte[] pdu) {
wspData = pdu;
}
@@ -71,17 +212,17 @@
* @param startIndex The starting position of the "Text-string" in this pdu
*
* @return false when error(not a Text-string) occur
- * return value can be retrieved by getValueString() method
- * length of data in pdu can be retrieved by getValue32() method
+ * return value can be retrieved by getValueString() method length of data in pdu can be
+ * retrieved by getDecodedDataLength() method
*/
public boolean decodeTextString(int startIndex) {
int index = startIndex;
while (wspData[index] != 0) {
index++;
}
- dataLength = index - startIndex + 1;
+ dataLength = index - startIndex + 1;
if (wspData[startIndex] == 127) {
- stringValue = new String(wspData, startIndex+1, dataLength - 2);
+ stringValue = new String(wspData, startIndex + 1, dataLength - 2);
} else {
stringValue = new String(wspData, startIndex, dataLength - 1);
}
@@ -89,13 +230,33 @@
}
/**
+ * Decode the "Token-text" type for WSP pdu
+ *
+ * @param startIndex The starting position of the "Token-text" in this pdu
+ *
+ * @return always true
+ * return value can be retrieved by getValueString() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
+ */
+ public boolean decodeTokenText(int startIndex) {
+ int index = startIndex;
+ while (wspData[index] != 0) {
+ index++;
+ }
+ dataLength = index - startIndex + 1;
+ stringValue = new String(wspData, startIndex, dataLength - 1);
+
+ return true;
+ }
+
+ /**
* Decode the "Short-integer" type for WSP pdu
*
* @param startIndex The starting position of the "Short-integer" in this pdu
*
* @return false when error(not a Short-integer) occur
* return value can be retrieved by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeShortInteger(int startIndex) {
if ((wspData[startIndex] & 0x80) == 0) {
@@ -113,7 +274,7 @@
*
* @return false when error(not a Long-integer) occur
* return value can be retrieved by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeLongInteger(int startIndex) {
int lengthMultiOctet = wspData[startIndex] & 0xff;
@@ -122,10 +283,10 @@
return false;
}
unsigned32bit = 0;
- for (int i=1; i<=lengthMultiOctet; i++) {
- unsigned32bit = (unsigned32bit << 8) | (wspData[startIndex+i] & 0xff);
+ for (int i = 1; i <= lengthMultiOctet; i++) {
+ unsigned32bit = (unsigned32bit << 8) | (wspData[startIndex + i] & 0xff);
}
- dataLength = 1+lengthMultiOctet;
+ dataLength = 1 + lengthMultiOctet;
return true;
}
@@ -136,7 +297,7 @@
*
* @return false when error(not a Integer-Value) occur
* return value can be retrieved by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeIntegerValue(int startIndex) {
if (decodeShortInteger(startIndex) == true) {
@@ -152,10 +313,10 @@
*
* @return false when error(not a Uintvar-integer) occur
* return value can be retrieved by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeUintvarInteger(int startIndex) {
- int index = startIndex;
+ int index = startIndex;
unsigned32bit = 0;
while ((wspData[index] & 0x80) != 0) {
@@ -177,7 +338,7 @@
*
* @return false when error(not a Value-length) occur
* return value can be retrieved by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeValueLength(int startIndex) {
if ((wspData[startIndex] & 0xff) > WAP_PDU_LENGTH_QUOTE) {
@@ -187,22 +348,22 @@
unsigned32bit = wspData[startIndex];
dataLength = 1;
} else {
- decodeUintvarInteger(startIndex+1);
- dataLength ++;
+ decodeUintvarInteger(startIndex + 1);
+ dataLength++;
}
return true;
}
/**
- * Decode the "Extension-media" type for WSP PDU.
- *
- * @param startIndex The starting position of the "Extension-media" in this PDU.
- *
- * @return false on error, such as if there is no Extension-media at startIndex.
- * Side-effects: updates stringValue (available with getValueString()), which will be
- * null on error. The length of the data in the PDU is available with getValue32(), 0
- * on error.
- */
+ * Decode the "Extension-media" type for WSP PDU.
+ *
+ * @param startIndex The starting position of the "Extension-media" in this PDU.
+ *
+ * @return false on error, such as if there is no Extension-media at startIndex.
+ * Side-effects: updates stringValue (available with
+ * getValueString()), which will be null on error. The length of the
+ * data in the PDU is available with getValue32(), 0 on error.
+ */
public boolean decodeExtensionMedia(int startIndex) {
int index = startIndex;
dataLength = 0;
@@ -214,7 +375,7 @@
index++;
}
- dataLength = index - startIndex + 1;
+ dataLength = index - startIndex + 1;
stringValue = new String(wspData, startIndex, dataLength - 1);
return rtrn;
@@ -227,7 +388,7 @@
*
* @return false when error(not a Constrained-encoding) occur
* return value can be retrieved first by getValueString() and second by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeConstrainedEncoding(int startIndex) {
if (decodeShortInteger(startIndex) == true) {
@@ -242,31 +403,162 @@
*
* @param startIndex The starting position of the "Content-type" in this pdu
*
- * @return false when error(not a Content-type) occur
- * return value can be retrieved first by getValueString() and second by getValue32()
- * method length of data in pdu can be retrieved by getValue32() method
+ * @return false when error(not a Content-type) occurs
+ * If a content type exists in the headers (either as inline string, or as well-known
+ * value), getValueString() will return it. If a 'well known value' is encountered that
+ * cannot be mapped to a string mime type, getValueString() will return null, and
+ * getValue32() will return the unknown content type value.
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
+ * Any content type parameters will be accessible via getContentParameters()
*/
public boolean decodeContentType(int startIndex) {
int mediaPrefixLength;
- long mediaFieldLength;
+ contentParameters = new HashMap<String, String>();
- if (decodeValueLength(startIndex) == false) {
- return decodeConstrainedEncoding(startIndex);
- }
- mediaPrefixLength = getDecodedDataLength();
- mediaFieldLength = getValue32();
- if (decodeIntegerValue(startIndex + mediaPrefixLength) == true) {
- dataLength += mediaPrefixLength;
- stringValue = null;
- return true;
- }
- if (decodeExtensionMedia(startIndex + mediaPrefixLength) == true) {
- dataLength += mediaPrefixLength;
- return true;
+ try {
+ if (decodeValueLength(startIndex) == false) {
+ boolean found = decodeConstrainedEncoding(startIndex);
+ if (found) {
+ expandWellKnownMimeType();
+ }
+ return found;
+ }
+ int headersLength = (int) unsigned32bit;
+ mediaPrefixLength = getDecodedDataLength();
+ if (decodeIntegerValue(startIndex + mediaPrefixLength) == true) {
+ dataLength += mediaPrefixLength;
+ int readLength = dataLength;
+ stringValue = null;
+ expandWellKnownMimeType();
+ long wellKnownValue = unsigned32bit;
+ String mimeType = stringValue;
+ if (readContentParameters(startIndex + dataLength,
+ (headersLength - (dataLength - mediaPrefixLength)), 0)) {
+ dataLength += readLength;
+ unsigned32bit = wellKnownValue;
+ stringValue = mimeType;
+ return true;
+ }
+ return false;
+ }
+ if (decodeExtensionMedia(startIndex + mediaPrefixLength) == true) {
+ dataLength += mediaPrefixLength;
+ int readLength = dataLength;
+ expandWellKnownMimeType();
+ long wellKnownValue = unsigned32bit;
+ String mimeType = stringValue;
+ if (readContentParameters(startIndex + dataLength,
+ (headersLength - (dataLength - mediaPrefixLength)), 0)) {
+ dataLength += readLength;
+ unsigned32bit = wellKnownValue;
+ stringValue = mimeType;
+ return true;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ //something doesn't add up
+ return false;
}
return false;
}
+ private boolean readContentParameters(int startIndex, int leftToRead, int accumulator) {
+
+ int totalRead = 0;
+
+ if (leftToRead > 0) {
+ byte nextByte = wspData[startIndex];
+ String value = null;
+ String param = null;
+ if ((nextByte & 0x80) == 0x00 && nextByte > 31) { // untyped
+ decodeTokenText(startIndex);
+ param = stringValue;
+ totalRead += dataLength;
+ } else { // typed
+ if (decodeIntegerValue(startIndex)) {
+ totalRead += dataLength;
+ int wellKnownParameterValue = (int) unsigned32bit;
+ param = WELL_KNOWN_PARAMETERS.get(wellKnownParameterValue);
+ if (param == null) {
+ param = "unassigned/0x" + Long.toHexString(wellKnownParameterValue);
+ }
+ // special case for the "Q" parameter, value is a uintvar
+ if (wellKnownParameterValue == Q_VALUE) {
+ if (decodeUintvarInteger(startIndex + totalRead)) {
+ totalRead += dataLength;
+ value = String.valueOf(unsigned32bit);
+ contentParameters.put(param, value);
+ return readContentParameters(startIndex + totalRead, leftToRead
+ - totalRead, accumulator + totalRead);
+ } else {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+ }
+
+ if (decodeNoValue(startIndex + totalRead)) {
+ totalRead += dataLength;
+ value = null;
+ } else if (decodeIntegerValue(startIndex + totalRead)) {
+ totalRead += dataLength;
+ int intValue = (int) unsigned32bit;
+ if (intValue == 0) {
+ value = "";
+ } else {
+ value = String.valueOf(intValue);
+ }
+ } else {
+ decodeTokenText(startIndex + totalRead);
+ totalRead += dataLength;
+ value = stringValue;
+ if (value.startsWith("\"")) {
+ // quoted string, so remove the quote
+ value = value.substring(1);
+ }
+ }
+ contentParameters.put(param, value);
+ return readContentParameters(startIndex + totalRead, leftToRead - totalRead,
+ accumulator + totalRead);
+
+ } else {
+ dataLength = accumulator;
+ return true;
+ }
+ }
+
+ /**
+ * Check if the next byte is No-Value
+ *
+ * @param startIndex The starting position of the "Content length" in this pdu
+ *
+ * @return true if and only if the next byte is 0x00
+ */
+ private boolean decodeNoValue(int startIndex) {
+ if (wspData[startIndex] == 0) {
+ dataLength = 1;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Populate stringValue with the mime type corresponding to the value in unsigned32bit
+ *
+ * Sets unsigned32bit to -1 if stringValue is already populated
+ */
+ private void expandWellKnownMimeType() {
+ if (stringValue == null) {
+ int binaryContentType = (int) unsigned32bit;
+ stringValue = WELL_KNOWN_MIME_TYPES.get(binaryContentType);
+ } else {
+ unsigned32bit = -1;
+ }
+ }
+
/**
* Decode the "Content length" type for WSP pdu
*
@@ -274,7 +566,7 @@
*
* @return false when error(not a Content length) occur
* return value can be retrieved by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeContentLength(int startIndex) {
return decodeIntegerValue(startIndex);
@@ -287,7 +579,7 @@
*
* @return false when error(not a Content location) occur
* return value can be retrieved by getValueString() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeContentLocation(int startIndex) {
return decodeTextString(startIndex);
@@ -300,7 +592,8 @@
*
* @return false when error(not a X-Wap-Application-Id) occur
* return value can be retrieved first by getValueString() and second by getValue32()
- * method length of data in pdu can be retrieved by getValue32() method
+ * method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeXWapApplicationId(int startIndex) {
if (decodeIntegerValue(startIndex) == true) {
@@ -311,13 +604,77 @@
}
/**
+ * Seek for the "X-Wap-Application-Id" field for WSP pdu
+ *
+ * @param startIndex The starting position of seek pointer
+ * @param endIndex Valid seek area end point
+ *
+ * @return false when error(not a X-Wap-Application-Id) occur
+ * return value can be retrieved by getValue32()
+ */
+ public boolean seekXWapApplicationId(int startIndex, int endIndex) {
+ int index = startIndex;
+
+ try {
+ for (index = startIndex; index <= endIndex; ) {
+ /**
+ * 8.4.1.1 Field name
+ * Field name is integer or text.
+ */
+ if (decodeIntegerValue(index)) {
+ int fieldValue = (int) getValue32();
+
+ if (fieldValue == PARAMETER_ID_X_WAP_APPLICATION_ID) {
+ unsigned32bit = index + 1;
+ return true;
+ }
+ } else {
+ if (!decodeTextString(index)) return false;
+ }
+ index += getDecodedDataLength();
+ if (index > endIndex) return false;
+
+ /**
+ * 8.4.1.2 Field values
+ * Value Interpretation of First Octet
+ * 0 - 30 This octet is followed by the indicated number (0 - 30)
+ of data octets
+ * 31 This octet is followed by a uintvar, which indicates the number
+ * of data octets after it
+ * 32 - 127 The value is a text string, terminated by a zero octet
+ (NUL character)
+ * 128 - 255 It is an encoded 7-bit value; this header has no more data
+ */
+ byte val = wspData[index];
+ if (0 <= val && val <= WAP_PDU_SHORT_LENGTH_MAX) {
+ index += wspData[index] + 1;
+ } else if (val == WAP_PDU_LENGTH_QUOTE) {
+ if (index + 1 >= endIndex) return false;
+ index++;
+ if (!decodeUintvarInteger(index)) return false;
+ index += getDecodedDataLength();
+ } else if (WAP_PDU_LENGTH_QUOTE < val && val <= 127) {
+ if (!decodeTextString(index)) return false;
+ index += getDecodedDataLength();
+ } else {
+ index++;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ //seek application ID failed. WSP header might be corrupted
+ return false;
+ }
+ return false;
+ }
+
+ /**
* Decode the "X-Wap-Content-URI" type for WSP pdu
*
* @param startIndex The starting position of the "X-Wap-Content-URI" in this pdu
*
* @return false when error(not a X-Wap-Content-URI) occur
* return value can be retrieved by getValueString() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeXWapContentURI(int startIndex) {
return decodeTextString(startIndex);
@@ -330,7 +687,7 @@
*
* @return false when error(not a X-Wap-Initiator-URI) occur
* return value can be retrieved by getValueString() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeXWapInitiatorURI(int startIndex) {
return decodeTextString(startIndex);
@@ -356,4 +713,18 @@
public String getValueString() {
return stringValue;
}
+
+ /**
+ * Any parameters encountered as part of a decodeContentType() invocation.
+ *
+ * @return a map of content parameters keyed by their names, or null if
+ * decodeContentType() has not been called If any unassigned
+ * well-known parameters are encountered, the key of the map will be
+ * 'unassigned/0x...', where '...' is the hex value of the
+ * unassigned parameter. If a parameter has No-Value the value will be null.
+ *
+ */
+ public HashMap<String, String> getContentParameters() {
+ return contentParameters;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/AppInterface.java b/telephony/java/com/android/internal/telephony/cat/AppInterface.java
similarity index 86%
rename from telephony/java/com/android/internal/telephony/gsm/stk/AppInterface.java
rename to telephony/java/com/android/internal/telephony/cat/AppInterface.java
index 58f1f97..2eb6ccb 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/AppInterface.java
+++ b/telephony/java/com/android/internal/telephony/cat/AppInterface.java
@@ -14,29 +14,29 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
/**
- * Interface for communication between STK App and STK Telephony
+ * Interface for communication between STK App and CAT Telephony
*
* {@hide}
*/
public interface AppInterface {
/*
- * Intent's actions which are broadcasted by the Telephony once a new STK
+ * Intent's actions which are broadcasted by the Telephony once a new CAT
* proactive command, session end arrive.
*/
- public static final String STK_CMD_ACTION =
+ public static final String CAT_CMD_ACTION =
"android.intent.action.stk.command";
- public static final String STK_SESSION_END_ACTION =
+ public static final String CAT_SESSION_END_ACTION =
"android.intent.action.stk.session_end";
/*
* Callback function from app to telephony to pass a result code and user's
- * input back to the SIM.
+ * input back to the ICC.
*/
- void onCmdResponse(StkResponseMessage resMsg);
+ void onCmdResponse(CatResponseMessage resMsg);
/*
* Enumeration for representing "Type of Command" of proactive commands.
@@ -58,7 +58,8 @@
SET_UP_EVENT_LIST(0x05),
SET_UP_IDLE_MODE_TEXT(0x28),
SET_UP_MENU(0x25),
- SET_UP_CALL(0x10);
+ SET_UP_CALL(0x10),
+ PROVIDE_LOCAL_INFORMATION(0x26);
private int mValue;
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/BerTlv.java b/telephony/java/com/android/internal/telephony/cat/BerTlv.java
similarity index 98%
rename from telephony/java/com/android/internal/telephony/gsm/stk/BerTlv.java
rename to telephony/java/com/android/internal/telephony/cat/BerTlv.java
index 19d3279..774bfa3 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/BerTlv.java
+++ b/telephony/java/com/android/internal/telephony/cat/BerTlv.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import java.util.List;
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkCmdMessage.java b/telephony/java/com/android/internal/telephony/cat/CatCmdMessage.java
similarity index 89%
rename from telephony/java/com/android/internal/telephony/gsm/stk/StkCmdMessage.java
rename to telephony/java/com/android/internal/telephony/cat/CatCmdMessage.java
index 5425a43..5155bb2 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/StkCmdMessage.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatCmdMessage.java
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.os.Parcel;
import android.os.Parcelable;
/**
- * Class used to pass STK messages from telephony to application. Application
+ * Class used to pass CAT messages from telephony to application. Application
* should call getXXX() to get commands's specific values.
*
*/
-public class StkCmdMessage implements Parcelable {
+public class CatCmdMessage implements Parcelable {
// members
CommandDetails mCmdDet;
private TextMessage mTextMsg;
@@ -50,7 +50,7 @@
public TextMessage callMsg;
}
- StkCmdMessage(CommandParams cmdParams) {
+ CatCmdMessage(CommandParams cmdParams) {
mCmdDet = cmdParams.cmdDet;
switch(getCmdType()) {
case SET_UP_MENU:
@@ -88,7 +88,7 @@
}
}
- public StkCmdMessage(Parcel in) {
+ public CatCmdMessage(Parcel in) {
mCmdDet = in.readParcelable(null);
mTextMsg = in.readParcelable(null);
mMenu = in.readParcelable(null);
@@ -130,13 +130,13 @@
}
}
- public static final Parcelable.Creator<StkCmdMessage> CREATOR = new Parcelable.Creator<StkCmdMessage>() {
- public StkCmdMessage createFromParcel(Parcel in) {
- return new StkCmdMessage(in);
+ public static final Parcelable.Creator<CatCmdMessage> CREATOR = new Parcelable.Creator<CatCmdMessage>() {
+ public CatCmdMessage createFromParcel(Parcel in) {
+ return new CatCmdMessage(in);
}
- public StkCmdMessage[] newArray(int size) {
- return new StkCmdMessage[size];
+ public CatCmdMessage[] newArray(int size) {
+ return new CatCmdMessage[size];
}
};
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java b/telephony/java/com/android/internal/telephony/cat/CatException.java
similarity index 80%
rename from telephony/java/com/android/internal/telephony/gsm/stk/StkException.java
rename to telephony/java/com/android/internal/telephony/cat/CatException.java
index 86de366..1bf1369 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatException.java
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.util.AndroidException;
/**
- * Base class for all the exceptions in STK service.
+ * Base class for all the exceptions in CAT service.
*
* {@hide}
*/
-class StkException extends AndroidException {
- public StkException() {
+class CatException extends AndroidException {
+ public CatException() {
super();
}
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkLog.java b/telephony/java/com/android/internal/telephony/cat/CatLog.java
similarity index 84%
rename from telephony/java/com/android/internal/telephony/gsm/stk/StkLog.java
rename to telephony/java/com/android/internal/telephony/cat/CatLog.java
index bd6bc8f..e19ff43 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/StkLog.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatLog.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.util.Log;
-public abstract class StkLog {
+public abstract class CatLog {
static final boolean DEBUG = true;
public static void d(Object caller, String msg) {
@@ -27,7 +27,7 @@
}
String className = caller.getClass().getName();
- Log.d("STK", className.substring(className.lastIndexOf('.') + 1) + ": "
+ Log.d("CAT", className.substring(className.lastIndexOf('.') + 1) + ": "
+ msg);
}
@@ -36,6 +36,6 @@
return;
}
- Log.d("STK", caller + ": " + msg);
+ Log.d("CAT", caller + ": " + msg);
}
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkResponseMessage.java b/telephony/java/com/android/internal/telephony/cat/CatResponseMessage.java
similarity index 91%
rename from telephony/java/com/android/internal/telephony/gsm/stk/StkResponseMessage.java
rename to telephony/java/com/android/internal/telephony/cat/CatResponseMessage.java
index 04a52e6..cfcac36 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/StkResponseMessage.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatResponseMessage.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
-public class StkResponseMessage {
+public class CatResponseMessage {
CommandDetails cmdDet = null;
ResultCode resCode = ResultCode.OK;
int usersMenuSelection = 0;
@@ -24,7 +24,7 @@
boolean usersYesNoSelection = false;
boolean usersConfirm = false;
- public StkResponseMessage(StkCmdMessage cmdMsg) {
+ public CatResponseMessage(CatCmdMessage cmdMsg) {
this.cmdDet = cmdMsg.mCmdDet;
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java b/telephony/java/com/android/internal/telephony/cat/CatService.java
similarity index 67%
rename from telephony/java/com/android/internal/telephony/gsm/stk/StkService.java
rename to telephony/java/com/android/internal/telephony/cat/CatService.java
index 29ed95c..36059ad 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.content.Context;
import android.content.Intent;
@@ -22,12 +22,13 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
+import android.os.SystemProperties;
import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.gsm.SimCard;
-import com.android.internal.telephony.gsm.SIMFileHandler;
-import com.android.internal.telephony.gsm.SIMRecords;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.IccFileHandler;
+import com.android.internal.telephony.IccRecords;
import android.util.Config;
@@ -111,17 +112,19 @@
*
* {@hide}
*/
-public class StkService extends Handler implements AppInterface {
+public class CatService extends Handler implements AppInterface {
// Class members
- private static SIMRecords mSimRecords;
+ private static IccRecords mIccRecords;
// Service members.
- private static StkService sInstance;
+ // Protects singleton instance lazy initialization.
+ private static final Object sInstanceLock = new Object();
+ private static CatService sInstance;
private CommandsInterface mCmdIf;
private Context mContext;
- private StkCmdMessage mCurrntCmd = null;
- private StkCmdMessage mMenuCmd = null;
+ private CatCmdMessage mCurrntCmd = null;
+ private CatCmdMessage mMenuCmd = null;
private RilMessageDecoder mMsgDecoder = null;
@@ -136,7 +139,7 @@
static final int MSG_ID_RIL_MSG_DECODED = 10;
// Events to signal SIM presence or absent in the device.
- private static final int MSG_ID_SIM_LOADED = 20;
+ private static final int MSG_ID_ICC_RECORDS_LOADED = 20;
private static final int DEV_ID_KEYPAD = 0x01;
private static final int DEV_ID_DISPLAY = 0x02;
@@ -146,10 +149,10 @@
private static final int DEV_ID_NETWORK = 0x83;
/* Intentionally private for singleton */
- private StkService(CommandsInterface ci, SIMRecords sr, Context context,
- SIMFileHandler fh, SimCard sc) {
- if (ci == null || sr == null || context == null || fh == null
- || sc == null) {
+ private CatService(CommandsInterface ci, IccRecords ir, Context context,
+ IccFileHandler fh, IccCard ic) {
+ if (ci == null || ir == null || context == null || fh == null
+ || ic == null) {
throw new NullPointerException(
"Service: Input parameters must not be null");
}
@@ -160,33 +163,33 @@
mMsgDecoder = RilMessageDecoder.getInstance(this, fh);
// Register ril events handling.
- mCmdIf.setOnStkSessionEnd(this, MSG_ID_SESSION_END, null);
- mCmdIf.setOnStkProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null);
- mCmdIf.setOnStkEvent(this, MSG_ID_EVENT_NOTIFY, null);
- mCmdIf.setOnStkCallSetUp(this, MSG_ID_CALL_SETUP, null);
+ mCmdIf.setOnCatSessionEnd(this, MSG_ID_SESSION_END, null);
+ mCmdIf.setOnCatProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null);
+ mCmdIf.setOnCatEvent(this, MSG_ID_EVENT_NOTIFY, null);
+ mCmdIf.setOnCatCallSetUp(this, MSG_ID_CALL_SETUP, null);
//mCmdIf.setOnSimRefresh(this, MSG_ID_REFRESH, null);
- mSimRecords = sr;
+ mIccRecords = ir;
// Register for SIM ready event.
- mSimRecords.registerForRecordsLoaded(this, MSG_ID_SIM_LOADED, null);
+ mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null);
mCmdIf.reportStkServiceIsRunning(null);
- StkLog.d(this, "StkService: is running");
+ CatLog.d(this, "Is running");
}
public void dispose() {
- mSimRecords.unregisterForRecordsLoaded(this);
- mCmdIf.unSetOnStkSessionEnd(this);
- mCmdIf.unSetOnStkProactiveCmd(this);
- mCmdIf.unSetOnStkEvent(this);
- mCmdIf.unSetOnStkCallSetUp(this);
+ mIccRecords.unregisterForRecordsLoaded(this);
+ mCmdIf.unSetOnCatSessionEnd(this);
+ mCmdIf.unSetOnCatProactiveCmd(this);
+ mCmdIf.unSetOnCatEvent(this);
+ mCmdIf.unSetOnCatCallSetUp(this);
this.removeCallbacksAndMessages(null);
}
protected void finalize() {
- StkLog.d(this, "Service finalized");
+ CatLog.d(this, "Service finalized");
}
private void handleRilMsg(RilMessage rilMsg) {
@@ -241,55 +244,53 @@
*
*/
private void handleProactiveCommand(CommandParams cmdParams) {
- StkLog.d(this, cmdParams.getCommandType().name());
+ CatLog.d(this, cmdParams.getCommandType().name());
- StkCmdMessage cmdMsg = new StkCmdMessage(cmdParams);
+ CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams);
switch (cmdParams.getCommandType()) {
- case SET_UP_MENU:
- if (removeMenu(cmdMsg.getMenu())) {
- mMenuCmd = null;
- } else {
- mMenuCmd = cmdMsg;
- }
- sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0,
- null);
- break;
- case DISPLAY_TEXT:
- // when application is not required to respond, send an immediate
- // response.
- if (!cmdMsg.geTextMessage().responseNeeded) {
- sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false,
- 0, null);
- }
- break;
- case REFRESH:
- // ME side only handles refresh commands which meant to remove IDLE
- // MODE TEXT.
- cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT
- .value();
- break;
- case SET_UP_IDLE_MODE_TEXT:
- sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false,
- 0, null);
- break;
- case LAUNCH_BROWSER:
- case SELECT_ITEM:
- case GET_INPUT:
- case GET_INKEY:
- case SEND_DTMF:
- case SEND_SMS:
- case SEND_SS:
- case SEND_USSD:
- case PLAY_TONE:
- case SET_UP_CALL:
- // nothing to do on telephony!
- break;
- default:
- StkLog.d(this, "Unsupported command");
- return;
+ case SET_UP_MENU:
+ if (removeMenu(cmdMsg.getMenu())) {
+ mMenuCmd = null;
+ } else {
+ mMenuCmd = cmdMsg;
+ }
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ break;
+ case DISPLAY_TEXT:
+ // when application is not required to respond, send an immediate response.
+ if (!cmdMsg.geTextMessage().responseNeeded) {
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ }
+ break;
+ case REFRESH:
+ // ME side only handles refresh commands which meant to remove IDLE
+ // MODE TEXT.
+ cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value();
+ break;
+ case SET_UP_IDLE_MODE_TEXT:
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ break;
+ case PROVIDE_LOCAL_INFORMATION:
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ return;
+ case LAUNCH_BROWSER:
+ case SELECT_ITEM:
+ case GET_INPUT:
+ case GET_INKEY:
+ case SEND_DTMF:
+ case SEND_SMS:
+ case SEND_SS:
+ case SEND_USSD:
+ case PLAY_TONE:
+ case SET_UP_CALL:
+ // nothing to do on telephony!
+ break;
+ default:
+ CatLog.d(this, "Unsupported command");
+ return;
}
mCurrntCmd = cmdMsg;
- Intent intent = new Intent(AppInterface.STK_CMD_ACTION);
+ Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
intent.putExtra("STK CMD", cmdMsg);
mContext.sendBroadcast(intent);
}
@@ -299,10 +300,10 @@
*
*/
private void handleSessionEnd() {
- StkLog.d(this, "SESSION END");
+ CatLog.d(this, "SESSION END");
mCurrntCmd = mMenuCmd;
- Intent intent = new Intent(AppInterface.STK_SESSION_END_ACTION);
+ Intent intent = new Intent(AppInterface.CAT_SESSION_END_ACTION);
mContext.sendBroadcast(intent);
}
@@ -315,6 +316,11 @@
}
ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ Input cmdInput = null;
+ if (mCurrntCmd != null) {
+ cmdInput = mCurrntCmd.geInput();
+ }
+
// command details
int tag = ComprehensionTlvTag.COMMAND_DETAILS.value();
if (cmdDet.compRequired) {
@@ -327,7 +333,13 @@
buf.write(cmdDet.commandQualifier);
// device identities
- tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value();
+ // According to TS102.223/TS31.111 section 6.8 Structure of
+ // TERMINAL RESPONSE, "For all SIMPLE-TLV objects with Min=N,
+ // the ME should set the CR(comprehension required) flag to
+ // comprehension not required.(CR=0)"
+ // Since DEVICE_IDENTITIES and DURATION TLVs have Min=N,
+ // the CR flag is not set.
+ tag = ComprehensionTlvTag.DEVICE_IDENTITIES.value();
buf.write(tag);
buf.write(0x02); // length
buf.write(DEV_ID_TERMINAL); // source device id
@@ -348,17 +360,65 @@
// Fill optional data for each corresponding command
if (resp != null) {
resp.format(buf);
+ } else {
+ encodeOptionalTags(cmdDet, resultCode, cmdInput, buf);
}
byte[] rawData = buf.toByteArray();
String hexString = IccUtils.bytesToHexString(rawData);
if (Config.LOGD) {
- StkLog.d(this, "TERMINAL RESPONSE: " + hexString);
+ CatLog.d(this, "TERMINAL RESPONSE: " + hexString);
}
mCmdIf.sendTerminalResponse(hexString, null);
}
+ private void encodeOptionalTags(CommandDetails cmdDet,
+ ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) {
+ switch (AppInterface.CommandType.fromInt(cmdDet.typeOfCommand)) {
+ case GET_INKEY:
+ // ETSI TS 102 384,27.22.4.2.8.4.2.
+ // If it is a response for GET_INKEY command and the response timeout
+ // occured, then add DURATION TLV for variable timeout case.
+ if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) &&
+ (cmdInput != null) && (cmdInput.duration != null)) {
+ getInKeyResponse(buf, cmdInput);
+ }
+ break;
+ case PROVIDE_LOCAL_INFORMATION:
+ if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) &&
+ (resultCode.value() == ResultCode.OK.value())) {
+ getPliResponse(buf);
+ }
+ break;
+ default:
+ CatLog.d(this, "encodeOptionalTags() Unsupported Cmd:" + cmdDet.typeOfCommand);
+ break;
+ }
+ }
+
+ private void getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput) {
+ int tag = ComprehensionTlvTag.DURATION.value();
+
+ buf.write(tag);
+ buf.write(0x02); // length
+ buf.write(cmdInput.duration.timeUnit.SECOND.value()); // Time (Unit,Seconds)
+ buf.write(cmdInput.duration.timeInterval); // Time Duration
+ }
+
+ private void getPliResponse(ByteArrayOutputStream buf) {
+
+ // Locale Language Setting
+ String lang = SystemProperties.get("persist.sys.language");
+
+ if (lang != null) {
+ // tag
+ int tag = ComprehensionTlvTag.LANGUAGE.value();
+ buf.write(tag);
+ ResponseData.writeLength(buf, lang.length());
+ buf.write(lang.getBytes(), 0, lang.length());
+ }
+ }
private void sendMenuSelection(int menuId, boolean helpRequired) {
@@ -446,37 +506,39 @@
}
/**
- * Used for instantiating/updating the Service from the GsmPhone constructor.
+ * Used for instantiating/updating the Service from the GsmPhone or CdmaPhone constructor.
*
* @param ci CommandsInterface object
- * @param sr SIMRecords object
+ * @param ir IccRecords object
* @param context phone app context
- * @param fh SIM file handler
- * @param sc GSM SIM card
+ * @param fh Icc file handler
+ * @param ic Icc card
* @return The only Service object in the system
*/
- public static StkService getInstance(CommandsInterface ci, SIMRecords sr,
- Context context, SIMFileHandler fh, SimCard sc) {
- if (sInstance == null) {
- if (ci == null || sr == null || context == null || fh == null
- || sc == null) {
- return null;
- }
- HandlerThread thread = new HandlerThread("Stk Telephony service");
- thread.start();
- sInstance = new StkService(ci, sr, context, fh, sc);
- StkLog.d(sInstance, "NEW sInstance");
- } else if ((sr != null) && (mSimRecords != sr)) {
- StkLog.d(sInstance, "Reinitialize the Service with SIMRecords");
- mSimRecords = sr;
+ public static CatService getInstance(CommandsInterface ci, IccRecords ir,
+ Context context, IccFileHandler fh, IccCard ic) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ if (ci == null || ir == null || context == null || fh == null
+ || ic == null) {
+ return null;
+ }
+ HandlerThread thread = new HandlerThread("Cat Telephony service");
+ thread.start();
+ sInstance = new CatService(ci, ir, context, fh, ic);
+ CatLog.d(sInstance, "NEW sInstance");
+ } else if ((ir != null) && (mIccRecords != ir)) {
+ CatLog.d(sInstance, "Reinitialize the Service with SIMRecords");
+ mIccRecords = ir;
- // re-Register for SIM ready event.
- mSimRecords.registerForRecordsLoaded(sInstance, MSG_ID_SIM_LOADED, null);
- StkLog.d(sInstance, "sr changed reinitialize and return current sInstance");
- } else {
- StkLog.d(sInstance, "Return current sInstance");
+ // re-Register for SIM ready event.
+ mIccRecords.registerForRecordsLoaded(sInstance, MSG_ID_ICC_RECORDS_LOADED, null);
+ CatLog.d(sInstance, "sr changed reinitialize and return current sInstance");
+ } else {
+ CatLog.d(sInstance, "Return current sInstance");
+ }
+ return sInstance;
}
- return sInstance;
}
/**
@@ -496,7 +558,7 @@
case MSG_ID_PROACTIVE_COMMAND:
case MSG_ID_EVENT_NOTIFY:
case MSG_ID_REFRESH:
- StkLog.d(this, "ril message arrived");
+ CatLog.d(this, "ril message arrived");
String data = null;
if (msg.obj != null) {
AsyncResult ar = (AsyncResult) msg.obj;
@@ -513,20 +575,20 @@
case MSG_ID_CALL_SETUP:
mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, null));
break;
- case MSG_ID_SIM_LOADED:
+ case MSG_ID_ICC_RECORDS_LOADED:
break;
case MSG_ID_RIL_MSG_DECODED:
handleRilMsg((RilMessage) msg.obj);
break;
case MSG_ID_RESPONSE:
- handleCmdResponse((StkResponseMessage) msg.obj);
+ handleCmdResponse((CatResponseMessage) msg.obj);
break;
default:
- throw new AssertionError("Unrecognized STK command: " + msg.what);
+ throw new AssertionError("Unrecognized CAT command: " + msg.what);
}
}
- public synchronized void onCmdResponse(StkResponseMessage resMsg) {
+ public synchronized void onCmdResponse(CatResponseMessage resMsg) {
if (resMsg == null) {
return;
}
@@ -535,7 +597,7 @@
msg.sendToTarget();
}
- private boolean validateResponse(StkResponseMessage resMsg) {
+ private boolean validateResponse(CatResponseMessage resMsg) {
if (mCurrntCmd != null) {
return (resMsg.cmdDet.compareTo(mCurrntCmd.mCmdDet));
}
@@ -548,13 +610,13 @@
return true;
}
} catch (NullPointerException e) {
- StkLog.d(this, "Unable to get Menu's items size");
+ CatLog.d(this, "Unable to get Menu's items size");
return true;
}
return false;
}
- private void handleCmdResponse(StkResponseMessage resMsg) {
+ private void handleCmdResponse(CatResponseMessage resMsg) {
// Make sure the response details match the last valid command. An invalid
// response is a one that doesn't have a corresponding proactive command
// and sending it can "confuse" the baseband/ril.
@@ -563,7 +625,7 @@
// by the framework inside the history stack. That activity will be
// available for relaunch using the latest application dialog
// (long press on the home button). Relaunching that activity can send
- // the same command's result again to the StkService and can cause it to
+ // the same command's result again to the CatService and can cause it to
// get out of sync with the SIM.
if (!validateResponse(resMsg)) {
return;
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandDetails.java b/telephony/java/com/android/internal/telephony/cat/CommandDetails.java
similarity index 98%
rename from telephony/java/com/android/internal/telephony/gsm/stk/CommandDetails.java
rename to telephony/java/com/android/internal/telephony/cat/CommandDetails.java
index e81ff98..e3f0798 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/CommandDetails.java
+++ b/telephony/java/com/android/internal/telephony/cat/CommandDetails.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java b/telephony/java/com/android/internal/telephony/cat/CommandParams.java
similarity index 98%
rename from telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java
rename to telephony/java/com/android/internal/telephony/cat/CommandParams.java
index 3da652f..22a5c8c 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java
+++ b/telephony/java/com/android/internal/telephony/cat/CommandParams.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.graphics.Bitmap;
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
similarity index 92%
rename from telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java
rename to telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index ce4c459..12204a0 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java
+++ b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.gsm.SIMFileHandler;
+import com.android.internal.telephony.IccFileHandler;
import java.util.Iterator;
import java.util.List;
@@ -52,8 +52,11 @@
static final int REFRESH_NAA_INIT = 0x03;
static final int REFRESH_UICC_RESET = 0x04;
+ // Command Qualifier values for PLI command
+ static final int LANGUAGE_SETTING = 0x04;
+
static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
- SIMFileHandler fh) {
+ IccFileHandler fh) {
if (sInstance != null) {
return sInstance;
}
@@ -63,7 +66,7 @@
return null;
}
- private CommandParamsFactory(RilMessageDecoder caller, SIMFileHandler fh) {
+ private CommandParamsFactory(RilMessageDecoder caller, IccFileHandler fh) {
mCaller = caller;
mIconLoader = IconLoader.getInstance(this, fh);
}
@@ -79,7 +82,7 @@
try {
cmdDet = ValueParser.retrieveCommandDetails(ctlvCmdDet);
} catch (ResultException e) {
- StkLog.d(this, "Failed to procees command details");
+ CatLog.d(this, "Failed to procees command details");
}
}
}
@@ -112,7 +115,10 @@
AppInterface.CommandType cmdType = AppInterface.CommandType
.fromInt(cmdDet.typeOfCommand);
if (cmdType == null) {
- sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD);
+ // This PROACTIVE COMMAND is presently not handled. Hence set
+ // result code as BEYOND_TERMINAL_CAPABILITY in TR.
+ mCmdParams = new CommandParams(cmdDet);
+ sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY);
return;
}
@@ -155,10 +161,13 @@
case PLAY_TONE:
cmdPending = processPlayTone(cmdDet, ctlvs);
break;
+ case PROVIDE_LOCAL_INFORMATION:
+ cmdPending = processProvideLocalInfo(cmdDet, ctlvs);
+ break;
default:
// unsupported proactive commands
mCmdParams = new CommandParams(cmdDet);
- sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD);
+ sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY);
return;
}
} catch (ResultException e) {
@@ -259,7 +268,7 @@
List<ComprehensionTlv> ctlvs)
throws ResultException {
- StkLog.d(this, "process DisplayText");
+ CatLog.d(this, "process DisplayText");
TextMessage textMsg = new TextMessage();
IconId iconId = null;
@@ -319,7 +328,7 @@
private boolean processSetUpIdleModeText(CommandDetails cmdDet,
List<ComprehensionTlv> ctlvs) throws ResultException {
- StkLog.d(this, "process SetUpIdleModeText");
+ CatLog.d(this, "process SetUpIdleModeText");
TextMessage textMsg = new TextMessage();
IconId iconId = null;
@@ -362,7 +371,7 @@
private boolean processGetInkey(CommandDetails cmdDet,
List<ComprehensionTlv> ctlvs) throws ResultException {
- StkLog.d(this, "process GetInkey");
+ CatLog.d(this, "process GetInkey");
Input input = new Input();
IconId iconId = null;
@@ -380,6 +389,12 @@
iconId = ValueParser.retrieveIconId(ctlv);
}
+ // parse duration
+ ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
+ if (ctlv != null) {
+ input.duration = ValueParser.retrieveDuration(ctlv);
+ }
+
input.minLen = 1;
input.maxLen = 1;
@@ -412,7 +427,7 @@
private boolean processGetInput(CommandDetails cmdDet,
List<ComprehensionTlv> ctlvs) throws ResultException {
- StkLog.d(this, "process GetInput");
+ CatLog.d(this, "process GetInput");
Input input = new Input();
IconId iconId = null;
@@ -476,7 +491,7 @@
private boolean processRefresh(CommandDetails cmdDet,
List<ComprehensionTlv> ctlvs) {
- StkLog.d(this, "process Refresh");
+ CatLog.d(this, "process Refresh");
// REFRESH proactive command is rerouted by the baseband and handled by
// the telephony layer. IDLE TEXT should be removed for a REFRESH command
@@ -505,7 +520,7 @@
private boolean processSelectItem(CommandDetails cmdDet,
List<ComprehensionTlv> ctlvs) throws ResultException {
- StkLog.d(this, "process SelectItem");
+ CatLog.d(this, "process SelectItem");
Menu menu = new Menu();
IconId titleIconId = null;
@@ -534,7 +549,7 @@
ctlv = searchForTag(ComprehensionTlvTag.ITEM_ID, ctlvs);
if (ctlv != null) {
- // STK items are listed 1...n while list start at 0, need to
+ // CAT items are listed 1...n while list start at 0, need to
// subtract one.
menu.defaultItem = ValueParser.retrieveItemId(ctlv) - 1;
}
@@ -602,7 +617,7 @@
private boolean processEventNotify(CommandDetails cmdDet,
List<ComprehensionTlv> ctlvs) throws ResultException {
- StkLog.d(this, "process EventNotify");
+ CatLog.d(this, "process EventNotify");
TextMessage textMsg = new TextMessage();
IconId iconId = null;
@@ -645,7 +660,7 @@
private boolean processSetUpEventList(CommandDetails cmdDet,
List<ComprehensionTlv> ctlvs) {
- StkLog.d(this, "process SetUpEventList");
+ CatLog.d(this, "process SetUpEventList");
//
// ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.EVENT_LIST,
// ctlvs);
@@ -670,10 +685,10 @@
* asynchronous processing is required.
* @throws ResultException
*/
- private boolean processLaunchBrowser(CommandDetails cmdDet,
+ private boolean processLaunchBrowser(CommandDetails cmdDet,
List<ComprehensionTlv> ctlvs) throws ResultException {
- StkLog.d(this, "process LaunchBrowser");
+ CatLog.d(this, "process LaunchBrowser");
TextMessage confirmMsg = new TextMessage();
IconId iconId = null;
@@ -744,10 +759,10 @@
* asynchronous processing is required.t
* @throws ResultException
*/
- private boolean processPlayTone(CommandDetails cmdDet,
+ private boolean processPlayTone(CommandDetails cmdDet,
List<ComprehensionTlv> ctlvs) throws ResultException {
- StkLog.d(this, "process PlayTone");
+ CatLog.d(this, "process PlayTone");
Tone tone = null;
TextMessage textMsg = new TextMessage();
@@ -810,9 +825,9 @@
* @return true if the command is processing is pending and additional
* asynchronous processing is required.
*/
- private boolean processSetupCall(CommandDetails cmdDet,
+ private boolean processSetupCall(CommandDetails cmdDet,
List<ComprehensionTlv> ctlvs) throws ResultException {
- StkLog.d(this, "process SetupCall");
+ CatLog.d(this, "process SetupCall");
Iterator<ComprehensionTlv> iter = ctlvs.iterator();
ComprehensionTlv ctlv = null;
@@ -863,4 +878,20 @@
}
return false;
}
+
+ private boolean processProvideLocalInfo(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)
+ throws ResultException {
+ CatLog.d(this, "process ProvideLocalInfo");
+ switch (cmdDet.commandQualifier) {
+ case LANGUAGE_SETTING:
+ CatLog.d(this, "PLI [LANGUAGE_SETTING]");
+ mCmdParams = new CommandParams(cmdDet);
+ break;
+ default:
+ CatLog.d(this, "PLI[" + cmdDet.commandQualifier + "] Command Not Supported");
+ mCmdParams = new CommandParams(cmdDet);
+ throw new ResultException(ResultCode.BEYOND_TERMINAL_CAPABILITY);
+ }
+ return false;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java b/telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java
similarity index 98%
rename from telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java
rename to telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java
index ffde6a3..99f662d 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java
+++ b/telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import java.util.ArrayList;
import java.util.List;
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Duration.java b/telephony/java/com/android/internal/telephony/cat/Duration.java
similarity index 94%
rename from telephony/java/com/android/internal/telephony/gsm/stk/Duration.java
rename to telephony/java/com/android/internal/telephony/cat/Duration.java
index 9d8cc97..e8cd404 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/Duration.java
+++ b/telephony/java/com/android/internal/telephony/cat/Duration.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.os.Parcel;
import android.os.Parcelable;
/**
- * Class for representing "Duration" object for STK.
+ * Class for representing "Duration" object for CAT.
*
* {@hide}
*/
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/FontSize.java b/telephony/java/com/android/internal/telephony/cat/FontSize.java
similarity index 96%
rename from telephony/java/com/android/internal/telephony/gsm/stk/FontSize.java
rename to telephony/java/com/android/internal/telephony/cat/FontSize.java
index bd4f49f..02c7ea0 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/FontSize.java
+++ b/telephony/java/com/android/internal/telephony/cat/FontSize.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java b/telephony/java/com/android/internal/telephony/cat/IconLoader.java
similarity index 95%
rename from telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java
rename to telephony/java/com/android/internal/telephony/cat/IconLoader.java
index fc02d2a..2fa1811 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java
+++ b/telephony/java/com/android/internal/telephony/cat/IconLoader.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
-import com.android.internal.telephony.gsm.SIMFileHandler;
+import com.android.internal.telephony.IccFileHandler;
import android.graphics.Bitmap;
import android.graphics.Color;
@@ -40,7 +40,7 @@
private ImageDescriptor mId = null;
private Bitmap mCurrentIcon = null;
private int mRecordNumber;
- private SIMFileHandler mSimFH = null;
+ private IccFileHandler mSimFH = null;
private Message mEndMsg = null;
private byte[] mIconData = null;
// multi icons state members
@@ -68,19 +68,19 @@
private static final int CLUT_ENTRY_SIZE = 3;
- private IconLoader(Looper looper , SIMFileHandler fh) {
+ private IconLoader(Looper looper , IccFileHandler fh) {
super(looper);
mSimFH = fh;
mIconsCache = new HashMap<Integer, Bitmap>(50);
}
- static IconLoader getInstance(Handler caller, SIMFileHandler fh) {
+ static IconLoader getInstance(Handler caller, IccFileHandler fh) {
if (sLoader != null) {
return sLoader;
}
if (fh != null) {
- HandlerThread thread = new HandlerThread("Stk Icon Loader");
+ HandlerThread thread = new HandlerThread("Cat Icon Loader");
thread.start();
return new IconLoader(thread.getLooper(), fh);
}
@@ -163,7 +163,7 @@
break;
}
} catch (Exception e) {
- StkLog.d(this, "Icon load failed");
+ CatLog.d(this, "Icon load failed");
// post null icon back to the caller.
postIcon();
}
@@ -254,7 +254,7 @@
}
if (pixelIndex != numOfPixels) {
- StkLog.d("IconLoader", "parseToBnW; size error");
+ CatLog.d("IconLoader", "parseToBnW; size error");
}
return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ImageDescriptor.java b/telephony/java/com/android/internal/telephony/cat/ImageDescriptor.java
similarity index 95%
rename from telephony/java/com/android/internal/telephony/gsm/stk/ImageDescriptor.java
rename to telephony/java/com/android/internal/telephony/cat/ImageDescriptor.java
index 880b9e5..711d977 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/ImageDescriptor.java
+++ b/telephony/java/com/android/internal/telephony/cat/ImageDescriptor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
/**
* {@hide}
@@ -69,7 +69,7 @@
d.length = ((rawData[valueIndex++] & 0xff) << 8 | (rawData[valueIndex++] & 0xff));
} catch (IndexOutOfBoundsException e) {
- StkLog.d("ImageDescripter", "parse; failed parsing image descriptor");
+ CatLog.d("ImageDescripter", "parse; failed parsing image descriptor");
d = null;
}
return d;
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Input.java b/telephony/java/com/android/internal/telephony/cat/Input.java
similarity index 91%
rename from telephony/java/com/android/internal/telephony/gsm/stk/Input.java
rename to telephony/java/com/android/internal/telephony/cat/Input.java
index 19f724b..13a5ad4 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/Input.java
+++ b/telephony/java/com/android/internal/telephony/cat/Input.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
/**
- * Container class for STK GET INPUT, GET IN KEY commands parameters.
+ * Container class for CAT GET INPUT, GET IN KEY commands parameters.
*
*/
public class Input implements Parcelable {
@@ -36,6 +36,7 @@
public boolean echo;
public boolean yesNo;
public boolean helpAvailable;
+ public Duration duration;
Input() {
text = "";
@@ -49,6 +50,7 @@
echo = false;
yesNo = false;
helpAvailable = false;
+ duration = null;
}
private Input(Parcel in) {
@@ -63,6 +65,7 @@
echo = in.readInt() == 1 ? true : false;
yesNo = in.readInt() == 1 ? true : false;
helpAvailable = in.readInt() == 1 ? true : false;
+ duration = in.readParcelable(null);
}
public int describeContents() {
@@ -81,6 +84,7 @@
dest.writeInt(echo ? 1 : 0);
dest.writeInt(yesNo ? 1 : 0);
dest.writeInt(helpAvailable ? 1 : 0);
+ dest.writeParcelable(duration, 0);
}
public static final Parcelable.Creator<Input> CREATOR = new Parcelable.Creator<Input>() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Item.java b/telephony/java/com/android/internal/telephony/cat/Item.java
similarity index 97%
rename from telephony/java/com/android/internal/telephony/gsm/stk/Item.java
rename to telephony/java/com/android/internal/telephony/cat/Item.java
index b2f338c..d4702bb 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/Item.java
+++ b/telephony/java/com/android/internal/telephony/cat/Item.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.graphics.Bitmap;
import android.os.Parcel;
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/LaunchBrowserMode.java b/telephony/java/com/android/internal/telephony/cat/LaunchBrowserMode.java
similarity index 95%
rename from telephony/java/com/android/internal/telephony/gsm/stk/LaunchBrowserMode.java
rename to telephony/java/com/android/internal/telephony/cat/LaunchBrowserMode.java
index 302273c..af043d1 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/LaunchBrowserMode.java
+++ b/telephony/java/com/android/internal/telephony/cat/LaunchBrowserMode.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Menu.java b/telephony/java/com/android/internal/telephony/cat/Menu.java
similarity index 96%
rename from telephony/java/com/android/internal/telephony/gsm/stk/Menu.java
rename to telephony/java/com/android/internal/telephony/cat/Menu.java
index 331f69d..7bbae01 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/Menu.java
+++ b/telephony/java/com/android/internal/telephony/cat/Menu.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.graphics.Bitmap;
import android.os.Parcel;
@@ -24,7 +24,7 @@
import java.util.List;
/**
- * Container class for STK menu (SET UP MENU, SELECT ITEM) parameters.
+ * Container class for CAT menu (SET UP MENU, SELECT ITEM) parameters.
*
*/
public class Menu implements Parcelable {
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/PresentationType.java b/telephony/java/com/android/internal/telephony/cat/PresentationType.java
similarity index 94%
rename from telephony/java/com/android/internal/telephony/gsm/stk/PresentationType.java
rename to telephony/java/com/android/internal/telephony/cat/PresentationType.java
index 71bdcdc..7c8cd8c 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/PresentationType.java
+++ b/telephony/java/com/android/internal/telephony/cat/PresentationType.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
similarity index 90%
rename from telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java
rename to telephony/java/com/android/internal/telephony/cat/ResponseData.java
index afd1bba..677d66b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java
+++ b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
@@ -14,7 +14,7 @@
* the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import com.android.internal.telephony.EncodeException;
import com.android.internal.telephony.GsmAlphabet;
@@ -28,6 +28,16 @@
* the ByteArrayOutputStream object.
*/
public abstract void format(ByteArrayOutputStream buf);
+
+ public static void writeLength(ByteArrayOutputStream buf, int length) {
+ // As per ETSI 102.220 Sec7.1.2, if the total length is greater
+ // than 0x7F, it should be coded in two bytes and the first byte
+ // should be 0x81.
+ if (length > 0x7F) {
+ buf.write(0x81);
+ }
+ buf.write(length);
+ }
}
class SelectItemResponseData extends ResponseData {
@@ -120,7 +130,7 @@
}
// length - one more for data coding scheme.
- buf.write(data.length + 1);
+ writeLength(buf, data.length + 1);
// data coding scheme
if (mIsUcs2) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ResultCode.java b/telephony/java/com/android/internal/telephony/cat/ResultCode.java
similarity index 98%
rename from telephony/java/com/android/internal/telephony/gsm/stk/ResultCode.java
rename to telephony/java/com/android/internal/telephony/cat/ResultCode.java
index b96a524..8544175 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/ResultCode.java
+++ b/telephony/java/com/android/internal/telephony/cat/ResultCode.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ResultException.java b/telephony/java/com/android/internal/telephony/cat/ResultException.java
similarity index 95%
rename from telephony/java/com/android/internal/telephony/gsm/stk/ResultException.java
rename to telephony/java/com/android/internal/telephony/cat/ResultException.java
index 2eb16c9..1c2cb63 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/ResultException.java
+++ b/telephony/java/com/android/internal/telephony/cat/ResultException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
/**
@@ -22,7 +22,7 @@
*
* {@hide}
*/
-public class ResultException extends StkException {
+public class ResultException extends CatException {
private ResultCode mResult;
private int mAdditionalInfo;
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/RilMessageDecoder.java b/telephony/java/com/android/internal/telephony/cat/RilMessageDecoder.java
similarity index 87%
rename from telephony/java/com/android/internal/telephony/gsm/stk/RilMessageDecoder.java
rename to telephony/java/com/android/internal/telephony/cat/RilMessageDecoder.java
index a82177c..a197c9a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/RilMessageDecoder.java
+++ b/telephony/java/com/android/internal/telephony/cat/RilMessageDecoder.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
-import com.android.internal.telephony.gsm.SIMFileHandler;
+import com.android.internal.telephony.IccFileHandler;
import com.android.internal.telephony.IccUtils;
import android.os.Handler;
@@ -26,7 +26,7 @@
/**
* Class used for queuing raw ril messages, decoding them into CommanParams
- * objects and sending the result back to the STK Service.
+ * objects and sending the result back to the CAT Service.
*/
class RilMessageDecoder extends HierarchicalStateMachine {
@@ -51,7 +51,7 @@
* @param fh
* @return RilMesssageDecoder
*/
- public static synchronized RilMessageDecoder getInstance(Handler caller, SIMFileHandler fh) {
+ public static synchronized RilMessageDecoder getInstance(Handler caller, IccFileHandler fh) {
if (sInstance == null) {
sInstance = new RilMessageDecoder(caller, fh);
sInstance.start();
@@ -85,12 +85,12 @@
}
private void sendCmdForExecution(RilMessage rilMsg) {
- Message msg = mCaller.obtainMessage(StkService.MSG_ID_RIL_MSG_DECODED,
+ Message msg = mCaller.obtainMessage(CatService.MSG_ID_RIL_MSG_DECODED,
new RilMessage(rilMsg));
msg.sendToTarget();
}
- private RilMessageDecoder(Handler caller, SIMFileHandler fh) {
+ private RilMessageDecoder(Handler caller, IccFileHandler fh) {
super("RilMessageDecoder");
addState(mStateStart);
@@ -108,7 +108,7 @@
transitionTo(mStateCmdParamsReady);
}
} else {
- StkLog.d(this, "StateStart unexpected expecting START=" +
+ CatLog.d(this, "StateStart unexpected expecting START=" +
CMD_START + " got " + msg.what);
}
return true;
@@ -123,7 +123,7 @@
sendCmdForExecution(mCurrentRilMessage);
transitionTo(mStateStart);
} else {
- StkLog.d(this, "StateCmdParamsReady expecting CMD_PARAMS_READY="
+ CatLog.d(this, "StateCmdParamsReady expecting CMD_PARAMS_READY="
+ CMD_PARAMS_READY + " got " + msg.what);
deferMessage(msg);
}
@@ -136,21 +136,21 @@
mCurrentRilMessage = rilMsg;
switch(rilMsg.mId) {
- case StkService.MSG_ID_SESSION_END:
- case StkService.MSG_ID_CALL_SETUP:
+ case CatService.MSG_ID_SESSION_END:
+ case CatService.MSG_ID_CALL_SETUP:
mCurrentRilMessage.mResCode = ResultCode.OK;
sendCmdForExecution(mCurrentRilMessage);
decodingStarted = false;
break;
- case StkService.MSG_ID_PROACTIVE_COMMAND:
- case StkService.MSG_ID_EVENT_NOTIFY:
- case StkService.MSG_ID_REFRESH:
+ case CatService.MSG_ID_PROACTIVE_COMMAND:
+ case CatService.MSG_ID_EVENT_NOTIFY:
+ case CatService.MSG_ID_REFRESH:
byte[] rawData = null;
try {
rawData = IccUtils.hexStringToBytes((String) rilMsg.mData);
} catch (Exception e) {
// zombie messages are dropped
- StkLog.d(this, "decodeMessageParams dropping zombie messages");
+ CatLog.d(this, "decodeMessageParams dropping zombie messages");
decodingStarted = false;
break;
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/TextAlignment.java b/telephony/java/com/android/internal/telephony/cat/TextAlignment.java
similarity index 96%
rename from telephony/java/com/android/internal/telephony/gsm/stk/TextAlignment.java
rename to telephony/java/com/android/internal/telephony/cat/TextAlignment.java
index c5dd50e..7fb58a5 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/TextAlignment.java
+++ b/telephony/java/com/android/internal/telephony/cat/TextAlignment.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/TextAttribute.java b/telephony/java/com/android/internal/telephony/cat/TextAttribute.java
similarity index 96%
rename from telephony/java/com/android/internal/telephony/gsm/stk/TextAttribute.java
rename to telephony/java/com/android/internal/telephony/cat/TextAttribute.java
index ace4300..0dea640 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/TextAttribute.java
+++ b/telephony/java/com/android/internal/telephony/cat/TextAttribute.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/TextColor.java b/telephony/java/com/android/internal/telephony/cat/TextColor.java
similarity index 96%
rename from telephony/java/com/android/internal/telephony/gsm/stk/TextColor.java
rename to telephony/java/com/android/internal/telephony/cat/TextColor.java
index 126fc62..6447e74 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/TextColor.java
+++ b/telephony/java/com/android/internal/telephony/cat/TextColor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/TextMessage.java b/telephony/java/com/android/internal/telephony/cat/TextMessage.java
similarity index 97%
rename from telephony/java/com/android/internal/telephony/gsm/stk/TextMessage.java
rename to telephony/java/com/android/internal/telephony/cat/TextMessage.java
index 3b6a09a..5ffd076 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/TextMessage.java
+++ b/telephony/java/com/android/internal/telephony/cat/TextMessage.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.graphics.Bitmap;
import android.os.Parcel;
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Tone.java b/telephony/java/com/android/internal/telephony/cat/Tone.java
similarity index 98%
rename from telephony/java/com/android/internal/telephony/gsm/stk/Tone.java
rename to telephony/java/com/android/internal/telephony/cat/Tone.java
index b64e777d..27b4489 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/Tone.java
+++ b/telephony/java/com/android/internal/telephony/cat/Tone.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ToneSettings.java b/telephony/java/com/android/internal/telephony/cat/ToneSettings.java
similarity index 97%
rename from telephony/java/com/android/internal/telephony/gsm/stk/ToneSettings.java
rename to telephony/java/com/android/internal/telephony/cat/ToneSettings.java
index 90cc6c1..6375afb 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/ToneSettings.java
+++ b/telephony/java/com/android/internal/telephony/cat/ToneSettings.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java b/telephony/java/com/android/internal/telephony/cat/ValueParser.java
similarity index 98%
rename from telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java
rename to telephony/java/com/android/internal/telephony/cat/ValueParser.java
index 09a860e..34e4811 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java
+++ b/telephony/java/com/android/internal/telephony/cat/ValueParser.java
@@ -14,11 +14,11 @@
* the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cat;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.IccUtils;
-import com.android.internal.telephony.gsm.stk.Duration.TimeUnit;
+import com.android.internal.telephony.cat.Duration.TimeUnit;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
@@ -182,7 +182,7 @@
*/
static ItemsIconId retrieveItemsIconId(ComprehensionTlv ctlv)
throws ResultException {
- StkLog.d("ValueParser", "retrieveItemsIconId:");
+ CatLog.d("ValueParser", "retrieveItemsIconId:");
ItemsIconId id = new ItemsIconId();
byte[] rawValue = ctlv.getRawValue();
diff --git a/telephony/java/com/android/internal/telephony/cat/package.html b/telephony/java/com/android/internal/telephony/cat/package.html
new file mode 100644
index 0000000..5b6bfc6
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/cat/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+Provides classes for ICC Toolkit Service (CAT).
+</BODY>
+</HTML>
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index c11dd0b..3890a98 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -42,6 +42,7 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.telephony.cat.CatService;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CommandException;
@@ -108,7 +109,7 @@
PhoneSubInfo mSubInfo;
EriManager mEriManager;
WakeLock mWakeLock;
-
+ CatService mCcatService;
// mNvLoadedRegistrants are informed after the EVENT_NV_READY
private RegistrantList mNvLoadedRegistrants = new RegistrantList();
@@ -160,6 +161,8 @@
mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this, mSMS);
mSubInfo = new PhoneSubInfo(this);
mEriManager = new EriManager(this, context, EriManager.ERI_FROM_XML);
+ mCcatService = CatService.getInstance(mCM, mRuimRecords, mContext,
+ mIccFileHandler, mRuimCard);
mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
mRuimRecords.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null);
@@ -220,6 +223,7 @@
mCM.unregisterForNVReady(this); //EVENT_NV_READY
mSST.unregisterForNetworkAttach(this); //EVENT_REGISTERED_TO_NETWORK
mCM.unSetOnSuppServiceNotification(this);
+ removeCallbacks(mExitEcmRunnable);
mPendingMmis.clear();
@@ -235,6 +239,7 @@
mRuimSmsInterfaceManager.dispose();
mSubInfo.dispose();
mEriManager.dispose();
+ mCcatService.dispose();
}
}
@@ -250,6 +255,8 @@
this.mCT = null;
this.mSST = null;
this.mEriManager = null;
+ this.mCcatService = null;
+ this.mExitEcmRunnable = null;
}
protected void finalize() {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index dceff2a..d6fc134 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -483,6 +483,11 @@
mCm.setCdmaBroadcastConfig(configValuesArray, response);
}
+ protected void handleBroadcastSms(AsyncResult ar) {
+ // Not supported
+ Log.e(TAG, "Error! Not implemented for CDMA.");
+ }
+
private int resultToCause(int rc) {
switch (rc) {
case Activity.RESULT_OK:
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
old mode 100644
new mode 100755
index 2cad6cc..5b6bc1f
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -552,32 +552,53 @@
}
@Override
- protected void powerOffRadioSafely(){
- // clean data connection
+ protected void powerOffRadioSafely() {
DataConnectionTracker dcTracker = phone.mDataConnection;
Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION);
- msg.arg1 = 1; // tearDown is true
msg.obj = CDMAPhone.REASON_RADIO_TURNED_OFF;
- dcTracker.sendMessage(msg);
- synchronized(this) {
- if (!mPendingRadioPowerOffAfterDataOff) {
- DataConnectionTracker.State currentState = dcTracker.getState();
- if (currentState != DataConnectionTracker.State.CONNECTED
- && currentState != DataConnectionTracker.State.DISCONNECTING
- && currentState != DataConnectionTracker.State.INITING) {
- if (DBG) log("Data disconnected, turn off radio right away.");
- hangupAndPowerOff();
- }
- else if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
- if (DBG) {
- log("Wait up to 30 sec for data to disconnect, then turn off radio.");
+ synchronized (this) {
+ if (networkType == ServiceState.RADIO_TECHNOLOGY_1xRTT) {
+ /*
+ * In 1x CDMA , during radio power off modem will disconnect the
+ * data call and sends the power down registration message along
+ * with the data call release message to the network
+ */
+
+ msg.arg1 = 0; // tearDown is false since modem does it anyway for 1X
+ dcTracker.sendMessage(msg);
+
+ Log.w(LOG_TAG, "Turn off the radio right away");
+ hangupAndPowerOff();
+ } else {
+ if (!mPendingRadioPowerOffAfterDataOff) {
+ DataConnectionTracker.State currentState = dcTracker.getState();
+ if (currentState != DataConnectionTracker.State.CONNECTED
+ && currentState != DataConnectionTracker.State.DISCONNECTING
+ && currentState != DataConnectionTracker.State.INITING) {
+
+ msg.arg1 = 0; // tearDown is false as it is not needed.
+ dcTracker.sendMessage(msg);
+
+ if (DBG)
+ log("Data disconnected, turn off radio right away.");
+ hangupAndPowerOff();
+ } else {
+ // clean data connection
+ msg.arg1 = 1; // tearDown is true
+ dcTracker.sendMessage(msg);
+
+ if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
+ if (DBG) {
+ log("Wait upto 30s for data to disconnect, then turn off radio.");
+ }
+ mPendingRadioPowerOffAfterDataOff = true;
+ } else {
+ Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
+ hangupAndPowerOff();
+ }
}
- mPendingRadioPowerOffAfterDataOff = true;
- } else {
- Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
- hangupAndPowerOff();
}
}
}
@@ -653,8 +674,7 @@
return;
}
- if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW &&
- err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
+ if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
Log.e(LOG_TAG,
"RIL implementation has returned an error where it must succeed",
ar.exception);
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
index e97549d..f4a6d11 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
@@ -192,6 +192,18 @@
return mSms;
}
+ public boolean enableCellBroadcast(int messageIdentifier) {
+ // Not implemented
+ Log.e(LOG_TAG, "Error! Not implemented for CDMA.");
+ return false;
+ }
+
+ public boolean disableCellBroadcast(int messageIdentifier) {
+ // Not implemented
+ Log.e(LOG_TAG, "Error! Not implemented for CDMA.");
+ return false;
+ }
+
protected void log(String msg) {
Log.d(LOG_TAG, "[RuimSmsInterfaceManager] " + msg);
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
old mode 100755
new mode 100644
index 6f024ed..54cf612
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -29,8 +29,10 @@
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.cdma.sms.BearerData;
import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
+import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.util.BitwiseInputStream;
import com.android.internal.util.HexDump;
import java.io.BufferedInputStream;
@@ -71,6 +73,16 @@
static final String LOG_TAG = "CDMA";
static private final String LOGGABLE_TAG = "CDMA:SMS";
+ private final static byte TELESERVICE_IDENTIFIER = 0x00;
+ private final static byte SERVICE_CATEGORY = 0x01;
+ private final static byte ORIGINATING_ADDRESS = 0x02;
+ private final static byte ORIGINATING_SUB_ADDRESS = 0x03;
+ private final static byte DESTINATION_ADDRESS = 0x04;
+ private final static byte DESTINATION_SUB_ADDRESS = 0x05;
+ private final static byte BEARER_REPLY_OPTION = 0x06;
+ private final static byte CAUSE_CODES = 0x07;
+ private final static byte BEARER_DATA = 0x08;
+
/**
* Status of a previously submitted SMS.
* This field applies to SMS Delivery Acknowledge messages. 0 indicates success;
@@ -138,6 +150,7 @@
SmsMessage msg = new SmsMessage();
SmsEnvelope env = new SmsEnvelope();
CdmaSmsAddress addr = new CdmaSmsAddress();
+ CdmaSmsSubaddress subaddr = new CdmaSmsSubaddress();
byte[] data;
byte count;
int countInt;
@@ -180,15 +193,24 @@
addr.origBytes = data;
- // ignore subaddress
- p.readInt(); //p_cur->sSubAddress.subaddressType
- p.readInt(); //p_cur->sSubAddress.odd
- count = p.readByte(); //p_cur->sSubAddress.number_of_digits
- //p_cur->sSubAddress.digits[digitCount] :
- for (int index=0; index < count; index++) {
- p.readByte();
+ subaddr.type = p.readInt(); // p_cur->sSubAddress.subaddressType
+ subaddr.odd = p.readByte(); // p_cur->sSubAddress.odd
+ count = p.readByte(); // p_cur->sSubAddress.number_of_digits
+
+ if (count < 0) {
+ count = 0;
}
+ // p_cur->sSubAddress.digits[digitCount] :
+
+ data = new byte[count];
+
+ for (int index = 0; index < count; ++index) {
+ data[index] = p.readByte();
+ }
+
+ subaddr.origBytes = data;
+
/* currently not supported by the modem-lib:
env.bearerReply
env.replySeqNo
@@ -210,6 +232,7 @@
// link the the filled objects to the SMS
env.origAddress = addr;
+ env.origSubaddress = subaddr;
msg.originatingAddress = addr;
msg.mEnvelope = env;
@@ -255,6 +278,7 @@
System.arraycopy(data, 2, pdu, 0, size);
// the message has to be parsed before it can be displayed
// see gsm.SmsMessage
+ msg.parsePduFromEfRecord(pdu);
return msg;
} catch (RuntimeException ex) {
Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
@@ -527,6 +551,143 @@
}
/**
+ * Decodes 3GPP2 sms stored in CSIM/RUIM cards As per 3GPP2 C.S0015-0
+ */
+ private void parsePduFromEfRecord(byte[] pdu) {
+ ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
+ DataInputStream dis = new DataInputStream(bais);
+ SmsEnvelope env = new SmsEnvelope();
+ CdmaSmsAddress addr = new CdmaSmsAddress();
+ CdmaSmsSubaddress subAddr = new CdmaSmsSubaddress();
+
+ try {
+ env.messageType = dis.readByte();
+
+ while (dis.available() > 0) {
+ int parameterId = dis.readByte();
+ int parameterLen = dis.readByte();
+ byte[] parameterData = new byte[parameterLen];
+
+ switch (parameterId) {
+ case TELESERVICE_IDENTIFIER:
+ /*
+ * 16 bit parameter that identifies which upper layer
+ * service access point is sending or should receive
+ * this message
+ */
+ env.teleService = dis.readUnsignedShort();
+ Log.i(LOG_TAG, "teleservice = " + env.teleService);
+ break;
+ case SERVICE_CATEGORY:
+ /*
+ * 16 bit parameter that identifies type of service as
+ * in 3GPP2 C.S0015-0 Table 3.4.3.2-1
+ */
+ env.serviceCategory = dis.readUnsignedShort();
+ break;
+ case ORIGINATING_ADDRESS:
+ case DESTINATION_ADDRESS:
+ dis.read(parameterData, 0, parameterLen);
+ BitwiseInputStream addrBis = new BitwiseInputStream(parameterData);
+ addr.digitMode = addrBis.read(1);
+ addr.numberMode = addrBis.read(1);
+ int numberType = 0;
+ if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
+ numberType = addrBis.read(3);
+ addr.ton = numberType;
+
+ if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK)
+ addr.numberPlan = addrBis.read(4);
+ }
+
+ addr.numberOfDigits = addrBis.read(8);
+
+ byte[] data = new byte[addr.numberOfDigits];
+ byte b = 0x00;
+
+ if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF) {
+ /* As per 3GPP2 C.S0005-0 Table 2.7.1.3.2.4-4 */
+ for (int index = 0; index < addr.numberOfDigits; index++) {
+ b = (byte) (0xF & addrBis.read(4));
+ // convert the value if it is 4-bit DTMF to 8
+ // bit
+ data[index] = convertDtmfToAscii(b);
+ }
+ } else if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
+ if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK) {
+ for (int index = 0; index < addr.numberOfDigits; index++) {
+ b = (byte) (0xFF & addrBis.read(8));
+ data[index] = b;
+ }
+
+ } else if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK) {
+ if (numberType == 2)
+ Log.e(LOG_TAG, "TODO: Originating Addr is email id");
+ else
+ Log.e(LOG_TAG,
+ "TODO: Originating Addr is data network address");
+ } else {
+ Log.e(LOG_TAG, "Originating Addr is of incorrect type");
+ }
+ } else {
+ Log.e(LOG_TAG, "Incorrect Digit mode");
+ }
+ addr.origBytes = data;
+ Log.i(LOG_TAG, "Originating Addr=" + addr.toString());
+ break;
+ case ORIGINATING_SUB_ADDRESS:
+ case DESTINATION_SUB_ADDRESS:
+ dis.read(parameterData, 0, parameterLen);
+ BitwiseInputStream subAddrBis = new BitwiseInputStream(parameterData);
+ subAddr.type = subAddrBis.read(3);
+ subAddr.odd = subAddrBis.readByteArray(1)[0];
+ int subAddrLen = subAddrBis.read(8);
+ byte[] subdata = new byte[subAddrLen];
+ for (int index = 0; index < subAddrLen; index++) {
+ b = (byte) (0xFF & subAddrBis.read(4));
+ // convert the value if it is 4-bit DTMF to 8 bit
+ subdata[index] = convertDtmfToAscii(b);
+ }
+ subAddr.origBytes = subdata;
+ break;
+ case BEARER_REPLY_OPTION:
+ dis.read(parameterData, 0, parameterLen);
+ BitwiseInputStream replyOptBis = new BitwiseInputStream(parameterData);
+ env.bearerReply = replyOptBis.read(6);
+ break;
+ case CAUSE_CODES:
+ dis.read(parameterData, 0, parameterLen);
+ BitwiseInputStream ccBis = new BitwiseInputStream(parameterData);
+ env.replySeqNo = ccBis.readByteArray(6)[0];
+ env.errorClass = ccBis.readByteArray(2)[0];
+ if (env.errorClass != 0x00)
+ env.causeCode = ccBis.readByteArray(8)[0];
+ break;
+ case BEARER_DATA:
+ dis.read(parameterData, 0, parameterLen);
+ env.bearerData = parameterData;
+ break;
+ default:
+ throw new Exception("unsupported parameterId (" + parameterId + ")");
+ }
+ }
+ bais.close();
+ dis.close();
+ } catch (Exception ex) {
+ Log.e(LOG_TAG, "parsePduFromEfRecord: conversion from pdu to SmsMessage failed" + ex);
+ }
+
+ // link the filled objects to this SMS
+ originatingAddress = addr;
+ env.origAddress = addr;
+ env.origSubaddress = subAddr;
+ mEnvelope = env;
+ mPdu = pdu;
+
+ parseSms();
+ }
+
+ /**
* Parses a SMS message from its BearerData stream. (mobile-terminated only)
*/
protected void parseSms() {
@@ -818,6 +979,8 @@
output.write(mEnvelope.teleService);
output.write(mEnvelope.origAddress.origBytes, 0, mEnvelope.origAddress.origBytes.length);
output.write(mEnvelope.bearerData, 0, mEnvelope.bearerData.length);
+ output.write(mEnvelope.origSubaddress.origBytes, 0,
+ mEnvelope.origSubaddress.origBytes.length);
return output.toByteArray();
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 7bc48ca..8a4e0ce 100755
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -588,7 +588,6 @@
uData.payload = new byte[0];
uData.numFields = 0;
} else {
- uData.payload = uData.payload;
uData.numFields = uData.payload.length;
}
} else {
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java
similarity index 64%
copy from telephony/java/com/android/internal/telephony/gsm/stk/StkException.java
copy to telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java
index 86de366..f9cebf5 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2010 The Android Open Source Project. All rights reserved.
+ * Copyright (C) 2010 Code Aurora Forum. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +15,13 @@
* limitations under the License.
*/
-package com.android.internal.telephony.gsm.stk;
+package com.android.internal.telephony.cdma.sms;
-import android.util.AndroidException;
+public class CdmaSmsSubaddress {
+ public int type;
+ public byte odd;
-/**
- * Base class for all the exceptions in STK service.
- *
- * {@hide}
- */
-class StkException extends AndroidException {
- public StkException() {
- super();
- }
+ public byte[] origBytes;
}
+
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
index 0dcacc1..4f00163 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
@@ -17,6 +17,8 @@
package com.android.internal.telephony.cdma.sms;
+import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress;
+
public final class SmsEnvelope{
/**
* Message Types
@@ -74,17 +76,23 @@
/**
* The origination address identifies the originator of the SMS message.
- * (See 3GPP2 C.S0015-B, v2, 3.4.3.4)
+ * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
*/
public CdmaSmsAddress origAddress;
/**
* The destination address identifies the target of the SMS message.
- * (See 3GPP2 C.S0015-B, v2, 3.4.3.4)
+ * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
*/
public CdmaSmsAddress destAddress;
/**
+ * The origination subaddress identifies the originator of the SMS message.
+ * (See 3GPP2 C.S0015-B, v2, 3.4.3.4)
+ */
+ public CdmaSmsSubaddress origSubaddress;
+
+ /**
* The 6-bit bearer reply parameter is used to request the return of a
* SMS Acknowledge Message.
* (See 3GPP2 C.S0015-B, v2, 3.4.3.5)
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 689a972..3959c67 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -49,6 +49,7 @@
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_BASEBAND_VERSION;
+import com.android.internal.telephony.cat.CatService;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallForwardInfo;
import com.android.internal.telephony.CallStateException;
@@ -68,7 +69,6 @@
import com.android.internal.telephony.PhoneSubInfo;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.UUSInfo;
-import com.android.internal.telephony.gsm.stk.StkService;
import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.IccVmNotSupportedException;
@@ -102,7 +102,7 @@
GsmSMSDispatcher mSMS;
SIMRecords mSIMRecords;
SimCard mSimCard;
- StkService mStkService;
+ CatService mStkService;
ArrayList <GsmMmiCode> mPendingMMIs = new ArrayList<GsmMmiCode>();
SimPhoneBookInterfaceManager mSimPhoneBookIntManager;
SimSmsInterfaceManager mSimSmsIntManager;
@@ -150,7 +150,7 @@
mSimSmsIntManager = new SimSmsInterfaceManager(this, mSMS);
mSubInfo = new PhoneSubInfo(this);
}
- mStkService = StkService.getInstance(mCM, mSIMRecords, mContext,
+ mStkService = CatService.getInstance(mCM, mSIMRecords, mContext,
(SIMFileHandler)mIccFileHandler, mSimCard);
mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
@@ -968,7 +968,9 @@
}
public void getCallWaiting(Message onComplete) {
- mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
+ //As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service
+ //class parameter in call waiting interrogation to network
+ mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_NONE, onComplete);
}
public void setCallWaiting(boolean enable, Message onComplete) {
@@ -1221,7 +1223,8 @@
// Check if this is a different SIM than the previous one. If so unset the
// voice mail number.
String imsi = getVmSimImsi();
- if (imsi != null && !getSubscriberId().equals(imsi)) {
+ String imsiFromSIM = getSubscriberId();
+ if (imsi != null && imsiFromSIM != null && !imsiFromSIM.equals(imsi)) {
storeVoiceMailNumber(null);
setVmSimImsi(null);
}
@@ -1480,4 +1483,7 @@
Log.e(LOG_TAG, "Error! This functionality is not implemented for GSM.");
}
+ public boolean isCspPlmnEnabled() {
+ return mSIMRecords.isCspPlmnEnabled();
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 5c7ce2f..2962e0f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -44,6 +44,13 @@
//***** Constants
+ // Max Size of the Short Code (aka Short String from TS 22.030 6.5.2)
+ static final int MAX_LENGTH_SHORT_CODE = 2;
+
+ // TS 22.030 6.5.2 Every Short String USSD command will end with #-key
+ // (known as #-String)
+ static final char END_OF_USSD_COMMAND = '#';
+
// From TS 22.030 6.5.2
static final String ACTION_ACTIVATE = "*";
static final String ACTION_DEACTIVATE = "#";
@@ -471,22 +478,69 @@
}
/**
- * Helper function for newFromDialString. Returns true if dialString appears to be a short code
- * AND conditions are correct for it to be treated as such.
+ * Helper function for newFromDialString. Returns true if dialString appears
+ * to be a short code AND conditions are correct for it to be treated as
+ * such.
*/
static private boolean isShortCode(String dialString, GSMPhone phone) {
// Refer to TS 22.030 Figure 3.5.3.2:
- // A 1 or 2 digit "short code" is treated as USSD if it is entered while on a call or
- // does not satisfy the condition (exactly 2 digits && starts with '1').
- return ((dialString != null && dialString.length() <= 2)
- && !PhoneNumberUtils.isEmergencyNumber(dialString)
- && (phone.isInCall()
- || !((dialString.length() == 2 && dialString.charAt(0) == '1')
- /* While contrary to TS 22.030, there is strong precedence
- * for treating "0" and "00" as call setup strings.
- */
- || dialString.equals("0")
- || dialString.equals("00"))));
+ if (dialString == null) {
+ return false;
+ }
+
+ // Illegal dial string characters will give a ZERO length.
+ // At this point we do not want to crash as any application with
+ // call privileges may send a non dial string.
+ // It return false as when the dialString is equal to NULL.
+ if (dialString.length() == 0) {
+ return false;
+ }
+
+ if (PhoneNumberUtils.isEmergencyNumber(dialString)) {
+ return false;
+ } else {
+ return isShortCodeUSSD(dialString, phone);
+ }
+ }
+
+ /**
+ * Helper function for isShortCode. Returns true if dialString appears to be
+ * a short code and it is a USSD structure
+ *
+ * According to the 3PGG TS 22.030 specification Figure 3.5.3.2: A 1 or 2
+ * digit "short code" is treated as USSD if it is entered while on a call or
+ * does not satisfy the condition (exactly 2 digits && starts with '1'), there
+ * are however exceptions to this rule (see below)
+ *
+ * Exception (1) to Call initiation is: If the user of the device is already in a call
+ * and enters a Short String without any #-key at the end and the length of the Short String is
+ * equal or less then the MAX_LENGTH_SHORT_CODE [constant that is equal to 2]
+ *
+ * The phone shall initiate a USSD/SS commands.
+ *
+ * Exception (2) to Call initiation is: If the user of the device enters one
+ * Digit followed by the #-key. This rule defines this String as the
+ * #-String which is a USSD/SS command.
+ *
+ * The phone shall initiate a USSD/SS command.
+ */
+ static private boolean isShortCodeUSSD(String dialString, GSMPhone phone) {
+ if (dialString != null) {
+ if (phone.isInCall()) {
+ // The maximum length of a Short Code (aka Short String) is 2
+ if (dialString.length() <= MAX_LENGTH_SHORT_CODE) {
+ return true;
+ }
+ }
+
+ // The maximum length of a Short Code (aka Short String) is 2
+ if (dialString.length() <= MAX_LENGTH_SHORT_CODE) {
+ if (dialString.charAt(dialString.length() - 1) == END_OF_USSD_COMMAND) {
+ return true;
+ }
+ }
+ }
+ return false;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index e7b6c37..8355046 100755
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -22,21 +22,27 @@
import android.content.Intent;
import android.os.AsyncResult;
import android.os.Message;
+import android.os.SystemProperties;
import android.provider.Telephony.Sms;
import android.provider.Telephony.Sms.Intents;
import android.telephony.ServiceState;
+import android.telephony.SmsCbMessage;
+import android.telephony.gsm.GsmCellLocation;
import android.util.Config;
import android.util.Log;
+import com.android.internal.telephony.BaseCommands;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
+import com.android.internal.telephony.TelephonyProperties;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
import static android.telephony.SmsMessage.MessageClass;
@@ -48,6 +54,8 @@
GsmSMSDispatcher(GSMPhone phone) {
super(phone);
mGsmPhone = phone;
+
+ ((BaseCommands)mCm).setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null);
}
/**
@@ -392,4 +400,162 @@
return CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR;
}
}
+
+ /**
+ * Holds all info about a message page needed to assemble a complete
+ * concatenated message
+ */
+ private static final class SmsCbConcatInfo {
+ private final SmsCbHeader mHeader;
+
+ private final String mPlmn;
+
+ private final int mLac;
+
+ private final int mCid;
+
+ public SmsCbConcatInfo(SmsCbHeader header, String plmn, int lac, int cid) {
+ mHeader = header;
+ mPlmn = plmn;
+ mLac = lac;
+ mCid = cid;
+ }
+
+ @Override
+ public int hashCode() {
+ return mHeader.messageIdentifier * 31 + mHeader.updateNumber;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof SmsCbConcatInfo) {
+ SmsCbConcatInfo other = (SmsCbConcatInfo)obj;
+
+ // Two pages match if all header attributes (except the page
+ // index) are identical, and both pages belong to the same
+ // location (which is also determined by the scope parameter)
+ if (mHeader.geographicalScope == other.mHeader.geographicalScope
+ && mHeader.messageCode == other.mHeader.messageCode
+ && mHeader.updateNumber == other.mHeader.updateNumber
+ && mHeader.messageIdentifier == other.mHeader.messageIdentifier
+ && mHeader.dataCodingScheme == other.mHeader.dataCodingScheme
+ && mHeader.nrOfPages == other.mHeader.nrOfPages) {
+ return matchesLocation(other.mPlmn, other.mLac, other.mCid);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if this concatenation info matches the given location. The
+ * granularity of the match depends on the geographical scope.
+ *
+ * @param plmn PLMN
+ * @param lac Location area code
+ * @param cid Cell ID
+ * @return true if matching, false otherwise
+ */
+ public boolean matchesLocation(String plmn, int lac, int cid) {
+ switch (mHeader.geographicalScope) {
+ case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE:
+ case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE:
+ if (mCid != cid) {
+ return false;
+ }
+ // deliberate fall-through
+ case SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE:
+ if (mLac != lac) {
+ return false;
+ }
+ // deliberate fall-through
+ case SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE:
+ return mPlmn != null && mPlmn.equals(plmn);
+ }
+
+ return false;
+ }
+ }
+
+ // This map holds incomplete concatenated messages waiting for assembly
+ private HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap =
+ new HashMap<SmsCbConcatInfo, byte[][]>();
+
+ protected void handleBroadcastSms(AsyncResult ar) {
+ try {
+ byte[][] pdus = null;
+ byte[] receivedPdu = (byte[])ar.result;
+
+ if (Config.LOGD) {
+ for (int i = 0; i < receivedPdu.length; i += 8) {
+ StringBuilder sb = new StringBuilder("SMS CB pdu data: ");
+ for (int j = i; j < i + 8 && j < receivedPdu.length; j++) {
+ int b = receivedPdu[j] & 0xff;
+ if (b < 0x10) {
+ sb.append("0");
+ }
+ sb.append(Integer.toHexString(b)).append(" ");
+ }
+ Log.d(TAG, sb.toString());
+ }
+ }
+
+ SmsCbHeader header = new SmsCbHeader(receivedPdu);
+ String plmn = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC);
+ GsmCellLocation cellLocation = (GsmCellLocation)mGsmPhone.getCellLocation();
+ int lac = cellLocation.getLac();
+ int cid = cellLocation.getCid();
+
+ if (header.nrOfPages > 1) {
+ // Multi-page message
+ SmsCbConcatInfo concatInfo = new SmsCbConcatInfo(header, plmn, lac, cid);
+
+ // Try to find other pages of the same message
+ pdus = mSmsCbPageMap.get(concatInfo);
+
+ if (pdus == null) {
+ // This it the first page of this message, make room for all
+ // pages and keep until complete
+ pdus = new byte[header.nrOfPages][];
+
+ mSmsCbPageMap.put(concatInfo, pdus);
+ }
+
+ // Page parameter is one-based
+ pdus[header.pageIndex - 1] = receivedPdu;
+
+ for (int i = 0; i < pdus.length; i++) {
+ if (pdus[i] == null) {
+ // Still missing pages, exit
+ return;
+ }
+ }
+
+ // Message complete, remove and dispatch
+ mSmsCbPageMap.remove(concatInfo);
+ } else {
+ // Single page message
+ pdus = new byte[1][];
+ pdus[0] = receivedPdu;
+ }
+
+ dispatchBroadcastPdus(pdus);
+
+ // Remove messages that are out of scope to prevent the map from
+ // growing indefinitely, containing incomplete messages that were
+ // never assembled
+ Iterator<SmsCbConcatInfo> iter = mSmsCbPageMap.keySet().iterator();
+
+ while (iter.hasNext()) {
+ SmsCbConcatInfo info = iter.next();
+
+ if (!info.matchesLocation(plmn, lac, cid)) {
+ iter.remove();
+ }
+ }
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Error in decoding SMS CB pdu", e);
+ }
+ }
+
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 6ddb312..18ef121 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -643,8 +643,7 @@
return;
}
- if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW &&
- err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
+ if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
Log.e(LOG_TAG,
"RIL implementation has returned an error where it must succeed" +
ar.exception);
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java b/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java
index 206e62f..e8d10f9 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java
@@ -81,6 +81,7 @@
case EF_SPN_CPHS:
case EF_SPN_SHORT_CPHS:
case EF_INFO_CPHS:
+ case EF_CSP_CPHS:
return MF_SIM + DF_GSM;
case EF_PBR:
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index e214061..3b133da 100755
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -73,6 +73,7 @@
* mCphsInfo[1] and mCphsInfo[2] is CPHS Service Table
*/
private byte[] mCphsInfo = null;
+ boolean mCspPlmnEnabled = true;
byte[] efMWIS = null;
byte[] efCPHS_MWI =null;
@@ -93,6 +94,7 @@
static final int SPN_RULE_SHOW_PLMN = 0x02;
// From TS 51.011 EF[SPDI] section
+ static final int TAG_SPDI = 0xA3;
static final int TAG_SPDI_PLMN_LIST = 0x80;
// Full Name IEI from TS 24.008
@@ -140,6 +142,7 @@
private static final int EVENT_SET_MSISDN_DONE = 30;
private static final int EVENT_SIM_REFRESH = 31;
private static final int EVENT_GET_CFIS_DONE = 32;
+ private static final int EVENT_GET_CSP_CPHS_DONE = 33;
// Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
@@ -589,6 +592,13 @@
break;
case EVENT_GET_CPHS_MAILBOX_DONE:
case EVENT_GET_MBDN_DONE:
+ //Resetting the voice mail number and voice mail tag to null
+ //as these should be updated from the data read from EF_MBDN.
+ //If they are not reset, incase of invalid data/exception these
+ //variables are retaining their previous values and are
+ //causing invalid voice mailbox info display to user.
+ voiceMailNum = null;
+ voiceMailTag = null;
isRecordLoadResponse = true;
ar = (AsyncResult)msg.obj;
@@ -1035,6 +1045,22 @@
((GSMPhone) phone).notifyCallForwardingIndicator();
break;
+ case EVENT_GET_CSP_CPHS_DONE:
+ isRecordLoadResponse = true;
+
+ ar = (AsyncResult)msg.obj;
+
+ if (ar.exception != null) {
+ Log.e(LOG_TAG,"Exception in fetching EF_CSP data " + ar.exception);
+ break;
+ }
+
+ data = (byte[])ar.result;
+
+ Log.i(LOG_TAG,"EF_CSP: " + IccUtils.bytesToHexString(data));
+ handleEfCspData(data);
+ break;
+
}}catch (RuntimeException exc) {
// I don't want these exceptions to be fatal
Log.w(LOG_TAG, "Exception parsing SIM record", exc);
@@ -1058,6 +1084,12 @@
new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1,
1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
break;
+ case EF_CSP_CPHS:
+ recordsToLoad++;
+ Log.i(LOG_TAG, "[CSP] SIM Refresh for EF_CSP_CPHS");
+ phone.getIccFileHandler().loadEFTransparent(EF_CSP_CPHS,
+ obtainMessage(EVENT_GET_CSP_CPHS_DONE));
+ break;
default:
// For now, fetch all records if this is not a
// voicemail number.
@@ -1288,6 +1320,9 @@
iccFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));
recordsToLoad++;
+ iccFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE));
+ recordsToLoad++;
+
// XXX should seek instead of examining them all
if (false) { // XXX
iccFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
@@ -1467,8 +1502,12 @@
byte[] plmnEntries = null;
- // There should only be one TAG_SPDI_PLMN_LIST
for ( ; tlv.isValidObject() ; tlv.nextObject()) {
+ // Skip SPDI tag, if existant
+ if (tlv.getTag() == TAG_SPDI) {
+ tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length);
+ }
+ // There should only be one TAG_SPDI_PLMN_LIST
if (tlv.getTag() == TAG_SPDI_PLMN_LIST) {
plmnEntries = tlv.getData();
break;
@@ -1505,4 +1544,53 @@
Log.d(LOG_TAG, "[SIMRecords] " + s);
}
+ /**
+ * Return true if "Restriction of menu options for manual PLMN selection"
+ * bit is set or EF_CSP data is unavailable, return false otherwise.
+ */
+ public boolean isCspPlmnEnabled() {
+ return mCspPlmnEnabled;
+ }
+
+ /**
+ * Parse EF_CSP data and check if
+ * "Restriction of menu options for manual PLMN selection" is
+ * Enabled/Disabled
+ *
+ * @param data EF_CSP hex data.
+ */
+ private void handleEfCspData(byte[] data) {
+ // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined
+ // 18 bytes (i.e 9 service groups info) and additional data specific to
+ // operator. The valueAddedServicesGroup is not part of standard
+ // services. This is operator specific and can be programmed any where.
+ // Normally this is programmed as 10th service after the standard
+ // services.
+ int usedCspGroups = data.length / 2;
+ // This is the "Servive Group Number" of "Value Added Services Group".
+ byte valueAddedServicesGroup = (byte)0xC0;
+
+ mCspPlmnEnabled = true;
+ for (int i = 0; i < usedCspGroups; i++) {
+ if (data[2 * i] == valueAddedServicesGroup) {
+ Log.i(LOG_TAG, "[CSP] found ValueAddedServicesGroup, value "
+ + data[(2 * i) + 1]);
+ if ((data[(2 * i) + 1] & 0x80) == 0x80) {
+ // Bit 8 is for
+ // "Restriction of menu options for manual PLMN selection".
+ // Operator Selection menu should be enabled.
+ mCspPlmnEnabled = true;
+ } else {
+ mCspPlmnEnabled = false;
+ // Operator Selection menu should be disabled.
+ // Operator Selection Mode should be set to Automatic.
+ Log.i(LOG_TAG,"[CSP] Set Automatic Network Selection");
+ phone.setNetworkSelectionModeAutomatic(null);
+ }
+ return;
+ }
+ }
+
+ Log.w(LOG_TAG, "[CSP] Value Added Service Group (0xC0), not found!");
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
index 67ecc77..e55596b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
@@ -17,7 +17,9 @@
package com.android.internal.telephony.gsm;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.AsyncResult;
+import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
@@ -30,7 +32,10 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
@@ -45,9 +50,15 @@
private final Object mLock = new Object();
private boolean mSuccess;
private List<SmsRawData> mSms;
+ private HashMap<Integer, HashSet<String>> mCellBroadcastSubscriptions =
+ new HashMap<Integer, HashSet<String>>();
private static final int EVENT_LOAD_DONE = 1;
private static final int EVENT_UPDATE_DONE = 2;
+ private static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3;
+ private static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4;
+ private static final int SMS_CB_CODE_SCHEME_MIN = 0;
+ private static final int SMS_CB_CODE_SCHEME_MAX = 255;
Handler mHandler = new Handler() {
@Override
@@ -75,6 +86,14 @@
mLock.notifyAll();
}
break;
+ case EVENT_SET_BROADCAST_ACTIVATION_DONE:
+ case EVENT_SET_BROADCAST_CONFIG_DONE:
+ ar = (AsyncResult) msg.obj;
+ synchronized (mLock) {
+ mSuccess = (ar.exception == null);
+ mLock.notifyAll();
+ }
+ break;
}
}
};
@@ -192,6 +211,126 @@
return mSms;
}
+ public boolean enableCellBroadcast(int messageIdentifier) {
+ if (DBG) log("enableCellBroadcast");
+
+ Context context = mPhone.getContext();
+
+ context.enforceCallingPermission(
+ "android.permission.RECEIVE_SMS",
+ "Enabling cell broadcast SMS");
+
+ String client = context.getPackageManager().getNameForUid(
+ Binder.getCallingUid());
+ HashSet<String> clients = mCellBroadcastSubscriptions.get(messageIdentifier);
+
+ if (clients == null) {
+ // This is a new message identifier
+ clients = new HashSet<String>();
+ mCellBroadcastSubscriptions.put(messageIdentifier, clients);
+
+ if (!updateCellBroadcastConfig()) {
+ mCellBroadcastSubscriptions.remove(messageIdentifier);
+ return false;
+ }
+ }
+
+ clients.add(client);
+
+ if (DBG)
+ log("Added cell broadcast subscription for MID " + messageIdentifier
+ + " from client " + client);
+
+ return true;
+ }
+
+ public boolean disableCellBroadcast(int messageIdentifier) {
+ if (DBG) log("disableCellBroadcast");
+
+ Context context = mPhone.getContext();
+
+ context.enforceCallingPermission(
+ "android.permission.RECEIVE_SMS",
+ "Disabling cell broadcast SMS");
+
+ String client = context.getPackageManager().getNameForUid(
+ Binder.getCallingUid());
+ HashSet<String> clients = mCellBroadcastSubscriptions.get(messageIdentifier);
+
+ if (clients != null && clients.remove(client)) {
+ if (DBG)
+ log("Removed cell broadcast subscription for MID " + messageIdentifier
+ + " from client " + client);
+
+ if (clients.isEmpty()) {
+ mCellBroadcastSubscriptions.remove(messageIdentifier);
+ updateCellBroadcastConfig();
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean updateCellBroadcastConfig() {
+ Set<Integer> messageIdentifiers = mCellBroadcastSubscriptions.keySet();
+
+ if (messageIdentifiers.size() > 0) {
+ SmsBroadcastConfigInfo[] configs =
+ new SmsBroadcastConfigInfo[messageIdentifiers.size()];
+ int i = 0;
+
+ for (int messageIdentifier : messageIdentifiers) {
+ configs[i++] = new SmsBroadcastConfigInfo(messageIdentifier, messageIdentifier,
+ SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, true);
+ }
+
+ return setCellBroadcastConfig(configs) && setCellBroadcastActivation(true);
+ } else {
+ return setCellBroadcastActivation(false);
+ }
+ }
+
+ private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) {
+ if (DBG)
+ log("Calling setGsmBroadcastConfig with " + configs.length + " configurations");
+
+ synchronized (mLock) {
+ Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE);
+
+ mSuccess = false;
+ mPhone.mCM.setGsmBroadcastConfig(configs, response);
+
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ log("interrupted while trying to set cell broadcast config");
+ }
+ }
+
+ return mSuccess;
+ }
+
+ private boolean setCellBroadcastActivation(boolean activate) {
+ if (DBG)
+ log("Calling setCellBroadcastActivation(" + activate + ")");
+
+ synchronized (mLock) {
+ Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
+
+ mSuccess = false;
+ mPhone.mCM.setGsmBroadcastActivation(activate, response);
+
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ log("interrupted while trying to set cell broadcast activation");
+ }
+ }
+
+ return mSuccess;
+ }
+
protected void log(String msg) {
Log.d(LOG_TAG, "[SimSmsInterfaceManager] " + msg);
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
new file mode 100644
index 0000000..5f27cfc
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.gsm;
+
+public class SmsCbHeader {
+ public static final int PDU_HEADER_LENGTH = 6;
+
+ public final int geographicalScope;
+
+ public final int messageCode;
+
+ public final int updateNumber;
+
+ public final int messageIdentifier;
+
+ public final int dataCodingScheme;
+
+ public final int pageIndex;
+
+ public final int nrOfPages;
+
+ public SmsCbHeader(byte[] pdu) throws IllegalArgumentException {
+ if (pdu == null || pdu.length < PDU_HEADER_LENGTH) {
+ throw new IllegalArgumentException("Illegal PDU");
+ }
+
+ geographicalScope = (pdu[0] & 0xc0) >> 6;
+ messageCode = ((pdu[0] & 0x3f) << 4) | ((pdu[1] & 0xf0) >> 4);
+ updateNumber = pdu[1] & 0x0f;
+ messageIdentifier = (pdu[2] << 8) | pdu[3];
+ dataCodingScheme = pdu[4];
+
+ // Check for invalid page parameter
+ int pageIndex = (pdu[5] & 0xf0) >> 4;
+ int nrOfPages = pdu[5] & 0x0f;
+
+ if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) {
+ pageIndex = 1;
+ nrOfPages = 1;
+ }
+
+ this.pageIndex = pageIndex;
+ this.nrOfPages = nrOfPages;
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 50dd402..f4c5e6c 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -24,6 +24,7 @@
import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.EncodeException;
import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.SimRegionCache;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
@@ -48,6 +49,12 @@
public class SmsMessage extends SmsMessageBase{
static final String LOG_TAG = "GSM";
+ /**
+ * Used with the ENCODING_ constants from {@link android.telephony.SmsMessage}
+ * Not a part of the public API, therefore not in order with those constants.
+ */
+ private static final int ENCODING_KSC5601 = 4000;
+
private MessageClass messageClass;
/**
@@ -781,6 +788,28 @@
return ret;
}
+ /**
+ * Interprets the user data payload as KSC5601 characters, and
+ * decodes them into a String
+ *
+ * @param byteCount the number of bytes in the user data payload
+ * @return a String with the decoded characters
+ */
+ String getUserDataKSC5601(int byteCount) {
+ String ret;
+
+ try {
+ ret = new String(pdu, cur, byteCount, "KSC5601");
+ } catch (UnsupportedEncodingException ex) {
+ // Should return same as ENCODING_UNKNOWN on error.
+ ret = null;
+ Log.e(LOG_TAG, "implausible UnsupportedEncodingException", ex);
+ }
+
+ cur += byteCount;
+ return ret;
+ }
+
boolean moreDataPresent() {
return (pdu.length > cur);
}
@@ -930,6 +959,8 @@
// TP-Message-Type-Indicator
// 9.2.3
case 0:
+ case 3: //GSM 03.40 9.2.3.1: MTI == 3 is Reserved.
+ //This should be processed in the same way as MTI == 0 (Deliver)
parseSmsDeliver(p, firstByte);
break;
case 2:
@@ -1108,6 +1139,10 @@
} else {
Log.w(LOG_TAG, "3 - Unsupported SMS data coding scheme "
+ (dataCodingScheme & 0xff));
+ if (SimRegionCache.getRegion() == SimRegionCache.MCC_KOREAN) {
+ Log.w(LOG_TAG, "Korean SIM, using KSC5601 for decoding.");
+ encodingType = ENCODING_KSC5601;
+ }
}
// set both the user data and the user data header.
@@ -1129,6 +1164,10 @@
case ENCODING_16BIT:
messageBody = p.getUserDataUCS2(count);
break;
+
+ case ENCODING_KSC5601:
+ messageBody = p.getUserDataKSC5601(count);
+ break;
}
if (Config.LOGV) Log.v(LOG_TAG, "SMS message body (raw): '" + messageBody + "'");
diff --git a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
old mode 100644
new mode 100755
index 6458fda..ec3d20a
--- a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
@@ -53,6 +53,7 @@
private ArrayList<byte[]> mIapFileRecord;
private ArrayList<byte[]> mEmailFileRecord;
private Map<Integer, ArrayList<String>> mEmailsForAdnRec;
+ private boolean mRefreshCache = false;
private static final int EVENT_PBR_LOAD_DONE = 1;
private static final int EVENT_USIM_ADN_LOAD_DONE = 2;
@@ -91,11 +92,19 @@
mEmailFileRecord = null;
mPbrFile = null;
mIsPbrPresent = true;
+ mRefreshCache = false;
}
public ArrayList<AdnRecord> loadEfFilesFromUsim() {
synchronized (mLock) {
- if (!mPhoneBookRecords.isEmpty()) return mPhoneBookRecords;
+ if (!mPhoneBookRecords.isEmpty()) {
+ if (mRefreshCache) {
+ mRefreshCache = false;
+ refreshCache();
+ }
+ return mPhoneBookRecords;
+ }
+
if (!mIsPbrPresent) return null;
// Check if the PBR file is present in the cache, if not read it
@@ -116,6 +125,20 @@
return mPhoneBookRecords;
}
+ private void refreshCache() {
+ if (mPbrFile == null) return;
+ mPhoneBookRecords.clear();
+
+ int numRecs = mPbrFile.mFileIds.size();
+ for (int i = 0; i < numRecs; i++) {
+ readAdnFileAndWait(i);
+ }
+ }
+
+ public void invalidateCache() {
+ mRefreshCache = true;
+ }
+
private void readPbrFileAndWait() {
mPhone.getIccFileHandler().loadEFLinearFixedAll(EF_PBR, obtainMessage(EVENT_PBR_LOAD_DONE));
try {
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/package.html b/telephony/java/com/android/internal/telephony/gsm/stk/package.html
deleted file mode 100644
index c285b57..0000000
--- a/telephony/java/com/android/internal/telephony/gsm/stk/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<HTML>
-<BODY>
-Provides classes for SIM Toolkit Service.
-</BODY>
-</HTML>
diff --git a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java
index de59b81..67df510 100644
--- a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java
+++ b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony;
+package android.telephony;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
diff --git a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberWatcherTest.java b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberWatcherTest.java
index 88eaecd..4c7ebca 100644
--- a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberWatcherTest.java
+++ b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberWatcherTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony;
+package android.telephony;
import android.telephony.PhoneNumberFormattingTextWatcher;
import android.test.suitebuilder.annotation.SmallTest;
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/AdnRecordTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/AdnRecordTest.java
index 8a4a285..5511c09c 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/AdnRecordTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/AdnRecordTest.java
@@ -170,6 +170,18 @@
assertEquals("Adgjm", adn.getAlphaTag());
assertEquals("+18885551212,12345678", adn.getNumber());
assertFalse(adn.isEmpty());
+
+ //
+ // Test that a ADN record with KSC5601 will get converted correctly
+ // This test will only be run when using a Korean SIM
+ //
+ if (SimRegionCache.getRegion() == SimRegionCache.MCC_KOREAN) {
+ adn = new AdnRecord(IccUtils.hexStringToBytes(
+ "3030312C20C8AB41B1E6FFFFFFFFFFFF07811010325476F8FFFFFFFFFFFF"));
+ assertEquals("001, \uD64DA\uAE38", adn.getAlphaTag());
+ assertEquals("01012345678", adn.getNumber());
+ assertFalse(adn.isEmpty());
+ }
}
}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
new file mode 100644
index 0000000..7136ea0
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.telephony.SmsCbMessage;
+import android.test.AndroidTestCase;
+
+/**
+ * Test cases for basic SmsCbMessage operations
+ */
+public class GsmSmsCbTest extends AndroidTestCase {
+
+ private void doTestGeographicalScopeValue(byte[] pdu, byte b, int expectedGs) {
+ pdu[0] = b;
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected geographical scope decoded", expectedGs, msg
+ .getGeographicalScope());
+ }
+
+ public void testCreateNullPdu() {
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(null);
+
+ assertNull("createFromPdu(byte[] with null pdu should return null", msg);
+ }
+
+ public void testCreateTooShortPdu() {
+ byte[] pdu = new byte[4];
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertNull("createFromPdu(byte[] with too short pdu should return null", msg);
+ }
+
+ public void testGetGeographicalScope() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+
+ doTestGeographicalScopeValue(pdu, (byte)0x00,
+ SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE);
+ doTestGeographicalScopeValue(pdu, (byte)0x40, SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE);
+ doTestGeographicalScopeValue(pdu, (byte)0x80, SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE);
+ doTestGeographicalScopeValue(pdu, (byte)0xC0, SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE);
+ }
+
+ public void testGetMessageBody7Bit() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+ }
+
+ public void testGetMessageBody7BitFull() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xC4, (byte)0xE5,
+ (byte)0xB4, (byte)0xFB, (byte)0x0C, (byte)0x2A, (byte)0xE3, (byte)0xC3, (byte)0x63,
+ (byte)0x3A, (byte)0x3B, (byte)0x0F, (byte)0xCA, (byte)0xCD, (byte)0x40, (byte)0x63,
+ (byte)0x74, (byte)0x58, (byte)0x1E, (byte)0x1E, (byte)0xD3, (byte)0xCB, (byte)0xF2,
+ (byte)0x39, (byte)0x88, (byte)0xFD, (byte)0x76, (byte)0x9F, (byte)0x59, (byte)0xA0,
+ (byte)0x76, (byte)0x39, (byte)0xEC, (byte)0x4E, (byte)0xBB, (byte)0xCF, (byte)0x20,
+ (byte)0x3A, (byte)0xBA, (byte)0x2C, (byte)0x2F, (byte)0x83, (byte)0xD2, (byte)0x73,
+ (byte)0x90, (byte)0xFB, (byte)0x0D, (byte)0x82, (byte)0x87, (byte)0xC9, (byte)0xE4,
+ (byte)0xB4, (byte)0xFB, (byte)0x1C, (byte)0x02
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals(
+ "Unexpected 7-bit string decoded",
+ "A GSM default alphabet message being exactly 93 characters long, " +
+ "meaning there is no padding!",
+ msg.getMessageBody());
+ }
+
+ public void testGetMessageBody7BitWithLanguage() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x04, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "es", msg.getLanguageCode());
+ }
+
+ public void testGetMessageBody7BitWithLanguageInBody() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x10, (byte)0x11, (byte)0x73,
+ (byte)0x7B, (byte)0x23, (byte)0x08, (byte)0x3A, (byte)0x4E, (byte)0x9B, (byte)0x20,
+ (byte)0x72, (byte)0xD9, (byte)0x1C, (byte)0xAE, (byte)0xB3, (byte)0xE9, (byte)0xA0,
+ (byte)0x30, (byte)0x1B, (byte)0x8E, (byte)0x0E, (byte)0x8B, (byte)0xCB, (byte)0x74,
+ (byte)0x50, (byte)0xBB, (byte)0x3C, (byte)0x9F, (byte)0x87, (byte)0xCF, (byte)0x65,
+ (byte)0xD0, (byte)0x3D, (byte)0x4D, (byte)0x47, (byte)0x83, (byte)0xC6, (byte)0x61,
+ (byte)0xB9, (byte)0x3C, (byte)0x1D, (byte)0x3E, (byte)0x97, (byte)0x41, (byte)0xF2,
+ (byte)0x32, (byte)0xBD, (byte)0x2E, (byte)0x77, (byte)0x83, (byte)0xE0, (byte)0x61,
+ (byte)0x32, (byte)0x39, (byte)0xED, (byte)0x3E, (byte)0x37, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "sv", msg.getLanguageCode());
+ }
+
+ public void testGetMessageBody8Bit() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x44, (byte)0x11, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("8-bit message body should be empty", "", msg.getMessageBody());
+ }
+
+ public void testGetMessageBodyUcs2() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x48, (byte)0x11, (byte)0x00,
+ (byte)0x41, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x55, (byte)0x00, (byte)0x43,
+ (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x20, (byte)0x00,
+ (byte)0x6D, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x73,
+ (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x65, (byte)0x00,
+ (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x6E,
+ (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x69, (byte)0x00,
+ (byte)0x6E, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x67,
+ (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x20, (byte)0x04,
+ (byte)0x34, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x68,
+ (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x61, (byte)0x00,
+ (byte)0x63, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x72,
+ (byte)0x00, (byte)0x0D, (byte)0x00, (byte)0x0D
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+ }
+
+ public void testGetMessageBodyUcs2WithLanguageInBody() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x11, (byte)0x11, (byte)0x78,
+ (byte)0x3C, (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x55,
+ (byte)0x00, (byte)0x43, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x32, (byte)0x00,
+ (byte)0x20, (byte)0x00, (byte)0x6D, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x73,
+ (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x67, (byte)0x00,
+ (byte)0x65, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F,
+ (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61, (byte)0x00,
+ (byte)0x69, (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E,
+ (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x61, (byte)0x00,
+ (byte)0x20, (byte)0x04, (byte)0x34, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63,
+ (byte)0x00, (byte)0x68, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x72, (byte)0x00,
+ (byte)0x61, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65,
+ (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x0D
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());
+ }
+
+ public void testGetMessageIdentifier() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected message identifier decoded", 12345, msg.getMessageIdentifier());
+ }
+
+ public void testGetMessageCode() {
+ byte[] pdu = {
+ (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected message code decoded", 682, msg.getMessageCode());
+ }
+
+ public void testGetUpdateNumber() {
+ byte[] pdu = {
+ (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected update number decoded", 5, msg.getUpdateNumber());
+ }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java
index 8a66614..f578a8d 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java
@@ -102,4 +102,25 @@
sms = SmsMessage.createFromEfRecord(1, data);
assertNotNull(sms.getMessageBody());
}
+
+ @MediumTest
+ public void testEfRecordKorean() throws Exception {
+ if (SimRegionCache.getRegion() == SimRegionCache.MCC_KOREAN) {
+ SmsMessage sms;
+
+ String s = "01089128010099010259040ba11000000000f00095013091900563008c4142"
+ + "434445b0a1b3aab4d9b6f3b8b631323334354142434445b0a1b3aab4d9b6f3"
+ + "b8b631323334354142434445b0a1b3aab4d9b6f3b8b6313233343541424344"
+ + "45b0a1b3aab4d9b6f3b8b63132333435000000000000000000000000000000"
+ + "00000000000000000000000000000000000000000000000000000000000000"
+ + "0000000000000000000000000000ffffffffffffff";
+
+
+ byte[] data = IccUtils.hexStringToBytes(s);
+
+ sms = SmsMessage.createFromEfRecord(1, data);
+ assertNotNull(sms.getMessageBody());
+ assertTrue(sms.getMessageBody().startsWith("ABCDE\uAC00\uB098\uB2E4\uB77C\uB9C812345ABCDE"));
+ }
+ }
}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/SimUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/SimUtilsTest.java
index db38ede..0502636 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/SimUtilsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/SimUtilsTest.java
@@ -82,6 +82,30 @@
data = IccUtils.hexStringToBytes("820505302D82d32d31");
// Example from 3GPP TS 11.11 V18.1.3.0 annex B
assertEquals("-\u0532\u0583-1", IccUtils.adnStringFieldToString(data, 0, data.length));
+
+ /*
+ * adnStringFieldToStringKsc5601Support()
+ * Tests equal the ones above, and will only be run if the SIM is NOT korean.
+ */
+
+ if (SimRegionCache.getRegion() != SimRegionCache.MCC_KOREAN) {
+ data = IccUtils.hexStringToBytes("00566f696365204d61696c07918150367742f3ffffffffffff");
+ // Again, skip prepended 0
+ // (this is an EF[ADN] record)
+ assertEquals("Voice Mail", IccUtils.adnStringFieldToStringKsc5601Support(data, 1, data.length - 15));
+
+ data = IccUtils.hexStringToBytes("809673539A5764002F004DFFFFFFFFFF");
+ // (this is from an EF[ADN] record)
+ assertEquals("\u9673\u539A\u5764/M", IccUtils.adnStringFieldToStringKsc5601Support(data, 0, data.length));
+
+ data = IccUtils.hexStringToBytes("810A01566fec6365204de0696cFFFFFF");
+ // (this is made up to test since I don't have a real one)
+ assertEquals("Vo\u00ECce M\u00E0il", IccUtils.adnStringFieldToStringKsc5601Support(data, 0, data.length));
+
+ data = IccUtils.hexStringToBytes("820505302D82d32d31");
+ // Example from 3GPP TS 11.11 V18.1.3.0 annex B
+ assertEquals("-\u0532\u0583-1", IccUtils.adnStringFieldToStringKsc5601Support(data, 0, data.length));
+ }
}
}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/Wap230WspContentTypeTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/Wap230WspContentTypeTest.java
new file mode 100644
index 0000000..d31b294
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/Wap230WspContentTypeTest.java
@@ -0,0 +1,853 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import com.android.internal.telephony.WspTypeDecoder;
+import com.android.internal.util.HexDump;
+
+import java.io.ByteArrayOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class Wap230WspContentTypeTest extends TestCase {
+
+ public static final Map<Integer, String> WELL_KNOWN_SHORT_MIME_TYPES
+ = new HashMap<Integer, String>();
+ public static final Map<Integer, String> WELL_KNOWN_LONG_MIME_TYPES
+ = new HashMap<Integer, String>();
+ public static final Map<Integer, String> WELL_KNOWN_PARAMETERS
+ = new HashMap<Integer, String>();
+
+ static {
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x00, "*/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x01, "text/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x02, "text/html");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x03, "text/plain");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x04, "text/x-hdml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x05, "text/x-ttml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x06, "text/x-vCalendar");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x07, "text/x-vCard");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x08, "text/vnd.wap.wml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x09, "text/vnd.wap.wmlscript");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x0A, "text/vnd.wap.wta-event");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x0B, "multipart/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x0C, "multipart/mixed");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x0D, "multipart/form-data");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x0E, "multipart/byterantes");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x0F, "multipart/alternative");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x10, "application/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x11, "application/java-vm");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x12, "application/x-www-form-urlencoded");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x13, "application/x-hdmlc");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x14, "application/vnd.wap.wmlc");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x15, "application/vnd.wap.wmlscriptc");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x16, "application/vnd.wap.wta-eventc");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x17, "application/vnd.wap.uaprof");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x18, "application/vnd.wap.wtls-ca-certificate");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x19, "application/vnd.wap.wtls-user-certificate");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x1A, "application/x-x509-ca-cert");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x1B, "application/x-x509-user-cert");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x1C, "image/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x1D, "image/gif");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x1E, "image/jpeg");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x1F, "image/tiff");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x20, "image/png");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x21, "image/vnd.wap.wbmp");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x22, "application/vnd.wap.multipart.*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x23, "application/vnd.wap.multipart.mixed");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x24, "application/vnd.wap.multipart.form-data");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x25, "application/vnd.wap.multipart.byteranges");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x26, "application/vnd.wap.multipart.alternative");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x27, "application/xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x28, "text/xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x29, "application/vnd.wap.wbxml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x2A, "application/x-x968-cross-cert");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x2B, "application/x-x968-ca-cert");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x2C, "application/x-x968-user-cert");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x2D, "text/vnd.wap.si");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x2E, "application/vnd.wap.sic");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x2F, "text/vnd.wap.sl");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x30, "application/vnd.wap.slc");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x31, "text/vnd.wap.co");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x32, "application/vnd.wap.coc");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x33, "application/vnd.wap.multipart.related");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x34, "application/vnd.wap.sia");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x35, "text/vnd.wap.connectivity-xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x36, "application/vnd.wap.connectivity-wbxml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x37, "application/pkcs7-mime");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x38, "application/vnd.wap.hashed-certificate");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x39, "application/vnd.wap.signed-certificate");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x3A, "application/vnd.wap.cert-response");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x3B, "application/xhtml+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x3C, "application/wml+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x3D, "text/css");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x3E, "application/vnd.wap.mms-message");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x3F, "application/vnd.wap.rollover-certificate");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x40, "application/vnd.wap.locc+wbxml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x41, "application/vnd.wap.loc+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x42, "application/vnd.syncml.dm+wbxml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x43, "application/vnd.syncml.dm+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x44, "application/vnd.syncml.notification");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x45, "application/vnd.wap.xhtml+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x46, "application/vnd.wv.csp.cir");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x47, "application/vnd.oma.dd+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x48, "application/vnd.oma.drm.message");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x49, "application/vnd.oma.drm.content");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x4A, "application/vnd.oma.drm.rights+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x4B, "application/vnd.oma.drm.rights+wbxml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x4C, "application/vnd.wv.csp+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x4D, "application/vnd.wv.csp+wbxml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x4E, "application/vnd.syncml.ds.notification");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x4F, "audio/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x50, "video/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x51, "application/vnd.oma.dd2+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x52, "application/mikey");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x53, "application/vnd.oma.dcd");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x54, "application/vnd.oma.dcdc");
+
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0201, "application/vnd.uplanet.cacheop-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0202, "application/vnd.uplanet.signal");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0203, "application/vnd.uplanet.alert-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0204, "application/vnd.uplanet.list-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0205, "application/vnd.uplanet.listcmd-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0206, "application/vnd.uplanet.channel-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0207, "application/vnd.uplanet.provisioning-status-uri");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0208, "x-wap.multipart/vnd.uplanet.header-set");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0209, "application/vnd.uplanet.bearer-choice-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x020A, "application/vnd.phonecom.mmc-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x020B, "application/vnd.nokia.syncset+wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x020C, "image/x-up-wpng");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0300, "application/iota.mmc-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0301, "application/iota.mmc-xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0302, "application/vnd.syncml+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0303, "application/vnd.syncml+wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0304, "text/vnd.wap.emn+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0305, "text/calendar");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0306, "application/vnd.omads-email+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0307, "application/vnd.omads-file+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0308, "application/vnd.omads-folder+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0309, "text/directory;profile=vCard");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x030A, "application/vnd.wap.emn+wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x030B, "application/vnd.nokia.ipdc-purchase-response");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x030C, "application/vnd.motorola.screen3+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x030D, "application/vnd.motorola.screen3+gzip");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x030E, "application/vnd.cmcc.setting+wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x030F, "application/vnd.cmcc.bombing+wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0310, "application/vnd.docomo.pf");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0311, "application/vnd.docomo.ub");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0312, "application/vnd.omaloc-supl-init");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0313, "application/vnd.oma.group-usage-list+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0314, "application/oma-directory+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0315, "application/vnd.docomo.pf2");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0316, "application/vnd.oma.drm.roap-trigger+wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0317, "application/vnd.sbm.mid2");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0318, "application/vnd.wmf.bootstrap");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0319, "application/vnc.cmcc.dcd+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x031A, "application/vnd.sbm.cid");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x031B, "application/vnd.oma.bcast.provisioningtrigger");
+
+ WELL_KNOWN_PARAMETERS.put(0x00, "Q");
+ WELL_KNOWN_PARAMETERS.put(0x01, "Charset");
+ WELL_KNOWN_PARAMETERS.put(0x02, "Level");
+ WELL_KNOWN_PARAMETERS.put(0x03, "Type");
+ WELL_KNOWN_PARAMETERS.put(0x07, "Differences");
+ WELL_KNOWN_PARAMETERS.put(0x08, "Padding");
+ WELL_KNOWN_PARAMETERS.put(0x09, "Type");
+ WELL_KNOWN_PARAMETERS.put(0x0E, "Max-Age");
+ WELL_KNOWN_PARAMETERS.put(0x10, "Secure");
+ WELL_KNOWN_PARAMETERS.put(0x11, "SEC");
+ WELL_KNOWN_PARAMETERS.put(0x12, "MAC");
+ WELL_KNOWN_PARAMETERS.put(0x13, "Creation-date");
+ WELL_KNOWN_PARAMETERS.put(0x14, "Modification-date");
+ WELL_KNOWN_PARAMETERS.put(0x15, "Read-date");
+ WELL_KNOWN_PARAMETERS.put(0x16, "Size");
+ WELL_KNOWN_PARAMETERS.put(0x17, "Name");
+ WELL_KNOWN_PARAMETERS.put(0x18, "Filename");
+ WELL_KNOWN_PARAMETERS.put(0x19, "Start");
+ WELL_KNOWN_PARAMETERS.put(0x1A, "Start-info");
+ WELL_KNOWN_PARAMETERS.put(0x1B, "Comment");
+ WELL_KNOWN_PARAMETERS.put(0x1C, "Domain");
+ WELL_KNOWN_PARAMETERS.put(0x1D, "Path");
+
+ }
+
+ final int WSP_DEFINED_SHORT_MIME_TYPE_COUNT = 85;
+ final int WSP_DEFINED_LONG_MIME_TYPE_COUNT = 85;
+
+ private static final byte WSP_STRING_TERMINATOR = 0x00;
+ private static final byte WSP_SHORT_INTEGER_MASK = (byte) 0x80;
+ private static final byte WSP_LENGTH_QUOTE = 0x1F;
+ private static final byte WSP_QUOTE = 0x22;
+
+ private static final short LONG_MIME_TYPE_OMA_DIRECTORY_XML = 0x0314;
+ private static final short LONG_MIME_TYPE_UNASSIGNED = 0x052C;
+
+ private static final byte SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE = 0x3F;
+ private static final byte SHORT_MIME_TYPE_UNASSIGNED = 0x60;
+
+ private static final String STRING_MIME_TYPE_ROLLOVER_CERTIFICATE
+ = "application/vnd.wap.rollover-certificate";
+
+ private static final byte TYPED_PARAM_Q = 0x00;
+ private static final byte TYPED_PARAM_DOMAIN = 0x1C;
+ private static final byte PARAM_UNASSIGNED = 0x42;
+ private static final byte PARAM_NO_VALUE = 0x00;
+ private static final byte TYPED_PARAM_SEC = 0x11;
+ private static final byte TYPED_PARAM_MAC = 0x12;
+
+ public void testHasExpectedNumberOfShortMimeTypes() {
+ assertEquals(WSP_DEFINED_SHORT_MIME_TYPE_COUNT, WELL_KNOWN_SHORT_MIME_TYPES.size());
+ }
+
+ public void testHasExpectedNumberOfLongMimeTypes() {
+ assertEquals(WSP_DEFINED_LONG_MIME_TYPE_COUNT, WELL_KNOWN_LONG_MIME_TYPES.size());
+ }
+
+ public void testWellKnownShortIntegerMimeTypeValues() {
+
+ for (int value : Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.keySet()) {
+ WspTypeDecoder unit = new WspTypeDecoder(
+ HexDump.toByteArray((byte) (value | WSP_SHORT_INTEGER_MASK)));
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+ int wellKnownValue = (int) unit.getValue32();
+ assertEquals(Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.get(value), mimeType);
+ assertEquals(value, wellKnownValue);
+ assertEquals(1, unit.getDecodedDataLength());
+ }
+ }
+
+ public void testWellKnownLongIntegerMimeTypeValues() {
+ byte headerLength = 3;
+ byte typeLength = 2;
+ for (int value : Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.keySet()) {
+ byte[] data = new byte[10];
+ data[0] = headerLength;
+ data[1] = typeLength;
+ data[2] = (byte) (value >> 8);
+ data[3] = (byte) (value & 0xFF);
+ WspTypeDecoder unit = new WspTypeDecoder(data);
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+ int wellKnownValue = (int) unit.getValue32();
+ assertEquals(Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.get(value), mimeType);
+ assertEquals(value, wellKnownValue);
+ assertEquals(4, unit.getDecodedDataLength());
+ }
+ }
+
+ public void testDecodeReturnsFalse_WhenOnlyAZeroBytePresent() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x00);
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertFalse(unit.decodeContentType(0));
+ }
+
+ public void testConstrainedMediaExtensionMedia() throws Exception {
+
+ String testType = "application/wibble";
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(testType.getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+ assertEquals(testType, mimeType);
+ assertEquals(-1, unit.getValue32());
+ assertEquals(19, unit.getDecodedDataLength());
+ }
+
+ public void testGeneralFormShortLengthExtensionMedia() throws Exception {
+
+ String testType = "12345678901234567890123456789";
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(testType.length() + 1);
+ out.write(testType.getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+ assertEquals(testType, mimeType);
+ assertEquals(-1, unit.getValue32());
+ assertEquals(31, unit.getDecodedDataLength());
+ }
+
+ public void testGeneralFormShortLengthWellKnownShortInteger() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x01);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(2, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormShortLengthWellKnownShortIntegerWithUnknownValue() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x01);
+ out.write(SHORT_MIME_TYPE_UNASSIGNED | WSP_SHORT_INTEGER_MASK);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+ assertNull(mimeType);
+ assertEquals(SHORT_MIME_TYPE_UNASSIGNED, unit.getValue32());
+ assertEquals(2, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormShortLengthWellKnownLongInteger() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(0x03); // header length
+ out.write(0x02); // type length (2 octets)
+ out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML >> 8);
+ out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML & 0xFF);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals("application/oma-directory+xml", mimeType);
+ assertEquals(LONG_MIME_TYPE_OMA_DIRECTORY_XML, unit.getValue32());
+ assertEquals(4, unit.getDecodedDataLength());
+ }
+
+ public void testGeneralFormShortLengthWellKnownLongIntegerWithUnknownValue() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(0x03); // Value-length, short-length
+ out.write(0x02); // long-integer length (2 octets)
+ out.write(LONG_MIME_TYPE_UNASSIGNED >> 8);
+ out.write(LONG_MIME_TYPE_UNASSIGNED & 0xFF);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertNull(mimeType);
+ assertEquals(LONG_MIME_TYPE_UNASSIGNED, unit.getValue32());
+ assertEquals(4, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormLengthQuoteWellKnownShortInteger() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(WSP_LENGTH_QUOTE);
+ out.write(0x01); // Length as UINTVAR
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(3, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormLengthQuoteWellKnownShortIntegerWithUnknownValue() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(WSP_LENGTH_QUOTE);
+ out.write(0x01); // Length as UINTVAR
+ out.write(SHORT_MIME_TYPE_UNASSIGNED | WSP_SHORT_INTEGER_MASK);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+ assertNull(mimeType);
+ assertEquals(SHORT_MIME_TYPE_UNASSIGNED, unit.getValue32());
+ assertEquals(3, unit.getDecodedDataLength());
+ }
+
+ public void testGeneralFormLengthQuoteWellKnownLongInteger() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(WSP_LENGTH_QUOTE);
+ out.write(0x03); // Length as UINTVAR
+ out.write(0x02); // long-integer length (2 octets)
+ out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML >> 8);
+ out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML & 0xFF);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals("application/oma-directory+xml", mimeType);
+ assertEquals(LONG_MIME_TYPE_OMA_DIRECTORY_XML, unit.getValue32());
+ assertEquals(5, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormLengthQuoteWellKnownLongIntegerWithUnknownValue() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(WSP_LENGTH_QUOTE);
+ out.write(0x03); // Length as UINTVAR
+ out.write(0x02); // long-integer length (2 octets)
+ out.write(LONG_MIME_TYPE_UNASSIGNED >> 8);
+ out.write(LONG_MIME_TYPE_UNASSIGNED & 0xFF);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertNull(mimeType);
+ assertEquals(LONG_MIME_TYPE_UNASSIGNED, unit.getValue32());
+ assertEquals(5, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormLengthQuoteExtensionMedia() throws Exception {
+
+ String testType = "application/wibble";
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(WSP_LENGTH_QUOTE);
+ out.write(testType.length() + 1); // Length as UINTVAR
+
+ out.write(testType.getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(testType, mimeType);
+ assertEquals(-1, unit.getValue32());
+ assertEquals(21, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormLengthQuoteExtensionMediaWithNiceLongMimeType() throws Exception {
+
+ String testType =
+ "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ +"01234567890123456789012345678901234567890123456789012345678901234567890123456789";
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(WSP_LENGTH_QUOTE);
+ out.write(0x81); // Length as UINTVAR (161 decimal, 0xA1), 2 bytes
+ out.write(0x21);
+
+ out.write(testType.getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(testType, mimeType);
+ assertEquals(-1, unit.getValue32());
+ assertEquals(164, unit.getDecodedDataLength());
+
+ }
+
+ public void testConstrainedMediaExtensionMediaWithSpace() throws Exception {
+
+ String testType = " application/wibble";
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(testType.getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(testType, mimeType);
+ assertEquals(-1, unit.getValue32());
+ assertEquals(20, unit.getDecodedDataLength());
+
+ }
+
+ public void testTypedParamWellKnownShortIntegerNoValue() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x03); // Value-length, short-length
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_DOMAIN | WSP_SHORT_INTEGER_MASK);
+ out.write(PARAM_NO_VALUE);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+ assertEquals(4, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals(null, params.get("Domain"));
+
+ }
+
+ public void testTypedParamWellKnownShortIntegerTokenText() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x14); // Value-length, short-length
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_DOMAIN | WSP_SHORT_INTEGER_MASK);
+ out.write("wdstechnology.com".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+ assertEquals(out.toByteArray().length, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("wdstechnology.com", params.get("Domain"));
+
+ }
+
+ public void testTypedParamWellKnownLongIntegerTokenText() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x15);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(0x01);
+ out.write(TYPED_PARAM_DOMAIN);
+ out.write("wdstechnology.com".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+ assertEquals(22, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("wdstechnology.com", params.get("Domain"));
+
+ }
+
+ public void testTypedParamWellKnownShortIntegerQuotedText() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x15);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_DOMAIN | WSP_SHORT_INTEGER_MASK);
+ out.write(WSP_QUOTE);
+ out.write("wdstechnology.com".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(0x3F, unit.getValue32());
+ assertEquals(22, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("wdstechnology.com", params.get("Domain"));
+
+ }
+
+ public void testTypedParamWellKnownShortIntegerCompactIntegerValue() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x3);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_SEC | WSP_SHORT_INTEGER_MASK);
+ out.write(0x01 | WSP_SHORT_INTEGER_MASK);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(0x3F, unit.getValue32());
+ assertEquals(4, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("1", params.get("SEC"));
+
+ }
+
+ public void testTypedParamWellKnownShortIntegerMultipleParameters() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x0B);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_SEC | WSP_SHORT_INTEGER_MASK);
+ out.write(0x01 | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_MAC | WSP_SHORT_INTEGER_MASK);
+ out.write(WSP_QUOTE);
+ out.write("imapc".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(12, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("1", params.get("SEC"));
+ assertEquals("imapc", params.get("MAC"));
+ }
+
+ public void testUntypedParamIntegerValueShortInteger() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x0A);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write("MYPARAM".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR); // EOS
+ out.write(0x45 | WSP_SHORT_INTEGER_MASK);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(11, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("69", params.get("MYPARAM"));
+ }
+
+ public void testUntypedParamIntegerValueLongInteger() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x0C);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write("MYPARAM".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+ out.write(0x02); // Short Length
+ out.write(0x42); // Long Integer byte 1
+ out.write(0x69); // Long Integer byte 2
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(0x3F, unit.getValue32());
+ assertEquals(13, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("17001", params.get("MYPARAM"));
+ }
+
+ public void testUntypedParamTextNoValue() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x0A);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write("MYPARAM".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+ out.write(PARAM_NO_VALUE);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(11, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals(null, params.get("MYPARAM"));
+
+ }
+
+ public void testUntypedParamTextTokenText() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x11);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write("MYPARAM".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+ out.write("myvalue".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(18, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("myvalue", params.get("MYPARAM"));
+ }
+
+ public void testUntypedParamTextQuotedString() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x11);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write("MYPARAM".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+ out.write(WSP_QUOTE);
+ out.write("myvalue".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(19, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("myvalue", params.get("MYPARAM"));
+
+ }
+
+ public void testDecodesReturnsFalse_ForParamWithMissingValue() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x09);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write("MYPARAM".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertFalse(unit.decodeContentType(0));
+ }
+
+ public void testTypedParamTextQValue() {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x04);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_Q);
+ out.write(0x83); // Q value byte 1
+ out.write(0x31); // Q value byte 2
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(0x3F, unit.getValue32());
+ assertEquals(5, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("433", params.get("Q"));
+
+ }
+
+ public void testTypedParamUnassignedWellKnownShortIntegerTokenText() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x14);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(PARAM_UNASSIGNED | WSP_SHORT_INTEGER_MASK);
+ out.write("wdstechnology.com".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+ assertEquals(21, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("wdstechnology.com", params.get("unassigned/0x42"));
+
+ }
+
+ public void testTypedParamUnassignedWellKnownLongIntegerTokenText() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x15);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(0x01); // Short-length of well-known parameter token
+ out.write(PARAM_UNASSIGNED);
+ out.write("wdstechnology.com".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+ assertEquals(22, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("wdstechnology.com", params.get("unassigned/0x42"));
+ }
+
+ public void testDecodesReturnsFalse_WhenParamValueNotTerminated() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x15);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(0x01);
+ out.write(PARAM_UNASSIGNED);
+ out.write("wdstechnology.com".getBytes("US-ASCII"));
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertFalse(unit.decodeContentType(0));
+ }
+}
\ No newline at end of file
diff --git a/test-runner/src/android/test/IsolatedContext.java b/test-runner/src/android/test/IsolatedContext.java
index b483b82..bc00f68 100644
--- a/test-runner/src/android/test/IsolatedContext.java
+++ b/test-runner/src/android/test/IsolatedContext.java
@@ -87,6 +87,11 @@
}
@Override
+ public void unregisterReceiver(BroadcastReceiver receiver) {
+ // Ignore
+ }
+
+ @Override
public void sendBroadcast(Intent intent) {
mBroadcastIntents.add(intent);
}
diff --git a/test-runner/src/android/test/SingleLaunchActivityTestCase.java b/test-runner/src/android/test/SingleLaunchActivityTestCase.java
index b63b3ce..79c554a 100644
--- a/test-runner/src/android/test/SingleLaunchActivityTestCase.java
+++ b/test-runner/src/android/test/SingleLaunchActivityTestCase.java
@@ -75,7 +75,7 @@
protected void tearDown() throws Exception {
// If it is the last test case, call finish on the activity.
sTestCaseCounter --;
- if (sTestCaseCounter == 1) {
+ if (sTestCaseCounter == 0) {
sActivity.finish();
}
super.tearDown();
diff --git a/tests/BrowserTestPlugin/Android.mk b/tests/BrowserTestPlugin/Android.mk
deleted file mode 100644
index 968d9e6..0000000
--- a/tests/BrowserTestPlugin/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (C) 2009 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-TOP_LOCAL_PATH:= $(call my-dir)
-
-# Build application
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := BrowserTestPlugin
-
-LOCAL_JNI_SHARED_LIBRARIES := libtestplugin
-
-include $(BUILD_PACKAGE)
-
-# ============================================================
-
-# Also build all of the sub-targets under this one: the shared library.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/BrowserTestPlugin/AndroidManifest.xml b/tests/BrowserTestPlugin/AndroidManifest.xml
deleted file mode 100644
index f071ab6..0000000
--- a/tests/BrowserTestPlugin/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.testplugin"
- android:versionCode="1"
- android:versionName="1.0">
-
- <uses-permission android:name="android.webkit.permission.PLUGIN"/>
-
- <uses-sdk android:minSdkVersion="3" />
-
- <application android:icon="@drawable/browser_test_plugin"
- android:label="Browser Test Plugin">
- <service android:name="TestPlugin">
- <intent-filter>
- <action android:name="android.webkit.PLUGIN" />
- </intent-filter>
- </service>
- </application>
-
-</manifest>
diff --git a/tests/BrowserTestPlugin/jni/Android.mk b/tests/BrowserTestPlugin/jni/Android.mk
deleted file mode 100644
index 95a21e9..0000000
--- a/tests/BrowserTestPlugin/jni/Android.mk
+++ /dev/null
@@ -1,49 +0,0 @@
-##
-##
-## Copyright 2009, The Android Open Source Project
-##
-## Redistribution and use in source and binary forms, with or without
-## modification, are permitted provided that the following conditions
-## are met:
-## * Redistributions of source code must retain the above copyright
-## notice, this list of conditions and the following disclaimer.
-## * Redistributions in binary form must reproduce the above copyright
-## notice, this list of conditions and the following disclaimer in the
-## documentation and/or other materials provided with the distribution.
-##
-## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
-## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
-## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
-## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-##
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- main.cpp \
- PluginObject.cpp \
- event/EventPlugin.cpp \
-
-LOCAL_C_INCLUDES += \
- $(LOCAL_PATH) \
- $(LOCAL_PATH)/event \
- external/webkit/WebCore/bridge \
- external/webkit/WebCore/plugins \
- external/webkit/WebCore/platform/android/JavaVM \
- external/webkit/WebKit/android/plugins
-
-LOCAL_CFLAGS += -fvisibility=hidden
-LOCAL_PRELINK_MODULE := false
-
-LOCAL_MODULE := libtestplugin
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/BrowserTestPlugin/jni/PluginObject.cpp b/tests/BrowserTestPlugin/jni/PluginObject.cpp
deleted file mode 100644
index 68fca60..0000000
--- a/tests/BrowserTestPlugin/jni/PluginObject.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
- consideration of your agreement to the following terms, and your use, installation,
- modification or redistribution of this Apple software constitutes acceptance of these
- terms. If you do not agree with these terms, please do not use, install, modify or
- redistribute this Apple software.
-
- In consideration of your agreement to abide by the following terms, and subject to these
- terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
- this original Apple software (the "Apple Software"), to use, reproduce, modify and
- redistribute the Apple Software, with or without modifications, in source and/or binary
- forms; provided that if you redistribute the Apple Software in its entirety and without
- modifications, you must retain this notice and the following text and disclaimers in all
- such redistributions of the Apple Software. Neither the name, trademarks, service marks
- or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
- the Apple Software without specific prior written permission from Apple. Except as expressly
- stated in this notice, no other rights or licenses, express or implied, are granted by Apple
- herein, including but not limited to any patent rights that may be infringed by your
- derivative works or by other works in which the Apple Software may be incorporated.
-
- The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
- EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
- USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
-
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
- REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
- WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
- OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdlib.h>
-#include "main.h"
-#include "PluginObject.h"
-
-static void pluginInvalidate(NPObject *obj);
-static bool pluginHasProperty(NPObject *obj, NPIdentifier name);
-static bool pluginHasMethod(NPObject *obj, NPIdentifier name);
-static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant);
-static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant);
-static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
-static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
-static NPObject *pluginAllocate(NPP npp, NPClass *theClass);
-static void pluginDeallocate(NPObject *obj);
-static bool pluginRemoveProperty(NPObject *npobj, NPIdentifier name);
-static bool pluginEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count);
-
-
-
-static NPClass pluginClass = {
- NP_CLASS_STRUCT_VERSION,
- pluginAllocate,
- pluginDeallocate,
- pluginInvalidate,
- pluginHasMethod,
- pluginInvoke,
- pluginInvokeDefault,
- pluginHasProperty,
- pluginGetProperty,
- pluginSetProperty,
- pluginRemoveProperty,
- pluginEnumerate
-};
-
-NPClass *getPluginClass(void)
-{
- return &pluginClass;
-}
-
-static bool identifiersInitialized = false;
-
-#define ID_TESTFILE_PROPERTY 0
-#define NUM_PROPERTY_IDENTIFIERS 1
-
-static NPIdentifier pluginPropertyIdentifiers[NUM_PROPERTY_IDENTIFIERS];
-static const NPUTF8 *pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = {
- "testfile"
-};
-
-#define ID_GETTESTFILE_METHOD 0
-#define NUM_METHOD_IDENTIFIERS 1
-
-static NPIdentifier pluginMethodIdentifiers[NUM_METHOD_IDENTIFIERS];
-static const NPUTF8 *pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = {
- "getTestFile"
-};
-
-static void initializeIdentifiers(void)
-{
- browser->getstringidentifiers(pluginPropertyIdentifierNames, NUM_PROPERTY_IDENTIFIERS, pluginPropertyIdentifiers);
- browser->getstringidentifiers(pluginMethodIdentifierNames, NUM_METHOD_IDENTIFIERS, pluginMethodIdentifiers);
-}
-
-static bool pluginHasProperty(NPObject *obj, NPIdentifier name)
-{
- int i;
- for (i = 0; i < NUM_PROPERTY_IDENTIFIERS; i++)
- if (name == pluginPropertyIdentifiers[i])
- return true;
- return false;
-}
-
-static bool pluginHasMethod(NPObject *obj, NPIdentifier name)
-{
- int i;
- for (i = 0; i < NUM_METHOD_IDENTIFIERS; i++)
- if (name == pluginMethodIdentifiers[i])
- return true;
- return false;
-}
-
-static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant)
-{
- PluginObject *plugin = (PluginObject *)obj;
- if (name == pluginPropertyIdentifiers[ID_TESTFILE_PROPERTY]) {
- BOOLEAN_TO_NPVARIANT(true, *variant);
- return true;
- }
- return false;
-}
-
-static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant)
-{
- return false;
-}
-
-static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
-{
- PluginObject *plugin = (PluginObject *)obj;
- if (name == pluginMethodIdentifiers[ID_GETTESTFILE_METHOD]) {
- return true;
- }
- return false;
-}
-
-static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result)
-{
- return false;
-}
-
-static void pluginInvalidate(NPObject *obj)
-{
- // Release any remaining references to JavaScript objects.
-}
-
-static NPObject *pluginAllocate(NPP npp, NPClass *theClass)
-{
- PluginObject *newInstance = (PluginObject*) malloc(sizeof(PluginObject));
- newInstance->header._class = theClass;
- newInstance->header.referenceCount = 1;
-
- if (!identifiersInitialized) {
- identifiersInitialized = true;
- initializeIdentifiers();
- }
-
- newInstance->npp = npp;
-
- return &newInstance->header;
-}
-
-static void pluginDeallocate(NPObject *obj)
-{
- free(obj);
-}
-
-static bool pluginRemoveProperty(NPObject *npobj, NPIdentifier name)
-{
- return false;
-}
-
-static bool pluginEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count)
-{
- return false;
-}
diff --git a/tests/BrowserTestPlugin/jni/PluginObject.h b/tests/BrowserTestPlugin/jni/PluginObject.h
deleted file mode 100644
index a058d4a..0000000
--- a/tests/BrowserTestPlugin/jni/PluginObject.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
- consideration of your agreement to the following terms, and your use, installation,
- modification or redistribution of this Apple software constitutes acceptance of these
- terms. If you do not agree with these terms, please do not use, install, modify or
- redistribute this Apple software.
-
- In consideration of your agreement to abide by the following terms, and subject to these
- terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
- this original Apple software (the "Apple Software"), to use, reproduce, modify and
- redistribute the Apple Software, with or without modifications, in source and/or binary
- forms; provided that if you redistribute the Apple Software in its entirety and without
- modifications, you must retain this notice and the following text and disclaimers in all
- such redistributions of the Apple Software. Neither the name, trademarks, service marks
- or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
- the Apple Software without specific prior written permission from Apple. Except as expressly
- stated in this notice, no other rights or licenses, express or implied, are granted by Apple
- herein, including but not limited to any patent rights that may be infringed by your
- derivative works or by other works in which the Apple Software may be incorporated.
-
- The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
- EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
- USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
-
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
- REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
- WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
- OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef PluginObject__DEFINED
-#define PluginObject__DEFINED
-
-#include "main.h"
-
-class SubPlugin {
-public:
- SubPlugin(NPP inst) : m_inst(inst) {}
- virtual ~SubPlugin() {}
- virtual int16 handleEvent(const ANPEvent* evt) = 0;
-
- NPP inst() const { return m_inst; }
-
-private:
- NPP m_inst;
-};
-
-typedef struct PluginObject {
- NPObject header;
- NPP npp;
- NPWindow* window;
-
- SubPlugin* subPlugin;
-
-} PluginObject;
-
-NPClass *getPluginClass(void);
-
-#endif // PluginObject__DEFINED
diff --git a/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp b/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
deleted file mode 100644
index 2eff394..0000000
--- a/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "EventPlugin.h"
-#include "android_npapi.h"
-
-#include <stdio.h>
-#include <sys/time.h>
-#include <time.h>
-#include <math.h>
-#include <string.h>
-
-extern NPNetscapeFuncs* browser;
-extern ANPCanvasInterfaceV0 gCanvasI;
-extern ANPLogInterfaceV0 gLogI;
-extern ANPPaintInterfaceV0 gPaintI;
-extern ANPTypefaceInterfaceV0 gTypefaceI;
-
-///////////////////////////////////////////////////////////////////////////////
-
-EventPlugin::EventPlugin(NPP inst) : SubPlugin(inst) { }
-
-EventPlugin::~EventPlugin() { }
-
-void EventPlugin::drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip) {
-
- gLogI.log(kDebug_ANPLogType, " ------ %p drawing the plugin (%d,%d)",
- inst(), bitmap.width, bitmap.height);
-
- // get the plugin's dimensions according to the DOM
- PluginObject *obj = (PluginObject*) inst()->pdata;
- const int W = obj->window->width;
- const int H = obj->window->height;
-
- // compute the current zoom level
- const float zoomFactorW = static_cast<float>(bitmap.width) / W;
- const float zoomFactorH = static_cast<float>(bitmap.height) / H;
-
- // check to make sure the zoom level is uniform
- if (zoomFactorW + .01 < zoomFactorH && zoomFactorW - .01 > zoomFactorH)
- gLogI.log(kError_ANPLogType, " ------ %p zoom is out of sync (%f,%f)",
- inst(), zoomFactorW, zoomFactorH);
-
- // scale the variables based on the zoom level
- const int fontSize = (int)(zoomFactorW * 16);
- const int leftMargin = (int)(zoomFactorW * 10);
-
- // create and clip a canvas
- ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
-
- ANPRectF clipR;
- clipR.left = clip.left;
- clipR.top = clip.top;
- clipR.right = clip.right;
- clipR.bottom = clip.bottom;
- gCanvasI.clipRect(canvas, &clipR);
-
- gCanvasI.drawColor(canvas, 0xFFFFFFFF);
-
- // configure the paint
- ANPPaint* paint = gPaintI.newPaint();
- gPaintI.setFlags(paint, gPaintI.getFlags(paint) | kAntiAlias_ANPPaintFlag);
- gPaintI.setColor(paint, 0xFF0000FF);
- gPaintI.setTextSize(paint, fontSize);
-
- // configure the font
- ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
- gPaintI.setTypeface(paint, tf);
- gTypefaceI.unref(tf);
-
- // retrieve the font metrics
- ANPFontMetrics fm;
- gPaintI.getFontMetrics(paint, &fm);
-
- // write text on the canvas
- const char c[] = "Browser Test Plugin";
- gCanvasI.drawText(canvas, c, sizeof(c)-1, leftMargin, -fm.fTop, paint);
-
- // clean up variables
- gPaintI.deletePaint(paint);
- gCanvasI.deleteCanvas(canvas);
-}
-
-void EventPlugin::printToDiv(const char* text, int length) {
- // Get the plugin's DOM object
- NPObject* windowObject = NULL;
- browser->getvalue(inst(), NPNVWindowNPObject, &windowObject);
-
- if (!windowObject)
- gLogI.log(kError_ANPLogType, " ------ %p Unable to retrieve DOM Window", inst());
-
- // create a string (JS code) that is stored in memory allocated by the browser
- const char* jsBegin = "var outputDiv = document.getElementById('eventOutput'); outputDiv.innerHTML += ' ";
- const char* jsEnd = "';";
-
- // allocate memory and configure pointers
- int totalLength = strlen(jsBegin) + length + strlen(jsEnd);
- char* beginMem = (char*)browser->memalloc(totalLength);
- char* middleMem = beginMem + strlen(jsBegin);
- char* endMem = middleMem + length;
-
- // copy into the allocated memory
- memcpy(beginMem, jsBegin, strlen(jsBegin));
- memcpy(middleMem, text, length);
- memcpy(endMem, jsEnd, strlen(jsEnd));
-
- gLogI.log(kDebug_ANPLogType, "text: %.*s\n", totalLength, (char*)beginMem);
-
- // execute the javascript in the plugin's DOM object
- NPString script = { (char*)beginMem, totalLength };
- NPVariant scriptVariant;
- if (!browser->evaluate(inst(), windowObject, &script, &scriptVariant))
- gLogI.log(kError_ANPLogType, " ------ %p Unable to eval the JS.", inst());
-
- // free the memory allocated within the browser
- browser->memfree(beginMem);
-}
-
-int16 EventPlugin::handleEvent(const ANPEvent* evt) {
- switch (evt->eventType) {
-
- case kDraw_ANPEventType: {
- switch (evt->data.draw.model) {
- case kBitmap_ANPDrawingModel:
- drawPlugin(evt->data.draw.data.bitmap, evt->data.draw.clip);
- return 1;
- default:
- break; // unknown drawing model
- }
- }
- case kLifecycle_ANPEventType:
- switch (evt->data.lifecycle.action) {
- case kOnLoad_ANPLifecycleAction: {
- char msg[] = "lifecycle-onLoad";
- printToDiv(msg, strlen(msg));
- break;
- }
- case kGainFocus_ANPLifecycleAction: {
- char msg[] = "lifecycle-gainFocus";
- printToDiv(msg, strlen(msg));
- break;
- }
- case kLoseFocus_ANPLifecycleAction: {
- char msg[] = "lifecycle-loseFocus";
- printToDiv(msg, strlen(msg));
- break;
- }
- }
- return 1;
- case kTouch_ANPEventType:
- gLogI.log(kError_ANPLogType, " ------ %p the plugin did not request touch events", inst());
- break;
- case kKey_ANPEventType:
- gLogI.log(kError_ANPLogType, " ------ %p the plugin did not request key events", inst());
- break;
- default:
- break;
- }
- return 0; // unknown or unhandled event
-}
diff --git a/tests/BrowserTestPlugin/jni/event/EventPlugin.h b/tests/BrowserTestPlugin/jni/event/EventPlugin.h
deleted file mode 100644
index 88b7c9d..0000000
--- a/tests/BrowserTestPlugin/jni/event/EventPlugin.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "PluginObject.h"
-
-#ifndef eventPlugin__DEFINED
-#define eventPlugin__DEFINED
-
-class EventPlugin : public SubPlugin {
-public:
- EventPlugin(NPP inst);
- virtual ~EventPlugin();
- virtual int16 handleEvent(const ANPEvent* evt);
-
-private:
- void drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip);
- void printToDiv(const char* text, int length);
-};
-
-#endif // eventPlugin__DEFINED
diff --git a/tests/BrowserTestPlugin/jni/main.cpp b/tests/BrowserTestPlugin/jni/main.cpp
deleted file mode 100644
index 402a7e2..0000000
--- a/tests/BrowserTestPlugin/jni/main.cpp
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include "android_npapi.h"
-#include "main.h"
-#include "PluginObject.h"
-#include "EventPlugin.h"
-
-NPNetscapeFuncs* browser;
-#define EXPORT __attribute__((visibility("default")))
-
-NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
- char* argn[], char* argv[], NPSavedData* saved);
-NPError NPP_Destroy(NPP instance, NPSavedData** save);
-NPError NPP_SetWindow(NPP instance, NPWindow* window);
-NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
- NPBool seekable, uint16* stype);
-NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason);
-int32 NPP_WriteReady(NPP instance, NPStream* stream);
-int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len,
- void* buffer);
-void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname);
-void NPP_Print(NPP instance, NPPrint* platformPrint);
-int16 NPP_HandleEvent(NPP instance, void* event);
-void NPP_URLNotify(NPP instance, const char* URL, NPReason reason,
- void* notifyData);
-NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value);
-NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value);
-
-extern "C" {
-EXPORT NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env);
-EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value);
-EXPORT const char* NP_GetMIMEDescription(void);
-EXPORT void NP_Shutdown(void);
-};
-
-ANPAudioTrackInterfaceV0 gSoundI;
-ANPBitmapInterfaceV0 gBitmapI;
-ANPCanvasInterfaceV0 gCanvasI;
-ANPLogInterfaceV0 gLogI;
-ANPPaintInterfaceV0 gPaintI;
-ANPPathInterfaceV0 gPathI;
-ANPTypefaceInterfaceV0 gTypefaceI;
-ANPWindowInterfaceV0 gWindowI;
-
-#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
-
-NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env)
-{
- // Make sure we have a function table equal or larger than we are built against.
- if (browserFuncs->size < sizeof(NPNetscapeFuncs)) {
- return NPERR_GENERIC_ERROR;
- }
-
- // Copy the function table (structure)
- browser = (NPNetscapeFuncs*) malloc(sizeof(NPNetscapeFuncs));
- memcpy(browser, browserFuncs, sizeof(NPNetscapeFuncs));
-
- // Build the plugin function table
- pluginFuncs->version = 11;
- pluginFuncs->size = sizeof(pluginFuncs);
- pluginFuncs->newp = NPP_New;
- pluginFuncs->destroy = NPP_Destroy;
- pluginFuncs->setwindow = NPP_SetWindow;
- pluginFuncs->newstream = NPP_NewStream;
- pluginFuncs->destroystream = NPP_DestroyStream;
- pluginFuncs->asfile = NPP_StreamAsFile;
- pluginFuncs->writeready = NPP_WriteReady;
- pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write;
- pluginFuncs->print = NPP_Print;
- pluginFuncs->event = NPP_HandleEvent;
- pluginFuncs->urlnotify = NPP_URLNotify;
- pluginFuncs->getvalue = NPP_GetValue;
- pluginFuncs->setvalue = NPP_SetValue;
-
- static const struct {
- NPNVariable v;
- uint32_t size;
- ANPInterface* i;
- } gPairs[] = {
- { kCanvasInterfaceV0_ANPGetValue, sizeof(gCanvasI), &gCanvasI },
- { kLogInterfaceV0_ANPGetValue, sizeof(gLogI), &gLogI },
- { kPaintInterfaceV0_ANPGetValue, sizeof(gPaintI), &gPaintI },
- { kTypefaceInterfaceV0_ANPGetValue, sizeof(gTypefaceI), &gTypefaceI },
- };
- for (size_t i = 0; i < ARRAY_COUNT(gPairs); i++) {
- gPairs[i].i->inSize = gPairs[i].size;
- NPError err = browser->getvalue(NULL, gPairs[i].v, gPairs[i].i);
- if (err) {
- return err;
- }
- }
-
- return NPERR_NO_ERROR;
-}
-
-void NP_Shutdown(void)
-{
-
-}
-
-const char *NP_GetMIMEDescription(void)
-{
- return "application/x-browsertestplugin:btp:Android Browser Test Plugin";
-}
-
-NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
- char* argn[], char* argv[], NPSavedData* saved)
-{
-
-
- gLogI.log(kDebug_ANPLogType, "creating plugin");
-
- PluginObject *obj = NULL;
-
- // Scripting functions appeared in NPAPI version 14
- if (browser->version >= 14) {
- instance->pdata = browser->createobject (instance, getPluginClass());
- obj = static_cast<PluginObject*>(instance->pdata);
- memset(obj, 0, sizeof(*obj));
- } else {
- return NPERR_GENERIC_ERROR;
- }
-
- // select the drawing model
- ANPDrawingModel model = kBitmap_ANPDrawingModel;
-
- // notify the plugin API of the drawing model we wish to use. This must be
- // done prior to creating certain subPlugin objects (e.g. surfaceViews)
- NPError err = browser->setvalue(instance, kRequestDrawingModel_ANPSetValue,
- reinterpret_cast<void*>(model));
- if (err) {
- gLogI.log(kError_ANPLogType, "request model %d err %d", model, err);
- return err;
- }
-
- // create the sub-plugin
- obj->subPlugin = new EventPlugin(instance);
-
- return NPERR_NO_ERROR;
-}
-
-NPError NPP_Destroy(NPP instance, NPSavedData** save)
-{
- PluginObject *obj = (PluginObject*) instance->pdata;
- if (obj) {
- delete obj->subPlugin;
- browser->releaseobject(&obj->header);
- }
-
- return NPERR_NO_ERROR;
-}
-
-NPError NPP_SetWindow(NPP instance, NPWindow* window)
-{
- PluginObject *obj = (PluginObject*) instance->pdata;
-
- // Do nothing if browser didn't support NPN_CreateObject which would have created the PluginObject.
- if (obj != NULL) {
- obj->window = window;
- }
-
- return NPERR_NO_ERROR;
-}
-
-NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
-{
- *stype = NP_ASFILEONLY;
- return NPERR_NO_ERROR;
-}
-
-NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason)
-{
- return NPERR_NO_ERROR;
-}
-
-int32 NPP_WriteReady(NPP instance, NPStream* stream)
-{
- return 0;
-}
-
-int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer)
-{
- return 0;
-}
-
-void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
-{
-}
-
-void NPP_Print(NPP instance, NPPrint* platformPrint)
-{
-}
-
-int16 NPP_HandleEvent(NPP instance, void* event)
-{
- PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
- const ANPEvent* evt = reinterpret_cast<const ANPEvent*>(event);
-
- if(!obj->subPlugin) {
- gLogI.log(kError_ANPLogType, "the sub-plugin is null.");
- return 0; // unknown or unhandled event
- }
- else {
- return obj->subPlugin->handleEvent(evt);
- }
-}
-
-void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
-{
-}
-
-EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value) {
-
- if (variable == NPPVpluginNameString) {
- const char **str = (const char **)value;
- *str = "Browser Test Plugin";
- return NPERR_NO_ERROR;
- }
-
- if (variable == NPPVpluginDescriptionString) {
- const char **str = (const char **)value;
- *str = "Description of Browser Test Plugin";
- return NPERR_NO_ERROR;
- }
-
- return NPERR_GENERIC_ERROR;
-}
-
-NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value)
-{
- if (variable == NPPVpluginScriptableNPObject) {
- void **v = (void **)value;
- PluginObject *obj = (PluginObject*) instance->pdata;
-
- if (obj)
- browser->retainobject((NPObject*)obj);
-
- *v = obj;
- return NPERR_NO_ERROR;
- }
-
- return NPERR_GENERIC_ERROR;
-}
-
-NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
-{
- return NPERR_GENERIC_ERROR;
-}
-
diff --git a/tests/BrowserTestPlugin/jni/main.h b/tests/BrowserTestPlugin/jni/main.h
deleted file mode 100644
index e6e8c73..0000000
--- a/tests/BrowserTestPlugin/jni/main.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <npapi.h>
-#include <npfunctions.h>
-#include <npruntime.h>
-#include "android_npapi.h"
-
-extern NPNetscapeFuncs* browser;
diff --git a/tests/BrowserTestPlugin/res/drawable/browser_test_plugin.png b/tests/BrowserTestPlugin/res/drawable/browser_test_plugin.png
deleted file mode 100755
index 47c79d1..0000000
--- a/tests/BrowserTestPlugin/res/drawable/browser_test_plugin.png
+++ /dev/null
Binary files differ
diff --git a/tests/CoreTests/android/AndroidManifest.xml b/tests/CoreTests/android/AndroidManifest.xml
index f02673c..8331f0c 100644
--- a/tests/CoreTests/android/AndroidManifest.xml
+++ b/tests/CoreTests/android/AndroidManifest.xml
@@ -24,6 +24,7 @@
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
<uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- location test permissions -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
diff --git a/tests/CoreTests/android/core/HttpHeaderTest.java b/tests/CoreTests/android/core/HttpHeaderTest.java
new file mode 100644
index 0000000..a5d48578
--- /dev/null
+++ b/tests/CoreTests/android/core/HttpHeaderTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.core;
+
+import android.test.AndroidTestCase;
+import org.apache.http.util.CharArrayBuffer;
+
+import android.net.http.Headers;
+
+public class HttpHeaderTest extends AndroidTestCase {
+
+ static final String LAST_MODIFIED = "Last-Modified: Fri, 18 Jun 2010 09:56:47 GMT";
+ static final String CACHE_CONTROL_MAX_AGE = "Cache-Control:max-age=15";
+ static final String CACHE_CONTROL_PRIVATE = "Cache-Control: private";
+
+ /**
+ * Tests that cache control header supports multiple instances of the header,
+ * according to HTTP specification.
+ *
+ * The HTTP specification states the following about the fields:
+ * Multiple message-header fields with the same field-name MAY be present
+ * in a message if and only if the entire field-value for that header field
+ * is defined as a comma-separated list [i.e., #(values)]. It MUST be
+ * possible to combine the multiple header fields into one "field-name:
+ * field-value" pair, without changing the semantics of the message, by
+ * appending each subsequent field-value to the first, each separated by a
+ * comma. The order in which header fields with the same field-name are
+ * received is therefore significant to the interpretation of the combined
+ * field value, and thus a proxy MUST NOT change the order of these field
+ * values when a message is forwarded.
+ */
+ public void testCacheControl() throws Exception {
+ Headers h = new Headers();
+ CharArrayBuffer buffer = new CharArrayBuffer(64);
+
+ buffer.append(CACHE_CONTROL_MAX_AGE);
+ h.parseHeader(buffer);
+
+ buffer.clear();
+ buffer.append(LAST_MODIFIED);
+ h.parseHeader(buffer);
+ assertEquals("max-age=15", h.getCacheControl());
+
+ buffer.clear();
+ buffer.append(CACHE_CONTROL_PRIVATE);
+ h.parseHeader(buffer);
+ assertEquals("max-age=15,private", h.getCacheControl());
+ }
+}
diff --git a/tests/CoreTests/android/core/ProxyTest.java b/tests/CoreTests/android/core/ProxyTest.java
new file mode 100644
index 0000000..12acfe8
--- /dev/null
+++ b/tests/CoreTests/android/core/ProxyTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.core;
+
+import org.apache.http.HttpHost;
+
+import android.content.Context;
+import android.net.Proxy;
+import android.test.AndroidTestCase;
+
+/**
+ * Proxy tests
+ */
+public class ProxyTest extends AndroidTestCase {
+ private Context mContext;
+ private HttpHost mHttpHost;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mContext = getContext();
+ mHttpHost = null;
+ String proxyHost = Proxy.getHost(mContext);
+ int proxyPort = Proxy.getPort(mContext);
+ if (proxyHost != null) {
+ mHttpHost = new HttpHost(proxyHost, proxyPort, "http");
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Bad url parameter should not cause any exception.
+ */
+ public void testProxyGetPreferredHttpHost_UrlBad() throws Exception {
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, null));
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, ""));
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "bad:"));
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "bad"));
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "bad:\\"));
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "bad://#"));
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "://#"));
+ }
+
+ /**
+ * Proxy (if available) should be returned when url parameter is not localhost.
+ */
+ public void testProxyGetPreferredHttpHost_UrlNotlLocalhost() throws Exception {
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "http://"));
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "http://example.com"));
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "http://example.com/"));
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "http://192.168.0.1/"));
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "file:///foo/bar"));
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "rtsp://example.com"));
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "rtsp://example.com/"));
+ assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "javascript:alert(1)"));
+ }
+
+ /**
+ * No proxy should be returned when url parameter is localhost.
+ */
+ public void testProxyGetPreferredHttpHost_UrlLocalhost() throws Exception {
+ assertNull(Proxy.getPreferredHttpHost(mContext, "http://localhost"));
+ assertNull(Proxy.getPreferredHttpHost(mContext, "http://localhost/"));
+ assertNull(Proxy.getPreferredHttpHost(mContext, "http://localhost/hej.html"));
+ assertNull(Proxy.getPreferredHttpHost(mContext, "http://127.0.0.1"));
+ assertNull(Proxy.getPreferredHttpHost(mContext, "http://127.0.0.1/"));
+ assertNull(Proxy.getPreferredHttpHost(mContext, "http://127.0.0.1/hej.html"));
+ assertNull(Proxy.getPreferredHttpHost(mContext, "http://127.0.0.1:80/"));
+ assertNull(Proxy.getPreferredHttpHost(mContext, "http://127.0.0.1:8080/"));
+ assertNull(Proxy.getPreferredHttpHost(mContext, "rtsp://127.0.0.1/"));
+ assertNull(Proxy.getPreferredHttpHost(mContext, "rtsp://localhost/"));
+ assertNull(Proxy.getPreferredHttpHost(mContext, "https://localhost/"));
+ }
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
index e741177..73d7363 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
@@ -31,6 +31,7 @@
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.os.Bundle;
+import android.os.Environment;
public abstract class FileList extends ListActivity
@@ -179,10 +180,9 @@
getListView().setSelection(mFocusIndex);
}
- protected void setupPath()
- {
- mPath = "/sdcard/android/layout_tests";
- mBaseLength = mPath.length();
+ protected void setupPath() {
+ mPath = Environment.getExternalStorageDirectory() + "/android/layout_tests";
+ mBaseLength = mPath.length();
}
protected String mPath;
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
index 322b0d2..6cfce41 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
@@ -18,6 +18,7 @@
import com.android.dumprendertree.forwarder.ForwardService;
+import android.os.Environment;
import android.util.Log;
import java.io.BufferedOutputStream;
@@ -32,11 +33,17 @@
public class FsUtils {
private static final String LOGTAG = "FsUtils";
- static final String HTTP_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/";
- static final String HTTPS_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/ssl/";
- static final String HTTP_LOCAL_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/local/";
- static final String HTTP_MEDIA_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/media/";
- static final String HTTP_WML_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/wml/";
+ static final String EXTERNAL_DIR = Environment.getExternalStorageDirectory().toString();
+ static final String HTTP_TESTS_PREFIX =
+ EXTERNAL_DIR + "/android/layout_tests/http/tests/";
+ static final String HTTPS_TESTS_PREFIX =
+ EXTERNAL_DIR + "/android/layout_tests/http/tests/ssl/";
+ static final String HTTP_LOCAL_TESTS_PREFIX =
+ EXTERNAL_DIR + "/android/layout_tests/http/tests/local/";
+ static final String HTTP_MEDIA_TESTS_PREFIX =
+ EXTERNAL_DIR + "/android/layout_tests/http/tests/media/";
+ static final String HTTP_WML_TESTS_PREFIX =
+ EXTERNAL_DIR + "/android/layout_tests/http/tests/wml/";
private FsUtils() {
//no creation of instances
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index 042158a..9ccf549 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -18,12 +18,12 @@
import com.android.dumprendertree.TestShellActivity.DumpDataType;
import com.android.dumprendertree.forwarder.AdbUtils;
-import com.android.dumprendertree.forwarder.ForwardServer;
import com.android.dumprendertree.forwarder.ForwardService;
import android.app.Instrumentation;
import android.content.Intent;
import android.os.Bundle;
+import android.os.Environment;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
@@ -92,10 +92,11 @@
public MyTestRecorder(boolean resume) {
try {
- File resultsPassedFile = new File("/sdcard/layout_tests_passed.txt");
- File resultsFailedFile = new File("/sdcard/layout_tests_failed.txt");
- File resultsIgnoreResultFile = new File("/sdcard/layout_tests_ignored.txt");
- File noExpectedResultFile = new File("/sdcard/layout_tests_nontext.txt");
+ File externalDir = Environment.getExternalStorageDirectory();
+ File resultsPassedFile = new File(externalDir, "layout_tests_passed.txt");
+ File resultsFailedFile = new File(externalDir, "layout_tests_failed.txt");
+ File resultsIgnoreResultFile = new File(externalDir, "layout_tests_ignored.txt");
+ File noExpectedResultFile = new File(externalDir, "layout_tests_nontext.txt");
mBufferedOutputPassedStream =
new BufferedOutputStream(new FileOutputStream(resultsPassedFile, resume));
@@ -128,11 +129,12 @@
private static final String LOGTAG = "LayoutTests";
static final int DEFAULT_TIMEOUT_IN_MILLIS = 5000;
- static final String LAYOUT_TESTS_ROOT = "/sdcard/android/layout_tests/";
- static final String LAYOUT_TESTS_RESULT_DIR = "/sdcard/android/layout_tests_results/";
- static final String ANDROID_EXPECTED_RESULT_DIR = "/sdcard/android/expected_results/";
- static final String LAYOUT_TESTS_LIST_FILE = "/sdcard/android/layout_tests_list.txt";
- static final String TEST_STATUS_FILE = "/sdcard/android/running_test.txt";
+ static final String EXTERNAL_DIR = Environment.getExternalStorageDirectory().toString();
+ static final String LAYOUT_TESTS_ROOT = EXTERNAL_DIR + "/android/layout_tests/";
+ static final String LAYOUT_TESTS_RESULT_DIR = EXTERNAL_DIR + "/android/layout_tests_results/";
+ static final String ANDROID_EXPECTED_RESULT_DIR = EXTERNAL_DIR + "/android/expected_results/";
+ static final String LAYOUT_TESTS_LIST_FILE = EXTERNAL_DIR + "/android/layout_tests_list.txt";
+ static final String TEST_STATUS_FILE = EXTERNAL_DIR + "/android/running_test.txt";
static final String LAYOUT_TESTS_RESULTS_REFERENCE_FILES[] = {
"results/layout_tests_passed.txt",
"results/layout_tests_failed.txt",
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
index 2ef342f..9352f39 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.Debug;
+import android.os.Environment;
import android.os.Process;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
@@ -35,7 +36,8 @@
public class LoadTestsAutoTest extends ActivityInstrumentationTestCase2<TestShellActivity> {
private final static String LOGTAG = "LoadTest";
- private final static String LOAD_TEST_RESULT = "/sdcard/load_test_result.txt";
+ private final static String LOAD_TEST_RESULT =
+ Environment.getExternalStorageDirectory() + "/load_test_result.txt";
private boolean mFinished;
static final String LOAD_TEST_RUNNER_FILES[] = {
"run_page_cycler.py"
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
index 82671eb..9c4b57256 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
@@ -18,6 +18,7 @@
import android.content.Intent;
import android.os.Bundle;
+import android.os.Environment;
import android.util.Log;
import java.io.BufferedOutputStream;
@@ -28,7 +29,8 @@
private static final int MENU_START = 0x01;
private static String LOGTAG = "MenuActivity";
- static final String LAYOUT_TESTS_LIST_FILE = "/sdcard/android/layout_tests_list.txt";
+ static final String LAYOUT_TESTS_LIST_FILE =
+ Environment.getExternalStorageDirectory() + "/android/layout_tests_list.txt";
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
index 9bc0962..d146fc7 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.content.Intent;
+import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.test.ActivityInstrumentationTestCase2;
@@ -37,10 +38,16 @@
private static final String LOGTAG = "ReliabilityTest";
private static final String PKG_NAME = "com.android.dumprendertree";
- private static final String TEST_LIST_FILE = "/sdcard/android/reliability_tests_list.txt";
- private static final String TEST_STATUS_FILE = "/sdcard/android/reliability_running_test.txt";
- private static final String TEST_TIMEOUT_FILE = "/sdcard/android/reliability_timeout_test.txt";
- private static final String TEST_LOAD_TIME_FILE = "/sdcard/android/reliability_load_time.txt";
+ private static final String EXTERNAL_DIR =
+ Environment.getExternalStorageDirectory().toString();
+ private static final String TEST_LIST_FILE = EXTERNAL_DIR +
+ "/android/reliability_tests_list.txt";
+ private static final String TEST_STATUS_FILE = EXTERNAL_DIR +
+ "/android/reliability_running_test.txt";
+ private static final String TEST_TIMEOUT_FILE = EXTERNAL_DIR +
+ "/android/reliability_timeout_test.txt";
+ private static final String TEST_LOAD_TIME_FILE = EXTERNAL_DIR +
+ "/android/reliability_load_time.txt";
private static final String TEST_DONE = "#DONE";
static final String RELIABILITY_TEST_RUNNER_FILES[] = {
"run_reliability_tests.py"
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 81d5b08..7475719 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -30,6 +30,7 @@
import android.graphics.Bitmap.Config;
import android.net.http.SslError;
import android.os.Bundle;
+import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
@@ -862,7 +863,8 @@
static final String SAVE_IMAGE = "SaveImage";
static final int DRAW_RUNS = 5;
- static final String DRAW_TIME_LOG = "/sdcard/android/page_draw_time.txt";
+ static final String DRAW_TIME_LOG = Environment.getExternalStorageDirectory() +
+ "/android/page_draw_time.txt";
private boolean mGeolocationPermissionSet;
private boolean mGeolocationPermission;
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardService.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardService.java
index 8b7de6e..25dd04fd 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardService.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardService.java
@@ -21,6 +21,7 @@
import java.io.FileReader;
import java.io.IOException;
+import android.os.Environment;
import android.util.Log;
public class ForwardService {
@@ -33,7 +34,8 @@
private static final String DEFAULT_TEST_HOST = "android-browser-test.mtv.corp.google.com";
- private static final String FORWARD_HOST_CONF = "/sdcard/drt_forward_host.txt";
+ private static final String FORWARD_HOST_CONF =
+ Environment.getExternalStorageDirectory() + "/drt_forward_host.txt";
private ForwardService() {
int addr = getForwardHostAddr();
diff --git a/tests/LocationTracker/src/com/android/locationtracker/TrackerActivity.java b/tests/LocationTracker/src/com/android/locationtracker/TrackerActivity.java
index 98d0a50..4cfdf6c 100644
--- a/tests/LocationTracker/src/com/android/locationtracker/TrackerActivity.java
+++ b/tests/LocationTracker/src/com/android/locationtracker/TrackerActivity.java
@@ -28,6 +28,7 @@
import android.database.Cursor;
import android.location.LocationManager;
import android.os.Bundle;
+import android.os.Environment;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -210,12 +211,11 @@
}
private String getUniqueFileName(String ext) {
- File dir = new File("/sdcard/locationtracker");
+ File dir = new File(Environment.getExternalStorageDirectory() + "/locationtracker");
if (!dir.exists()) {
dir.mkdir();
}
- return "/sdcard/locationtracker/tracking-" +
- DateUtils.getCurrentTimestamp() + "." + ext;
+ return dir + "/tracking-" + DateUtils.getCurrentTimestamp() + "." + ext;
}
private void launchSettings() {
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index cfce7bd..b793d62 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -16,24 +16,19 @@
package com.android.statusbartest;
-import android.app.ListActivity;
import android.app.PendingIntent;
-import android.widget.ArrayAdapter;
-import android.view.View;
-import android.widget.ListView;
import android.content.Context;
import android.content.ContentResolver;
import android.content.Intent;
import android.app.Notification;
import android.app.NotificationManager;
+import android.os.Environment;
import android.os.Vibrator;
-import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.net.Uri;
import android.os.SystemClock;
import android.widget.RemoteViews;
-import android.widget.TextView;
import android.os.PowerManager;
public class NotificationTestList extends TestActivity
@@ -70,7 +65,8 @@
pm.goToSleep(SystemClock.uptimeMillis());
Notification n = new Notification();
- n.sound = Uri.parse("file:///sdcard/virtual-void.mp3");
+ n.sound = Uri.parse("file://" + Environment.getExternalStorageDirectory() +
+ "/virtual-void.mp3");
Log.d(TAG, "n.sound=" + n.sound);
mNM.notify(1, n);
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index e4f447e..2b2ec7b 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -685,13 +685,11 @@
if (*c != 0) return false;
if (c-val == 0 || c-val > 3) return false;
- int d = atoi(val);
- if (d != 0) {
- if (out) out->mnc = d;
- return true;
+ if (out) {
+ out->mnc = atoi(val);
}
- return false;
+ return true;
}
/*
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index c5aa573..15570e4 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -45,7 +45,7 @@
mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL),
- mMaxResVersion(NULL), mDebugMode(false), mProduct(NULL),
+ mMaxResVersion(NULL), mDebugMode(false), mNonConstantId(false), mProduct(NULL),
mArgc(0), mArgv(NULL)
{}
~Bundle(void) {}
@@ -139,6 +139,8 @@
void setMaxResVersion(const char * val) { mMaxResVersion = val; }
bool getDebugMode() { return mDebugMode; }
void setDebugMode(bool val) { mDebugMode = val; }
+ bool getNonConstantId() { return mNonConstantId; }
+ void setNonConstantId(bool val) { mNonConstantId = val; }
const char* getProduct() const { return mProduct; }
void setProduct(const char * val) { mProduct = val; }
@@ -239,6 +241,7 @@
const char* mCustomPackage;
const char* mMaxResVersion;
bool mDebugMode;
+ bool mNonConstantId;
const char* mProduct;
/* file specification */
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 739b01f..266a02f 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -160,7 +160,11 @@
" product variants\n"
" --utf16\n"
" changes default encoding for resources to UTF-16. Only useful when API\n"
- " level is set to 7 or higher where the default encoding is UTF-8.\n");
+ " level is set to 7 or higher where the default encoding is UTF-8.\n"
+ " --non-constant-id\n"
+ " Make the resources ID non constant. This is required to make an R java class\n"
+ " that does not contain the final value but is used to make reusable compiled\n"
+ " libraries that need to access resources.\n");
}
/*
@@ -497,6 +501,8 @@
goto bail;
}
bundle.setProduct(argv[0]);
+ } else if (strcmp(cp, "-non-constant-id") == 0) {
+ bundle.setNonConstantId(true);
} else {
fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
wantUsage = true;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index c8ba904..0a4f24f 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1655,7 +1655,8 @@
static status_t writeSymbolClass(
FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
- const sp<AaptSymbols>& symbols, const String8& className, int indent)
+ const sp<AaptSymbols>& symbols, const String8& className, int indent,
+ bool nonConstantId)
{
fprintf(fp, "%spublic %sfinal class %s {\n",
getIndentSpace(indent),
@@ -1665,6 +1666,10 @@
size_t i;
status_t err = NO_ERROR;
+ const char * id_format = nonConstantId ?
+ "%spublic static int %s=0x%08x;\n" :
+ "%spublic static final int %s=0x%08x;\n";
+
size_t N = symbols->getSymbols().size();
for (i=0; i<N; i++) {
const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
@@ -1717,7 +1722,7 @@
if (deprecated) {
fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
}
- fprintf(fp, "%spublic static final int %s=0x%08x;\n",
+ fprintf(fp, id_format,
getIndentSpace(indent),
String8(name).string(), (int)sym.int32Val);
}
@@ -1768,7 +1773,7 @@
if (nclassName == "styleable") {
styleableSymbols = nsymbols;
} else {
- err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent);
+ err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId);
}
if (err != NO_ERROR) {
return err;
@@ -1839,7 +1844,7 @@
"\n"
"package %s;\n\n", package.string());
- status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0);
+ status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0, bundle->getNonConstantId());
if (err != NO_ERROR) {
return err;
}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 29644a6..818c3c6 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1322,6 +1322,22 @@
}
}
} else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) {
+ // Check whether these strings need valid formats.
+ // (simplified form of what string16 does above)
+ size_t n = block.getAttributeCount();
+ for (size_t i = 0; i < n; i++) {
+ size_t length;
+ const uint16_t* attr = block.getAttributeName(i, &length);
+ if (strcmp16(attr, translatable16.string()) == 0
+ || strcmp16(attr, formatted16.string()) == 0) {
+ const uint16_t* value = block.getAttributeStringValue(i, &length);
+ if (strcmp16(value, false16.string()) == 0) {
+ curIsFormatted = false;
+ break;
+ }
+ }
+ }
+
curTag = &string_array16;
curType = array16;
curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
@@ -1724,13 +1740,6 @@
// If a parent is explicitly specified, set it.
if (bagParent.size() > 0) {
- String16 curPar = e->getParent();
- if (curPar.size() > 0 && curPar != bagParent) {
- sourcePos.error("Conflicting parents specified, was '%s', now '%s'\n",
- String8(e->getParent()).string(),
- String8(bagParent).string());
- return UNKNOWN_ERROR;
- }
e->setParent(bagParent);
}
@@ -1778,13 +1787,6 @@
// If a parent is explicitly specified, set it.
if (bagParent.size() > 0) {
- String16 curPar = e->getParent();
- if (curPar.size() > 0 && curPar != bagParent) {
- sourcePos.error("Conflicting parents specified, was '%s', now '%s'\n",
- String8(e->getParent()).string(),
- String8(bagParent).string());
- return UNKNOWN_ERROR;
- }
e->setParent(bagParent);
}
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 8551b0f..c0d7427 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -451,13 +451,15 @@
printf("=?0x%x", (int)value.data);
} else if (value.dataType == Res_value::TYPE_STRING) {
printf("=\"%s\"",
- String8(block->getAttributeStringValue(i, &len)).string());
+ ResTable::normalizeForOutput(String8(block->getAttributeStringValue(i,
+ &len)).string()).string());
} else {
printf("=(type 0x%x)0x%x", (int)value.dataType, (int)value.data);
}
const char16_t* val = block->getAttributeStringValue(i, &len);
if (val != NULL) {
- printf(" (Raw: \"%s\")", String8(val).string());
+ printf(" (Raw: \"%s\")", ResTable::normalizeForOutput(String8(val).string()).
+ string());
}
printf("\n");
}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 27e6a72..bf2d033 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -322,6 +322,7 @@
private static String LS = System.getProperty("line.separator");
private static String[] sDnsPropNames;
+ private Runnable mReleaseWakeLockCallback;
/**
* Keep track of whether we last told the battery stats we had started.
@@ -2449,11 +2450,11 @@
setBluetoothCoexistenceMode(
WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
}
-
+
powerMode = getPowerMode();
if (powerMode < 0) {
- // Handle the case where supplicant driver does not support
- // getPowerModeCommand.
+ // Handle the case where supplicant driver does not support
+ // getPowerModeCommand.
powerMode = DRIVER_POWER_MODE_AUTO;
}
if (powerMode != DRIVER_POWER_MODE_ACTIVE) {