diff options
419 files changed, 31638 insertions, 2662 deletions
@@ -185,6 +185,7 @@ LOCAL_SRC_FILES += \ 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 @@ status_t BootAnimation::readyToRun() { 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 @@ public final class Pm { 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 @@ static struct { { 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 class AlertDialog extends Dialog implements DialogInterface { 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 @@ public class AlertDialog extends Dialog implements DialogInterface { * 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 @@ public class ApplicationErrorReport implements Parcelable { /** * 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.PermissionInfo; 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.PowerManager; 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { } 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 @@ class ContextImpl extends Context { return mFilesDir; } } - + @Override public File getExternalFilesDir(String type) { synchronized (mSync) { @@ -487,7 +484,7 @@ class ContextImpl extends Context { return dir; } } - + @Override public File getCacheDir() { synchronized (mSync) { @@ -507,7 +504,7 @@ class ContextImpl extends Context { } return mCacheDir; } - + @Override public File getExternalCacheDir() { synchronized (mSync) { @@ -529,7 +526,7 @@ class ContextImpl extends Context { return mExternalCacheDir; } } - + @Override public File getFileStreamPath(String name) { return makeFilename(getFilesDir(), name); @@ -570,7 +567,7 @@ class ContextImpl extends Context { return (list != null) ? list : EMPTY_FILE_LIST; } - + private File getDatabasesDir() { synchronized (mSync) { if (mDatabasesDir == null) { @@ -582,7 +579,7 @@ class ContextImpl extends Context { return mDatabasesDir; } } - + @Override public Drawable getWallpaper() { return getWallpaperManager().getDrawable(); @@ -650,7 +647,7 @@ class ContextImpl extends Context { } catch (RemoteException e) { } } - + @Override public void sendBroadcast(Intent intent) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); @@ -1587,15 +1584,15 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { public boolean releaseProvider(IContentProvider provider) { return mMainThread.releaseProvider(provider); } - + private final ActivityThread mMainThread; } @@ -1706,7 +1703,7 @@ class ContextImpl extends Context { throw new RuntimeException("Package manager has died", e); } } - + @Override public String[] canonicalToCurrentPackageNames(String[] names) { try { @@ -1715,7 +1712,7 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { throw new RuntimeException("Package manager has died", e); } } - + @Override public boolean hasSystemFeature(String name) { try { @@ -1913,7 +1911,7 @@ class ContextImpl extends Context { throw new RuntimeException("Package manager has died", e); } } - + @Override public int checkPermission(String permName, String pkgName) { try { @@ -1985,9 +1983,9 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { } } } - + private static final class ResourceName { final String packageName; final int iconId; @@ -2563,7 +2561,7 @@ class ContextImpl extends Context { } } @Override - public void clearApplicationUserData(String packageName, + public void clearApplicationUserData(String packageName, IPackageDataObserver observer) { try { mPM.clearApplicationUserData(packageName, observer); @@ -2572,7 +2570,7 @@ class ContextImpl extends Context { } } @Override - public void deleteApplicationCacheFiles(String packageName, + public void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer) { try { mPM.deleteApplicationCacheFiles(packageName, observer); @@ -2597,9 +2595,9 @@ class ContextImpl extends Context { // 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 @@ class ContextImpl extends Context { // Should never happen! } } - + @Override public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { @@ -2663,7 +2661,7 @@ class ContextImpl extends Context { // Should never happen! } } - + @Override public int getPreferredActivities(List<IntentFilter> outFilters, List<ComponentName> outActivities, String packageName) { @@ -2674,7 +2672,7 @@ class ContextImpl extends Context { } return 0; } - + @Override public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) { @@ -2704,7 +2702,7 @@ class ContextImpl extends Context { // 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; 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 @@ import java.util.Calendar; * <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 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, 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 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, 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 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, 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 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, mCalendar.set(Calendar.DAY_OF_MONTH, day); setTitle(mTitleDateFormat.format(mCalendar.getTime())); } - + @Override public Bundle onSaveInstanceState() { Bundle state = super.onSaveInstanceState(); @@ -175,7 +174,7 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, 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 @@ public class LocalActivityManager { 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 @@ public class Notification implements Parcelable 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 @@ import android.util.Log; * </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 @@ public class NotificationManager /** * 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 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * @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 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS 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 @@ import java.util.Calendar; * <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 @@ public class TimePickerDialog extends AlertDialog implements OnClickListener, 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 @@ public class TimePickerDialog extends AlertDialog implements OnClickListener, 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 @@ public class TimePickerDialog extends AlertDialog implements OnClickListener, 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 class TimePickerDialog extends AlertDialog implements OnClickListener, 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 @@ public class TimePickerDialog extends AlertDialog implements OnClickListener, mCalendar.set(Calendar.MINUTE, minute); setTitle(mDateFormat.format(mCalendar.getTime())); } - + @Override public Bundle onSaveInstanceState() { Bundle state = super.onSaveInstanceState(); @@ -150,7 +151,7 @@ public class TimePickerDialog extends AlertDialog implements OnClickListener, 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 @@ public class WallpaperManager { 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 @@ public class WallpaperManager { 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.IBinder; 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 @@ public class AppWidgetManager { * 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 @@ public class AppWidgetManager { /** * 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 @@ public class AppWidgetManager { * 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 @@ public class AppWidgetManager { * @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 @@ public class AppWidgetManager { } 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 @@ public class AppWidgetManager { 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 @@ public class AppWidgetManager { /** * 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 @@ public abstract class AtCommandHandler { * 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 @@ public abstract class AtCommandHandler { * 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 @@ public final class BluetoothHeadset { "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 @@ public final class HeadsetBase { 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 @@ public final class HeadsetBase { 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 @@ public final class HeadsetBase { */ 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 @@ public final class HeadsetBase { 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 @@ public final class HeadsetBase { 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 @@ public abstract class ContentResolver { } 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 @@ public abstract class ContentResolver { 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 @@ public abstract class ContentResolver { @Override protected void finalize() throws Throwable { + // TODO: integrate CloseGuard support. try { if(!mCloseFlag) { - ContentResolver.this.releaseProvider(mContentProvider); + Log.w(TAG, "Cursor finalized without prior close()"); + close(); } } finally { super.finalize(); diff --git a/core/java/android/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 @@ import java.util.regex.Pattern; /** 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 @@ Typically, it looks something like this: 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 @@ logic you'd otherwise need. Like this: } } </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 index 9bb3b75..e9a2929 100644..100755 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -25,6 +25,7 @@ import org.xmlpull.v1.XmlPullParserException; 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 @@ public class Resources { = 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 @@ public class Resources { = 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 @@ public class Resources { (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 @@ public class Resources { } } + 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 @@ public class Resources { } 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 @@ public class Resources { 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 @@ public class Resources { 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 @@ public class Resources { 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 @@ final class StringBlock { 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 @@ final class StringBlock { } 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 @@ final class StringBlock { 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 @@ public abstract class AbstractCursor implements CrossProcessCursor { * @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 @@ public interface Cursor { /** * 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 @@ public interface Cursor { /** * 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 @@ public interface Cursor { /** * 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 @@ public interface Cursor { /** * 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 @@ public interface Cursor { /** * 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 @@ public interface Cursor { /** * 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 @@ public interface Cursor { /** * 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 @@ public interface Cursor { * 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 @@ public interface Cursor { * * <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 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { } } + /** + * 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 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { } } + /** + * 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 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { } } + /** + * 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 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { } } + /** + * 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 @@ public class DatabaseUtils { 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 @@ public class DatabaseUtils { * @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 @@ public class SQLiteCursor extends AbstractWindowedCursor { // 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 @@ public class SQLiteCursor extends AbstractWindowedCursor { 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 @@ public class SQLiteQueryBuilder } String sql = buildQuery( - projectionIn, selection, selectionArgs, groupBy, having, + projectionIn, selection, groupBy, having, sortOrder, limit); if (Log.isLoggable(TAG, Log.DEBUG)) { @@ -345,10 +345,6 @@ public class SQLiteQueryBuilder * 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 @@ public class SQLiteQueryBuilder * @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 @@ public class SQLiteQueryBuilder } /** + * @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 @@ public class SQLiteQueryBuilder * 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 @@ public class SQLiteQueryBuilder int computedColumnsOffset, String typeDiscriminatorValue, String selection, - String[] selectionArgs, String groupBy, String having) { int unionColumnsCount = unionColumns.length; @@ -463,12 +467,36 @@ public class SQLiteQueryBuilder } } 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.annotation.SdkConstant.SdkConstantType; 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 @@ public class ConnectivityManager * @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 @@ interface IConnectivityManager 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 b559601..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.content.IntentFilter; 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 @@ public class MobileDataStateTracker extends NetworkStateTracker { * 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 @@ package android.net; 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 @@ public abstract class NetworkStateTracker extends Handler { } 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 @@ public abstract class NetworkStateTracker extends Handler { 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 @@ public abstract class NetworkStateTracker extends Handler { * @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 @@ public class NetworkUtils { 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; + } + + 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; } - 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 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 @@ final public class Proxy { } } + /** + * 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..8dc2a86 100644 --- a/core/java/android/net/http/AndroidHttpClient.java +++ b/core/java/android/net/http/AndroidHttpClient.java @@ -79,6 +79,9 @@ public final class AndroidHttpClient implements HttpClient { // 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 @@ public final class AndroidHttpClient implements HttpClient { // 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 @@ public final class AndroidHttpClient implements HttpClient { 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 @@ class CertificateValidatorCache { 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 @@ class CertificateValidatorCache { } // 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 @@ public final class Headers { 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 @@ public class HttpsConnection extends Connection { 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 @@ public class RequestHandle { 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 class BatteryManager { 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 @@ public class Binder implements IBinder { } 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 @@ public final class Debug /** * 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 final class Debug public int otherPrivateDirty; /** The shared dirty pages used by everything else. */ public int otherSharedDirty; - + public MemoryInfo() { } @@ -137,21 +138,21 @@ public final class Debug 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 @@ public final class Debug 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 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * 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 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * 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 @@ public class DropBoxManager { 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 @@ public class DropBoxManager { 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 @@ public final class Message implements Parcelable { // 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 @@ public final class Message implements Parcelable { * 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 @@ public final class Message implements Parcelable { * 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 @@ public final class Message implements Parcelable { 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 @@ public abstract class DialogPreference extends Preference implements 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 @@ public class ListPreference extends DialogPreference { 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 @@ public class ListPreference extends DialogPreference { 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 @@ public class ListPreference extends DialogPreference { } /** + * 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 95e5432..fae5f1a 100644 --- a/core/java/android/preference/PreferenceScreen.java +++ b/core/java/android/preference/PreferenceScreen.java @@ -80,6 +80,8 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi private ListAdapter mRootAdapter; private Dialog mDialog; + + private ListView mListView; /** * Do NOT use this constructor, use {@link PreferenceManager#createPreferenceScreen(Context)}. @@ -145,15 +147,18 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi 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 @@ public final class Settings { 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 @@ public final class Settings { 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 @@ public final class Settings { 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.content.Intent; 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 @@ public final class Telephony { * 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 @@ public final class Telephony { */ @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 @@ public final class Telephony { * 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 android.util.Log; import java.util.HashMap; import java.util.Set; +import android.os.PowerManager; + /** * TODO: Move this to @@ -51,6 +53,9 @@ class BluetoothEventLoop { 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 @@ class BluetoothEventLoop { 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 @@ class BluetoothEventLoop { 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 @@ class BluetoothEventLoop { 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 @@ class BluetoothEventLoop { 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 @@ class BluetoothEventLoop { 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 @@ public abstract class WallpaperService extends Service { mLayout.windowAnimations = com.android.internal.R.style.Animation_Wallpaper; mInputChannel = new InputChannel(); - mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets, - mInputChannel); + if (mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets, + mInputChannel) < 0) { + Log.w(TAG, "Failed to add window while updating wallpaper surface."); + return; + } mCreated = true; InputQueue.registerInputChannel(mInputChannel, mInputHandler, diff --git a/core/java/android/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 @@ public interface InputFilter } 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 index 38ac9b7..4e197cd 100644..100755 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -749,6 +749,9 @@ public abstract class Layout { 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 @@ public abstract class Layout { Directions dirs = getLineDirections(line); if (line != getLineCount() - 1) - end--; + end = TextUtils.getOffsetBefore(mText, end); float horiz = getPrimaryHorizontal(offset); @@ -993,7 +996,7 @@ public abstract class Layout { Directions dirs = getLineDirections(line); if (line != getLineCount() - 1) - end--; + end = TextUtils.getOffsetBefore(mText, end); float horiz = getPrimaryHorizontal(offset); @@ -1564,7 +1567,8 @@ public abstract class Layout { 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 @@ public abstract class Layout { new Directions(new short[] { 0, 32767 }); } - diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index f02ad2a..d0d2482 100644..100755 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -313,7 +313,9 @@ extends Layout 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 @@ extends Layout } 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 @@ extends Layout 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 index 513b2cd..13cc42c 100644..100755 --- a/core/java/android/text/Styled.java +++ b/core/java/android/text/Styled.java @@ -203,9 +203,10 @@ public class Styled } } + 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 @@ public class Styled 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 @@ public class DateUtils } } } 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 @@ public class DateUtils } /** + * 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 @@ public class DateUtils 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 @@ public class DateUtils * @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 class DateUtils 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); - - // 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); + sNowTime.set(now); + sThenTime.set(millis); + + 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 @@ public class Time { 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 class Time { 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 @@ public class Time { * 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 @@ public class Time { * 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 @@ public class Time { * <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 @@ public class Time { * 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 @@ public class Time { } return false; } - + native private boolean nativeParse3339(String s); /** @@ -484,13 +484,13 @@ public class Time { * <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 @@ public class Time { * 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 @@ public class Time { * 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 @@ public class Time { * 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 @@ public class Time { /** * 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 @@ public class Time { /** * 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 @@ public class Time { * 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 @@ public class Time { * 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 @@ public class Time { 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 @@ public class Time { } /** - * 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 @@ public class Time { 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 @@ public class Time { 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 @@ public class Time { * 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 @@ public class Time { 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 @@ public class Time { * 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 @@ public class Time { * 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 @@ public class Time { // 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 @@ public class Linkify { 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 @@ public class TimeUtils { 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 @@ public class FocusFinder { /** - * 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 @@ import java.util.HashMap; * for the device you are running on. For example: * * <pre>LayoutInflater inflater = (LayoutInflater)context.getSystemService - * Context.LAYOUT_INFLATER_SERVICE);</pre> + * (Context.LAYOUT_INFLATER_SERVICE);</pre> * * <p> * To create a new LayoutInflater with an additional {@link Factory} for your diff --git a/core/java/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 @@ public final class MotionEvent extends InputEvent implements Parcelable { 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility 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 @@ public final class ViewRoot extends Handler implements ViewParent, } } 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 @@ public interface WindowManager extends ViewManager { 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 @@ package android.webkit; 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 @@ class FrameLoader { private int mCacheMode; private String mReferrer; private String mContentType; + private final String mUaprofHeader; private static final int URI_PROTOCOL = 0x100; @@ -57,6 +59,8 @@ class FrameLoader { 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 @@ class FrameLoader { } 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 @@ public class MimeTypeMap { 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 @@ public class WebView extends AbsoluteLayout // 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 class WebView extends AbsoluteLayout */ 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 @@ public class WebView extends AbsoluteLayout } } + 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout 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 @@ final class WebViewCore { // 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 @@ public class AbsoluteLayout extends ViewGroup { * </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 android.view.ViewGroup; 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 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { */ private boolean mNotifyOnChange = true; - private Context mContext; + private Context mContext; private ArrayList<T> mOriginalValues; private ArrayFilter mFilter; @@ -181,6 +182,44 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { } /** + * 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 class ArrayAdapter<T> extends BaseAdapter implements Filterable { */ 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 @@ public class EdgeGlow { 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 @@ class FastScroller { 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 class NumberPicker extends LinearLayout { 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 @@ class NumberPickerButton extends ImageButton { 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 @@ public class ProgressBar extends View { 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 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { } 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - if (onCheckIsTextEditor()) { + if (onCheckIsTextEditor() && isEnabled()) { if (mInputMethodState == null) { mInputMethodState = new InputMethodState(); } @@ -4666,6 +4682,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener + ": " + 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class AlertController { 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.os.Bundle; 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 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa 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 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa // 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 @@ public final class ShutdownThread extends Thread { 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 @@ public final class ShutdownThread extends Thread { 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 @@ public final class ShutdownThread extends Thread { 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 @@ public final class BatteryStatsImpl extends BatteryStats { private final Map<String, KernelWakelockStats> readKernelWakelockStats() { - byte[] buffer = new byte[4096]; + byte[] buffer = new byte[8192]; int len; try { @@ -1031,9 +1031,11 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public class SamplingProfilerIntegration { 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 @@ public class PduParser { // 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 @@ public final class PduCache extends AbstractCache<Uri, PduCacheEntry> { } 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 @@ static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstri 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 @@ static jint android_net_utils_removeHostRoutes(JNIEnv* env, jobject clazz, jstri 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 @@ static JNINativeMethod gNetworkUtilMethods[] = { { "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 @@ static int directionality_map[U_CHAR_DIRECTION_COUNT] = { 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 @@ static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject 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 @@ static JNINativeMethod gStringBlockMethods[] = { (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..2b6dee8 --- /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_SRC_FILES := $(call all-java-files-under, src) 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.Matcher; 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 @@ public class PackageManagerHostTestUtils extends Assert { * @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 @@ public class PackageManagerHostTestUtils extends Assert { * @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 @@ public class PackageManagerHostTestUtils extends Assert { * 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 @@ public class PackageManagerHostTestUtils extends Assert { /** * 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 @@ public class PackageManagerHostTestUtils extends Assert { * @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 @@ public class PackageManagerHostTestUtils extends Assert { * 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 @@ public class PackageManagerHostTestUtils extends Assert { * * @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 - */ - public boolean doesRemoteFileExist(String destPath) 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 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 @@ public class PackageManagerHostTestUtils extends Assert { * @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 @@ public class PackageManagerHostTestUtils extends Assert { * * @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 - */ - public boolean doesPackageExist(String packageName) 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 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 @@ public class PackageManagerHostTestUtils extends Assert { * * @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 - */ - public boolean doesAppExistOnDevice(String packageName) 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 boolean doesAppExistOnDevice(String packageName) throws IOException, TimeoutException, + AdbCommandRejectedException, ShellCommandUnresponsiveException { return doesRemoteFileExistContainingString(DEVICE_APP_PATH, packageName); } @@ -288,9 +331,14 @@ public class PackageManagerHostTestUtils extends Assert { * * @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 - */ - public boolean doesAppExistOnSDCard(String packageName) 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 boolean doesAppExistOnSDCard(String packageName) throws IOException, TimeoutException, + AdbCommandRejectedException, ShellCommandUnresponsiveException { return doesRemoteFileExistContainingString(SDCARD_APP_PATH, packageName); } @@ -299,9 +347,14 @@ public class PackageManagerHostTestUtils extends Assert { * * @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 - */ - public boolean doesAppExistAsForwardLocked(String packageName) 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 boolean doesAppExistAsForwardLocked(String packageName) throws IOException, + TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException { return doesRemoteFileExistContainingString(APP_PRIVATE_PATH, packageName); } @@ -309,9 +362,14 @@ public class PackageManagerHostTestUtils extends Assert { * Waits for device's package manager to respond. * * @throws InterruptedException - * @throws IOException - */ - public void waitForPackageManager() throws InterruptedException, 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, 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 @@ public class PackageManagerHostTestUtils extends Assert { * * @param packageName The name of the package to wait to load * @throws InterruptedException - * @throws IOException - */ - public void waitForApp(String packageName) throws InterruptedException, 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, + 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 @@ public class PackageManagerHostTestUtils extends Assert { /** * Helper method which executes a adb shell command and returns output as a {@link String} * @return the output of the command - * @throws IOException - */ - public String executeShellCommand(String 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, 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 @@ public class PackageManagerHostTestUtils extends Assert { /** * Helper method ensures we are in root mode on the host side. It returns only after * PackageManager is actually up and running. - * @throws IOException - */ - public void runAdbRoot() throws IOException, 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 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 @@ public class PackageManagerHostTestUtils extends Assert { /** * 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 - */ - public void rebootDevice() throws IOException, 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, 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 @@ public class PackageManagerHostTestUtils extends Assert { 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 @@ public class PackageManagerHostTestUtils extends Assert { mAllTestsPassed = false; } - public void testRunEnded(long elapsedTime) { + public void testRunEnded(long elapsedTime, Map<String, String> resultBundle) { // ignore } @@ -519,7 +597,7 @@ public class PackageManagerHostTestUtils extends Assert { mTestRunErrorMessage = errorMessage; } - public void testRunStarted(int testCount) { + public void testRunStarted(String runName, int testCount) { // ignore } @@ -586,17 +664,23 @@ public class PackageManagerHostTestUtils extends Assert { /** * 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 @@ public class PackageManagerHostTestUtils extends Assert { /** * 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 @@ public class PackageManagerHostTestUtils extends Assert { /** * 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 @@ public class PackageManagerHostTestUtils extends Assert { /** * Helper method for uninstalling an app. + * <p/> + * Assumes adb is running as root in device under test. * * @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. - */ - public void uninstallApp(String pkgName) throws IOException, 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. + * @throws InstallException if the uninstall failed. + */ + 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 @@ public class PackageManagerHostTestUtils extends Assert { * 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. - */ - public void wipeNonSystemApps() 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 InstallException if the uninstall failed. + */ + 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 @@ public class PackageManagerHostTestUtils extends Assert { * * <p/> * Assumes adb is running as root in device under test. - */ - public void setDevicePreferredInstallLocation(InstallLocPreference pref) 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 setDevicePreferredInstallLocation(InstallLocPreference pref) throws IOException, + TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException { String command = "pm setInstallLocation %d"; int locValue = 0; switch (pref) { @@ -749,8 +865,14 @@ public class PackageManagerHostTestUtils extends Assert { * * <p/> * Assumes adb is running as root in device under test. - */ - public InstallLocPreference getDevicePreferredInstallLocation() 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 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * @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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerHostTests extends DeviceTestCase { * 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 @@ public class PackageManagerStressHostTests extends DeviceTestCase { * <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 @@ public class PackageManagerStressHostTests extends DeviceTestCase { * <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 @@ public class PackageManagerStressHostTests extends DeviceTestCase { * <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 @@ public class PackageManagerStressHostTests extends DeviceTestCase { * <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/tests/BrowserTestPlugin/Android.mk b/data/sounds/AllAudio.mk index 968d9e6..4e7a403 100644 --- a/tests/BrowserTestPlugin/Android.mk +++ b/data/sounds/AllAudio.mk @@ -1,3 +1,4 @@ +# # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,24 +14,7 @@ # 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)) +$(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 @@ page.title=Community <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 @@ then please refer to the <a href="http://source.android.com/discuss">Open Source 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 @@ In XML: <code>@[<em>package</em>:]anim/<em>filename</em></code> 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 @@ inherrited by this element).</p> 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 @@ inherrited by this element).</p> 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 @@ which indicates the type of touchscreen on the device.</p> <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 @@ page.title=コミュニティ <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 @@ page.title=コミュニティ <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 @@ page.title=コミュニティ <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 @@ page.title=コミュニティ <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 @@ you. Our previous example can easily be rewritten with 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 @@ page.title=Developer Forums <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/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java b/drm/common/DrmConvertedStatus.cpp index 7bb4c35..5d035f5 100644 --- a/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java +++ b/drm/common/DrmConvertedStatus.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,15 @@ * limitations under the License. */ -package com.android.testplugin; +#include <drm/DrmConvertedStatus.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; - } +DrmConvertedStatus::DrmConvertedStatus( + int _statusCode, const DrmBuffer* _convertedData, int _offset) : + statusCode(_statusCode), + convertedData(_convertedData), + offset(_offset) { } + 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/drm/common/DrmInfoStatus.cpp b/drm/common/DrmInfoStatus.cpp new file mode 100644 index 0000000..8ec7311 --- /dev/null +++ b/drm/common/DrmInfoStatus.cpp @@ -0,0 +1,29 @@ +/* + * 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/DrmInfoStatus.h> + +using namespace android; + +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..92bda8f --- /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..98284e7 --- /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 +file’s integrity and getting the MIME type of its embedded content.</p> + +<p class=MsoBodyText style='margin-bottom:24.0pt'>The converter and decoder are +built into two separate libraries, which share common code for random number +generation and key encryption in a third library. For test purposes there is +also a unit test application. See Figure 1.</p> + +<p class=MsoBodyText style='page-break-after:avoid'><img width=288 height=364 +src="images/image001.gif"></p> + +<p class=MsoCaption style='margin-top:12.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm'><a name="_Ref262730885">Figure </a>1. Block diagram illustrating the dependencies between the executable modules.</p> + +<b><span style='font-size:16.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></b> + +<h1><a name="_Toc276471424">3<span style='font:7.0pt "Times New Roman"'> +</span>Use Cases</a></h1> + +<p class=MsoBodyText>This section describes all the use cases for the converter +and decoder. It shows the sequence of API calls that should be used to solve +these use cases.</p> + +<h2><a name="_Toc276471425">3.1<span style='font:7.0pt "Times New Roman"'> +</span>Converter</a></h2> + +<p class=MsoBodyText>Through the converter API, conversion can be performed in one +of two ways:</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span><i>Push-mode +conversion</i> is when the client progressively feeds data to the converter as +it arrives. This is appropriate when data arrives gradually in chunks, with +idle time in between. Consequently, push mode is used for converting files +being downloaded through HTTP. See section 3.1.1.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span><i>Pull-mode +conversion</i> is when the converter drives the process and consumes data from +the client as it needs it. This is appropriate when the entire file to be +converted is readily available. Hence, pull mode is used by the unit test application. +See section 3.1.2.</p> + +<p class=MsoBodyText>Internally, pull-mode conversion is implemented in terms +of the API for push-mode conversion.</p> + +<h3><a name="_Toc276471426"></a><a name="_Ref263085478">3.1.1<span +style='font:7.0pt "Times New Roman"'> </span>Convert Data +(Push-Mode Conversion)</a></h3> + +<p class=MsoBodyText>Push-mode conversion is performed as follows (see also Figure 2):</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span><span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_OpenSession</span> +initializes the output parameter and returns a <i>session ID</i> to be used in +subsequent calls to the API. The output parameter is a union of return values +whose correct use at any given moment is determined by the API function last +called.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span><span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_ConvertData</span> +is called repeatedly until no more input data remains. Each call converts the +maximum amount of data possible and writes it to the output buffer. The client then +writes this data to file.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span +style='font:7.0pt "Times New Roman"'> </span><span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_CloseSession</span> +cleans up the session and deallocates the output buffer. If all has gone well, a +two-part cryptographic signature of the output file is calculated. The client +must go back and rewrite part of the file header with this updated signature +information.</p> + +<p class=MsoBodyText>Every time a file is being converted, the converter calls <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetRandomNumber</span> +to generate a new, unique session key. No two converted files look alike, even +if the original files are the same.</p> + +<p class=MsoBodyText><b>Note:</b> The random bytes cannot come from any bare-minimum +implementation of the C-library <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>rand</span> +function—they must be cryptographically secure. Otherwise, security will be +compromised.</p> + +<p class=MsoBodyText>The session key is encrypted and stored within the +converted file. Key encryption is performed using <span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetEncryptedKeyLength</span> and <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_EncryptKey</span>. +These two functions, together with the corresponding decryption function (<span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_DecryptKey</span>), +are the integration points where an OEM manufacturer may implement their own +key-encryption scheme.</p> + +<p class=MsoBodyText><b>Note:</b> The key-encryption key must be unique to each +device; this is what makes the files forward lock–protected. Ideally, it should +be derived from secret hardware parameters, but at the very least it should be +persistent from one master reset to the next.</p> + +<div style='margin-bottom:24.0pt;border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt; +background:#F2F2F2'> + +<p class=MsoBodyText style='background:#F2F2F2;border: +none;padding:0cm'><b>Note:</b> In the open-source implementation of the <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>libfwdlock-common</span> +library, a random key-encryption key is generated and stored in plaintext in +the file system, without being obfuscated in any way (doing so would be futile +since the source code is openly available). This key must be kept secret from +the user, and shouldn’t be possible to extract through backup-and-restore +functionality or the like. OEM manufacturers will probably want to implement a +truly hardware-based device-unique key.</p> + +</div> + +<p class=MsoBodyText style='page-break-after:avoid'><img width=531 height=563 +src="images/image002.gif"></p> + +<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm'><a name="_Ref263085187">Figure </a>2. Converter UC: Convert Data.</p> + +<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></b> + +<h3><a name="_Toc276471427"></a><a name="_Ref263163082">3.1.2<span +style='font:7.0pt "Times New Roman"'> </span>Convert File +(Pull-Mode Conversion)</a></h3> + +<p class=MsoBodyText>Pull-mode conversion is performed by calling <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertFile</span> +with the filename, unless there is need for a specialized <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>read</span> function, in +which case <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertOpenFile</span> +should be used directly instead. See Figure 3.</p> + +<p class=MsoBodyText style='margin-bottom:24.0pt'>Internally, <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertFile</span> +calls <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertOpenFile</span>. +The latter then proceeds with the conversion using the push-mode API, acting as +the client in the previous use case; see section 3.1.1.</p> + +<p class=MsoBodyText style='page-break-after:avoid'><img width=531 height=731 +src="images/image003.gif"></p> + +<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm'><a name="_Ref263085208">Figure </a>3. Converter UC: Convert File.</p> + +<b><i><span style='font-size:14.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></i></b> + +<h2><a name="_Toc276471428">3.2<span style='font:7.0pt "Times New Roman"'> +</span>Decoder</a></h2> + +<p class=MsoBodyText>The decoder API allows the client to do the following:</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span>Check +the integrity of an internal Forward Lock file, i.e., detect whether it has +been manipulated in any way; see section 3.2.1.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span>Get +the MIME type of the embedded content (the “original” MIME type before DRM protection +was applied); see section 3.2.2.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span +style='font:7.0pt "Times New Roman"'> </span>Decode +the file by random access, i.e., read and seek through it in an arbitrary +manner; see section 3.2.3.</p> + +<p class=MsoBodyText>All subsequent operations on a file first require it to be +opened. Opening a file returns a <i>file descriptor</i>—a handle to be used in +these subsequent operations.</p> + +<p class=MsoBodyText>If the filename is known, an internal Forward Lock file +can be opened using <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span>. +If only the file descriptor of an already open file is available, a decoding +session can instead be initialized using <span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>.</p> + +<p class=MsoBodyText>Internally, <span style='font-size:10.0pt;font-family: +"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> calls <span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>. For efficiency +reasons, <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span> +therefore assumes that the file position is at the beginning of the file when +the function gets called. A client who calls it directly must make sure that +this assumption holds.</p> + +<p class=MsoBodyText>When a file is being attached, the session key stored in +the file during conversion is decrypted using <span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetEncryptedKeyLength</span> and <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_DecryptKey</span>, +in order to set up for decoding and integrity checking.</p> + +<p class=MsoBodyText>For just getting the content type, however, retrieving the +session key would strictly speaking not be necessary, so there is an +opportunity here to optimize for that if it proves necessary later.</p> + +<p class=MsoBodyText>Symmetrical to <span style='font-size:10.0pt;font-family: +"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> and <span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>, there are also functions +for closing a file or detaching from it:</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span>If +it was opened with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> +it should be closed with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_close</span>.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span>If +it was attached with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span> +it should be detached with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_detach</span>.</p> + +<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></b> + +<h3><a name="_Ref263163099"></a><a name="_Toc276471429">3.2.1<span +style='font:7.0pt "Times New Roman"'> </span>Check Integrity</a></h3> + +<p class=MsoBodyText>There are three methods for checking the integrity of an +internal Forward Lock file, in whole or in part (see also Figure 4):</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span><span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>, +which checks the integrity of the encrypted content data.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span><span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span>, +which checks the integrity of the file header, including the content type and +other fields not currently supported but reserved for future use.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span +style='font:7.0pt "Times New Roman"'> </span><span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckIntegrity</span>, +which internally calls first <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span> +and then <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>.</p> + +<p class=MsoBodyText style='margin-bottom:24.0pt'><span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span> is +generally much faster than <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>, +whose running time is directly proportional to the size of the file.</p> + +<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=575 +src="images/image004.gif"></p> + +<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm'><a name="_Ref263163308">Figure </a>4. Decoder UC: Check Integrity.</p> + +<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></b> + +<h3><a name="_Toc276471430"></a><a name="_Ref263163117">3.2.2<span +style='font:7.0pt "Times New Roman"'> </span>Get Content Type</a></h3> + +<p class=MsoBodyText style='margin-bottom:24.0pt'><span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_GetContentType</span> returns a +read-only reference to an ASCII string containing the MIME type of the +embedded content. This reference is valid as long as the file is kept open. +Clients who need access to the content type after closing the file should make +a copy of the string. See Figure 5 below.</p> + +<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=488 +src="images/image005.gif"></p> + +<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm'><a name="_Ref263163392">Figure </a>5. Decoder UC: Get Content Type.</p> + +<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></b> + +<h3><a name="_Toc276471431"></a><a name="_Ref263163137">3.2.3<span +style='font:7.0pt "Times New Roman"'> </span>Decode File</a></h3> + +<p class=MsoBodyText>After opening an internal Forward Lock file (or attaching +to an already open one), it can be transparently read from as if it were +unencrypted. Any number of calls to read data from the current file position or +set it to a new one (which is what <span style='font-size:10.0pt;font-family: +"Lucida Console","DejaVu Sans Mono"'>lseek</span> does) can be made in any order; this is what we +call <i>random access</i>. See Figure 6.</p> + +<p class=MsoBodyText>The Forward Lock Decoder versions of the <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>read</span>, <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>lseek</span>, and <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>close</span> functions +have the exact same signatures as their POSIX counterparts. So, for example, +the call <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_lseek(fd, +0, SEEK_END)</span> returns the size of the embedded content data, i.e., the +size of the original file before DRM protection.</p> + +<p class=MsoBodyText style='margin-bottom:24.0pt'>Moreover, <span +style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> +is like regular POSIX <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>open</span> +except it takes only the filename as a parameter—access is always read-only.</p> + +<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=522 +src="images/image006.gif"></p> + +<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm'><a name="_Ref263166303">Figure </a>6. Decoder UC: Decode File.</p> + +<b><span style='font-size:16.0pt;font-family:"Arial","sans-serif"'><br +clear=all style='page-break-before:always'> +</span></b> + +<h1><a name="_Toc276471432">4<span style='font:7.0pt "Times New Roman"'> +</span>Definition of the Internal Forward Lock File Format</a></h1> + +<p class=MsoBodyText style='margin-bottom:12.0pt'>The inner structure of an internal +Forward Lock file is defined in Table 1 below.</p> + +<table class=MsoNormalTable border=1 cellspacing=0 cellpadding=0 + style='border-collapse:collapse;border:none'> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><b>Offset [bytes]</b></p> + </td> + <td width=96 valign=top style='width:72.0pt;border:solid windowtext 1.0pt; + border-left:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><b>Size [bytes]</b></p> + </td> + <td width=361 valign=top style='width:270.85pt;border:solid windowtext 1.0pt; + border-left:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><b>Description</b></p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>0</p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>4</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>The file signature (so-called + <i>magic number</i>): a four-character code consisting of the letters + F-W-L-K.</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>4</p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>1</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>Version number (0 for the + first version).</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>5</p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>1</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>Indicates the subformat:</p> + <p class=MsoNormal style='page-break-after:avoid'><i>0x00 Forward Lock</i></p> + <p class=MsoNormal style='page-break-after:avoid'><i>0x01 Combined Delivery</i></p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>6</p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>1</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>Usage restriction flags (prohibitions + against usage as ringtone or as wallpaper and screen saver). Also indicates + if the file is bound to a specific SIM card.</p> + <p class=MsoNormal style='page-break-after:avoid'><i>0x00 No usage + restrictions</i></p> + <p class=MsoNormal style='page-break-after:avoid'><i>0x01 Ringtone usage + prohibited</i></p> + <p class=MsoNormal style='page-break-after:avoid'><i>0x02 Screen usage + prohibited</i></p> + <p class=MsoNormal style='page-break-after:avoid'><i>0x80 Bound to SIM</i></p> + <p class=MsoNormal style='page-break-after:avoid'>(Any number of these may be + OR-ed together.)</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>7</p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>1</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>Length of the MIME content + type (<i>k</i>).</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>8</p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><i>k</i></p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>The MIME content type + (ASCII-encoded without null-character termination).</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i></p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><i>l </i>= 0 or 16</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>If the subformat is + Combined Delivery, this field contains the auto-generated content ID (16 bytes). + If not, this field is zero-size.</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i></p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><i>m </i>= 0 or 9</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>If the file is bound to a + specific SIM card, this field contains the 9-byte packed IMSI number. If not, + this field is zero-size.</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i>+<i>m</i></p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><i>n</i> ≥ 16</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>The encrypted session key, the + first sixteen bytes of which are also used as the CTR-mode <i>nonce</i> (similar + to the CBC-mode <i>initialization vector</i>).</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>20</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>Data signature—the SHA-1 + HMAC of the encrypted content data.</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>28+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>20</p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>Header signature—the SHA-1 + HMAC of all the fields above, including the encrypted session key and data + signature.</p> + </td> + </tr> + <tr> + <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt; + border-top:none;padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>48+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p> + </td> + <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none; + border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'><i><to the end of the + file></i></p> + </td> + <td width=361 valign=top style='width:270.85pt;border-top:none;border-left: + none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt; + padding:0cm 5.4pt 0cm 5.4pt'> + <p class=MsoNormal style='page-break-after:avoid'>The content data encrypted + using 128-bit AES in CTR mode.</p> + </td> + </tr> +</table> + +<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom: +12.0pt;margin-left:0cm;page-break-after:avoid'><a name="_Ref151269206">Table </a>1. Definition of the fields of an internal Forward Lock file.</p> + +<p class=MsoBodyText>As of now, neither Combined Delivery nor usage +restrictions (including SIM binding) are supported. These fields are reserved +for future use.</p> + +<h2><a name="_Toc276471433">4.1<span style='font:7.0pt "Times New Roman"'> +</span>Key Derivation</a></h2> + +<p class=MsoBodyText>The session key consists of sixteen bytes fetched from a +cryptographically secure random number generator. From the session key, two +separate keys are derived: one used for encryption, the other for signing.</p> + +<p class=MsoBodyText>The encryption key is the output from encrypting the +16-byte all-zero input block {0, 0, …, 0} using 128-bit AES with the random session +key as the key. The signing key is the output from encrypting the 16-byte input +block {1, 0, …, 0} the same way. The keys so derived will be cryptographically +independent from each other.</p> + +<p class=MsoBodyText>The session key is encrypted using a hardware-dependent +key-encryption key unique to each device. The encrypted session key is stored +inside the file, and its first sixteen bytes are also used as the <i>nonce</i> +for the CTR-mode encryption of the content data.</p> + +<h2><a name="_Toc276471434">4.2<span style='font:7.0pt "Times New Roman"'> +</span>Calculation of the Counters</a></h2> + +<p class=MsoBodyText>Using CTR (“counter”) mode, a block cipher such as AES can +be turned into a stream cipher. The process of encryption and decryption is +well defined in [1], except for the specifics of the calculation of the +counters. For the internal Forward Lock file format, the counters are +calculated as follows:</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span>The +nonce is interpreted as a 128-bit unsigned integer in little-endian format.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span>The +zero-based block sequence number (also a little-endian unsigned integer) is +added modulo 2<sup>128</sup> to the nonce to produce the counter for a given +block.</p> + +<h1><a name="_Toc276471435">5<span style='font:7.0pt "Times New Roman"'> +</span>Unit Test Cases</a></h1> + +<p class=MsoBodyText>Unit test cases for the converter and decoder come in two +varieties:</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span +style='font:7.0pt "Times New Roman"'> </span><i>Black-box</i> +test cases aim to verify that you get sensible results from malformed or +“tricky” input data.</p> + +<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span +style='font:7.0pt "Times New Roman"'> </span><i>White-box</i> +test cases aim to maximize code coverage using knowledge of code internals.</p> + +<p class=MsoBodyText>The black-box test cases are dependent on a specifically +designed set of input files found in the <span style='font-size:10.0pt; +font-family:"Lucida Console","DejaVu Sans Mono"'>forward-lock/internal-format/test/res</span> +directory in the repository. For ‘tests’ variants of the software, these input +files will be automatically installed in the file system image during build.</p> + +<p class=MsoBodyText>Run the test cases from the ADB shell command line as +follows:</p> + +<p class=MsoNormal style='margin-top:0cm;margin-right:0cm;margin-bottom:6.0pt; +margin-left:21.55pt'><span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'># +gtest_fwdlock</span></p> + +<p class=MsoBodyText>If all black-box but no white-box test cases fail, the +input files probably can’t be found in the working directory.</p> + +<h1><a name="_Toc276471436">6<span style='font:7.0pt "Times New Roman"'> +</span>References</a></h1> + +<p class=MsoBodyText style='margin-left:28.9pt;text-indent:-28.9pt'>[1]<span +style='font:7.0pt "Times New Roman"'> +</span><a +href="http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf">Dworkin, +Morris: “Recommendation for Block Cipher Modes of Operation—Methods and +Techniques,” NIST Special Publication 800-38A, December 2001.</a><a +name="_Ref151269073"></a></p> + +</div> + +</body> + +</html> diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif Binary files differnew file mode 100644 index 0000000..ee94513 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif Binary files differnew file mode 100644 index 0000000..8c12f46 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif Binary files differnew file mode 100644 index 0000000..9e019ca --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif Binary files differnew file mode 100644 index 0000000..cae1d01 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif Binary files differnew file mode 100644 index 0000000..0d87be9 --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif Binary files differnew file mode 100644 index 0000000..9445b6b --- /dev/null +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif 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..a587d0d 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -1221,7 +1221,7 @@ public class Canvas { 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 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac @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 @@ public class BitmapDrawable extends Drawable { @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 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { @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 @@ public class ColorDrawable extends Drawable { @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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { @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 @@ public class GradientDrawable extends Drawable { @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 @@ public class InsetDrawable extends Drawable implements Drawable.Callback @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 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { @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 @@ public class NinePatchDrawable extends Drawable { @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 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { @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 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { @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 @@ public class ShapeDrawable extends Drawable { @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/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h new file mode 100644 index 0000000..a83756e --- /dev/null +++ b/include/gui/SurfaceTextureClient.h @@ -0,0 +1,30 @@ +/* + * 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_SURFACETEXTURECLIENT_H +#define ANDROID_GUI_SURFACETEXTURECLIENT_H + +#include <ui/egl/android_natives.h> + +namespace android { + +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 @@ public: 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 @@ public: 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 @@ enum { // 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 @@ public: // 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 @@ enum { 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 @@ private: 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 @@ enum { 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/include/utils/Functor.h b/include/utils/Functor.h new file mode 100644 index 0000000..56a7557 --- /dev/null +++ b/include/utils/Functor.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_FUNCTOR_H +#define ANDROID_FUNCTOR_H + +namespace android { + +struct Functor{}; + +} + +#endif + diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index da86da4..ab7b973 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1983,6 +1983,7 @@ public: #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::writeCString(const char* str) 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 @@ float SC_turbulencef3(float x, float y, float z, float octaves) } } -}
\ 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 @@ FramebufferNativeWindow::FramebufferNativeWindow() 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 @@ FramebufferNativeWindow::FramebufferNativeWindow() 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)); - - err = grDev->alloc(grDev, - fbDev->width, fbDev->height, fbDev->format, - GRALLOC_USAGE_HW_FB, &buffers[1]->handle, &buffers[1]->stride); - LOGE_IF(err, "fb buffer 1 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); + } + + 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 %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 @@ SwitchInputMapper::~SwitchInputMapper() { } 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::getBufferAddress(overlay_buffer_t buffer) } 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 @@ void Overlay::destroy() { 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 @@ TEST_F(SwitchInputMapperTest, GetSources) { 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 @@ void print_complex(uint32_t complex, bool isFraction) } } +// 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 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const 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.Bundle; 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 @@ public class GpsNetInitiatedHandler { 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 @@ public class GpsNetInitiatedHandler { } 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 @@ public class GpsNetInitiatedHandler { 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 @@ public class GpsNetInitiatedHandler { */ 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 = ""; - - 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; + final char PADDING_CHAR = 0x00; + int lengthBytes = input.length; + int lengthSeptets = (lengthBytes * 8) / 7; + String decoded; + + /* 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 @@ public class GpsNetInitiatedHandler { } // 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 @@ public class MediaRecorder 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 @@ public class MediaRecorder 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 @@ public class MediaRecorder 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 @@ public class MediaRecorder 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 @@ public class MediaRecorder 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 @@ public class MediaScanner 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 @@ status_t MediaScanner::processDirectory( } 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 @@ LOCAL_SRC_FILES:= \ AwesomePlayer.cpp \ CameraSource.cpp \ DataSource.cpp \ + DRMExtractor.cpp \ ESDS.cpp \ FileSource.cpp \ HTTPStream.cpp \ @@ -59,7 +60,8 @@ LOCAL_SHARED_LIBRARIES := \ 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 @@ AwesomePlayer::AwesomePlayer() mExtractorFlags(0), mLastVideoBuffer(NULL), mVideoBuffer(NULL), - mSuspensionState(NULL) { + mSuspensionState(NULL), + mDecryptHandle(NULL) { CHECK_EQ(mClient.connect(), OK); DataSource::RegisterDefaultSniffers(); @@ -346,6 +347,12 @@ status_t AwesomePlayer::setDataSource_l( 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() { } 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 @@ status_t AwesomePlayer::play_l() { 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 @@ status_t AwesomePlayer::play_l() { mFlags &= ~(PLAYING | FIRST_FRAME); + if (mDecryptHandle != NULL) { + mDrmManagerClient->setPlaybackStatus(mDecryptHandle, + Playback::STOP, 0); + } + return err; } @@ -905,6 +931,11 @@ status_t AwesomePlayer::pause_l(bool at_eos) { mFlags &= ~PLAYING; + if (mDecryptHandle != NULL) { + mDrmManagerClient->setPlaybackStatus(mDecryptHandle, + Playback::PAUSE, 0); + } + return OK; } @@ -1022,6 +1053,13 @@ void AwesomePlayer::seekAudioIfNecessary_l() { 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 @@ void AwesomePlayer::onVideoEvent() { 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 @@ status_t AwesomePlayer::finishSetDataSource_l() { 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 @@ void DataSource::RegisterDefaultSniffers() { 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 @@ namespace android { 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 @@ FileSource::~FileSource() { 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 @@ ssize_t FileSource::readAt(off_t offset, void *data, size_t size) { } } - 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 @@ status_t FileSource::getSize(off_t *size) { 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 34064c8..5497322 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -264,7 +264,9 @@ MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source) mHasVideo(false), mFirstTrack(NULL), mLastTrack(NULL), - mFileMetaData(new MetaData) { + mFileMetaData(new MetaData), + mFirstSINF(NULL), + mIsDrm(false) { } MPEG4Extractor::~MPEG4Extractor() { @@ -276,6 +278,15 @@ MPEG4Extractor::~MPEG4Extractor() { 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 @@ status_t MPEG4Extractor::readMetaData() { 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 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { } 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 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { 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 @@ status_t MPEG4Extractor::parseTrackHeader( 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 @@ status_t MPEG4Source::read( } 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 @@ status_t MPEG4Source::read( 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; - if (nalLength == 0) { - continue; - } + return ERROR_MALFORMED; + } + + if (nalLength == 0) { + continue; + } - CHECK(dstOffset + 4 <= mBuffer->size()); + 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; + 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 @@ sp<MediaExtractor> MediaExtractor::Create( 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 @@ static bool ParseURL( 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 @@ status_t StagefrightMediaScanner::processFile( || !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 @@ static bool MakeURL(const char *baseURL, const char *url, AString *out) { 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 ARTSPController; struct ARTPSession; struct UDPPusher; +class DrmManagerClinet; +class DecryptHandle; + struct AwesomeRenderer : public RefBase { AwesomeRenderer() {} @@ -216,6 +220,9 @@ private: } } *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 @@ public: 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 @@ private: 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 @@ void ASessionDescription::getFormatType( 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 @@ private: 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 @@ LOCAL_SRC_FILES := \ 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 @@ public final class ServerOperation implements Operation, BaseStream { && (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 @@ void egl_window_surface_v2_t::copyBlt( 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 @@ EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw, } 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 @@ JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_changeBackground(JNIEnv * { 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 index e69de29..e69de29 100644 --- a/tests/BrowserTestPlugin/MODULE_LICENSE_APACHE2 +++ b/packages/WAPPushManager/MODULE_LICENSE_APACHE2 diff --git a/tests/BrowserTestPlugin/NOTICE b/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 Binary files differnew file mode 100644 index 0000000..8074c4c --- /dev/null +++ b/packages/WAPPushManager/tests/res/drawable-hdpi/icon.png diff --git a/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png b/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png Binary files differnew file mode 100644 index 0000000..1095584 --- /dev/null +++ b/packages/WAPPushManager/tests/res/drawable-ldpi/icon.png diff --git a/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png b/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png Binary files differnew file mode 100644 index 0000000..a07c69f --- /dev/null +++ b/packages/WAPPushManager/tests/res/drawable-mdpi/icon.png diff --git a/packages/WAPPushManager/tests/res/layout/main.xml b/packages/WAPPushManager/tests/res/layout/main.xml new file mode 100644 index 0000000..c7bdbb2 --- /dev/null +++ b/packages/WAPPushManager/tests/res/layout/main.xml @@ -0,0 +1,152 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<AbsoluteLayout +android:id="@+id/widget133" +android:layout_width="fill_parent" +android:layout_height="fill_parent" +xmlns:android="http://schemas.android.com/apk/res/android" +> +<EditText +android:id="@+id/app_id" +android:layout_width="wrap_content" +android:layout_height="wrap_content" +android:text="10" +android:textSize="18sp" +android:layout_x="0px" +android:layout_y="26px" +> +</EditText> +<EditText +android:id="@+id/cont" +android:layout_width="wrap_content" +android:layout_height="wrap_content" +android:text="20" +android:textSize="18sp" +android:layout_x="47px" +android:layout_y="26px" +> +</EditText> +<EditText +android:id="@+id/pkg" +android:layout_width="125px" +android:layout_height="wrap_content" +android:text="pkg" +android:textSize="18sp" +android:layout_x="0px" +android:layout_y="81px" +> +</EditText> +<EditText +android:id="@+id/cls" +android:layout_width="173px" +android:layout_height="wrap_content" +android:text="cls" +android:textSize="18sp" +android:layout_x="147px" +android:layout_y="81px" +> +</EditText> +<Button +android:id="@+id/addpkg" +android:layout_width="182px" +android:layout_height="wrap_content" +android:text="add/update package" +android:layout_x="15px" +android:layout_y="225px" +> +</Button> +<TextView +android:id="@+id/widget52" +android:layout_width="wrap_content" +android:layout_height="wrap_content" +android:text="input app_id, cont_type, pkg, cls" +android:textSize="18sp" +android:layout_x="0px" +android:layout_y="0px" +> +</TextView> +<Button +android:id="@+id/procmsg" +android:layout_width="109px" +android:layout_height="wrap_content" +android:text="process msg" +android:layout_x="197px" +android:layout_y="361px" +> +</Button> +<RadioGroup +android:id="@+id/widget137" +android:layout_width="83px" +android:layout_height="80px" +android:orientation="vertical" +android:layout_x="19px" +android:layout_y="137px" +> +<RadioButton +android:id="@+id/act" +android:layout_width="182px" +android:layout_height="wrap_content" +android:text="act" +android:checked="true" +> +</RadioButton> +<RadioButton +android:id="@+id/svc" +android:layout_width="wrap_content" +android:layout_height="wrap_content" +android:text="svc" +> +</RadioButton> +</RadioGroup> +<Button +android:id="@+id/delpkg" +android:layout_width="174px" +android:layout_height="wrap_content" +android:text="delete pkg" +android:layout_x="14px" +android:layout_y="283px" +> +</Button> +<EditText +android:id="@+id/pdu" +android:layout_width="186px" +android:layout_height="83px" +android:text="0006080302030aaf02905c030d6a0085070373616d706c6540646f636f6d6f2e6e652e6a700005c3072009102012345601" +android:textSize="18sp" +android:layout_x="10px" +android:layout_y="341px" +> +</EditText> +<CheckBox +android:id="@+id/ftr" +android:layout_width="wrap_content" +android:layout_height="wrap_content" +android:text="ftr_proc" +android:layout_x="143px" +android:layout_y="181px" +> +</CheckBox> +<CheckBox +android:id="@+id/sig" +android:layout_width="wrap_content" +android:layout_height="wrap_content" +android:text="nd_sig" +android:checked="true" +android:layout_x="142px" +android:layout_y="140px" +> +</CheckBox> +</AbsoluteLayout> diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java new file mode 100644 index 0000000..78fd174 --- /dev/null +++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ClientTest.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.smspush.unitTests; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.RadioButton; + +import com.android.internal.telephony.IWapPushManager; +import com.android.internal.telephony.WapPushManagerParams; +import com.android.internal.telephony.WapPushOverSms; +import com.android.internal.util.HexDump; +import com.android.smspush.WapPushManager; + +/** + * WapPushManager test application + */ +public class ClientTest extends Activity { + private static final String LOG_TAG = "WAP PUSH"; + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + Button addpbtn = (Button) findViewById(R.id.addpkg); + Button procbtn = (Button) findViewById(R.id.procmsg); + Button delbtn = (Button) findViewById(R.id.delpkg); + + Log.v(LOG_TAG, "activity created!!"); + + addpbtn.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + EditText app_id = (EditText) findViewById(R.id.app_id); + EditText cont = (EditText) findViewById(R.id.cont); + EditText pkg = (EditText) findViewById(R.id.pkg); + EditText cls = (EditText) findViewById(R.id.cls); + RadioButton act = (RadioButton) findViewById(R.id.act); + CheckBox sig = (CheckBox) findViewById(R.id.sig); + CheckBox ftr = (CheckBox) findViewById(R.id.ftr); + + try { + if (!mWapPushMan.addPackage( + app_id.getText().toString(), + cont.getText().toString(), + pkg.getText().toString(), + cls.getText().toString(), + act.isChecked() ? WapPushManagerParams.APP_TYPE_ACTIVITY : + WapPushManagerParams.APP_TYPE_SERVICE, + sig.isChecked(), ftr.isChecked())) { + + Log.w(LOG_TAG, "remote add pkg failed..."); + mWapPushMan.updatePackage( + app_id.getText().toString(), + cont.getText().toString(), + pkg.getText().toString(), + cls.getText().toString(), + act.isChecked() ? WapPushManagerParams.APP_TYPE_ACTIVITY : + WapPushManagerParams.APP_TYPE_SERVICE, + sig.isChecked(), ftr.isChecked()); + } + } catch (RemoteException e) { + Log.w(LOG_TAG, "remote func failed..."); + } + } + }); + + delbtn.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + EditText app_id = (EditText) findViewById(R.id.app_id); + EditText cont = (EditText) findViewById(R.id.cont); + EditText pkg = (EditText) findViewById(R.id.pkg); + EditText cls = (EditText) findViewById(R.id.cls); + // CheckBox delall = (CheckBox) findViewById(R.id.delall); + // Log.d(LOG_TAG, "button clicked"); + + try { + mWapPushMan.deletePackage( + app_id.getText().toString(), + cont.getText().toString(), + pkg.getText().toString(), + cls.getText().toString()); + // delall.isChecked()); + } catch (RemoteException e) { + Log.w(LOG_TAG, "remote func failed..."); + } + } + }); + + procbtn.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + EditText pdu = (EditText) findViewById(R.id.pdu); + EditText app_id = (EditText) findViewById(R.id.app_id); + EditText cont = (EditText) findViewById(R.id.cont); + + // WapPushOverSms wap = new WapPushOverSms(); + // wap.dispatchWapPdu(strToHex(pdu.getText().toString())); + try { + Intent intent = new Intent(); + intent.putExtra("transactionId", 0); + intent.putExtra("pduType", 6); + intent.putExtra("header", + HexDump.hexStringToByteArray(pdu.getText().toString())); + intent.putExtra("data", + HexDump.hexStringToByteArray(pdu.getText().toString())); + + mWapPushMan.processMessage( + app_id.getText().toString(), + cont.getText().toString(), + intent); + //HexDump.hexStringToByteArray(pdu.getText().toString()), 0, 6, 5, 5); + } catch (RemoteException e) { + Log.w(LOG_TAG, "remote func failed..."); + } + } + }); + } + + private IWapPushManager mWapPushMan; + private ServiceConnection conn = new ServiceConnection() { + public void onServiceDisconnected(ComponentName name) { + mWapPushMan = null; + Log.v(LOG_TAG, "service disconnected."); + } + + public void onServiceConnected(ComponentName name, IBinder service) { + mWapPushMan = IWapPushManager.Stub.asInterface(service); + Log.v(LOG_TAG, "service connected."); + } + }; + + @Override + public void onStart() { + super.onStart(); + Log.v(LOG_TAG, "onStart bind WAPPushManager service " + + IWapPushManager.class.getName()); + this.bindService(new Intent(IWapPushManager.class.getName()), conn, + Context.BIND_AUTO_CREATE); + Log.v(LOG_TAG, "bind service done."); + } + + @Override + public void onDestroy() { + super.onDestroy(); + this.unbindService(conn); + } + +} diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java new file mode 100644 index 0000000..ef491fd --- /dev/null +++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DataVerify.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.smspush.unitTests; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.util.Log; + +import com.android.internal.util.HexDump; + +/** + * To verify that receiver application receives correct body data. + */ +public class DataVerify extends Service { + private static final String LOG_TAG = "WAP PUSH"; + private static final int TIME_WAIT = 100; + private static final int WAIT_COUNT = 100; + private static byte[] mLastReceivedPdu = null; + private static boolean sDataSet = false; + + private class IDataVerifyStub extends IDataVerify.Stub { + public Context mContext; + + public IDataVerifyStub() { + } + + boolean arrayCompare(byte[] arr1, byte[] arr2) { + int i; + + if (arr1 == null || arr2 == null) { + if (arr1 == null) { + Log.w(LOG_TAG, "arr1 is null"); + } else { + Log.w(LOG_TAG, "arr2 is null"); + } + return false; + } + + if (arr1.length != arr2.length) { + return false; + } + + for (i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) return false; + } + return true; + } + + /** + * Compare pdu and received pdu + */ + public synchronized boolean verifyData(byte[] pdu) { + int cnt = 0; + + while (!sDataSet) { + // wait for the activity receive data. + try { + Thread.sleep(TIME_WAIT); + if (cnt++ > WAIT_COUNT) { + // don't wait more than 10 sec. + return false; + } + } catch (InterruptedException e) {} + } + + Log.v(LOG_TAG, "verify pdu"); + boolean ret = arrayCompare(pdu, mLastReceivedPdu); + return ret; + } + + /** + * Clear the old data. This method must be called before starting the test + */ + public void resetData() { + mLastReceivedPdu = null; + sDataSet = false; + } + } + + private final IDataVerifyStub binder = new IDataVerifyStub(); + + /** + * Constructor + */ + public DataVerify() { + } + + /** + * Receiver application must call this method when it receives the wap push message + */ + public static void SetLastReceivedPdu(byte[] pdu) { + mLastReceivedPdu = pdu; + sDataSet = true; + } + + @Override + public IBinder onBind(Intent arg0) { + return binder; + } + +} + + diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java new file mode 100644 index 0000000..5f5f121 --- /dev/null +++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/DrmReceiver.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.smspush.unitTests; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import com.android.internal.util.HexDump; + +/** + * A sample wap push receiver application for existing framework + * This class is listening for "application/vnd.oma.drm.rights+xml" message + */ +public class DrmReceiver extends BroadcastReceiver { + private static final String LOG_TAG = "WAP PUSH"; + + @Override + public void onReceive(Context context, Intent intent) { + Log.d(LOG_TAG, "DrmReceiver received."); + + byte[] body; + byte[] header; + + body = intent.getByteArrayExtra("data"); + header = intent.getByteArrayExtra("header"); + + Log.d(LOG_TAG, "header:"); + Log.d(LOG_TAG, HexDump.dumpHexString(header)); + Log.d(LOG_TAG, "body:"); + Log.d(LOG_TAG, HexDump.dumpHexString(body)); + + DataVerify.SetLastReceivedPdu(body); + } + +} + + diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl new file mode 100644 index 0000000..f0670fa --- /dev/null +++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/IDataVerify.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.smspush.unitTests; + +/** + * Interface to receiver application data verifyer class + */ +interface IDataVerify { + /** + * Verify data + */ + boolean verifyData(in byte[] pdu); + + /** + * Initialize data + */ + void resetData(); +} + diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java new file mode 100644 index 0000000..07f55ea --- /dev/null +++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverActivity.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.smspush.unitTests; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +import com.android.internal.util.HexDump; + +/** + * Activity type receiver application + */ +public class ReceiverActivity extends Activity { + private static final String LOG_TAG = "WAP PUSH"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.d(LOG_TAG, "activity created!!"); + + Intent in = getIntent(); + byte[] body; + byte[] header; + + body = in.getByteArrayExtra("data"); + header = in.getByteArrayExtra("header"); + + Log.d(LOG_TAG, "header:"); + Log.d(LOG_TAG, HexDump.dumpHexString(header)); + Log.d(LOG_TAG, "body:"); + Log.d(LOG_TAG, HexDump.dumpHexString(body)); + + DataVerify.SetLastReceivedPdu(body); + + finish(); + + } +} + diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java new file mode 100644 index 0000000..b024bf5 --- /dev/null +++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/ReceiverService.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.smspush.unitTests; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.util.Log; + +import com.android.internal.util.HexDump; + +/** + * Service type receiver application + */ +public class ReceiverService extends Service { + private static final String LOG_TAG = "WAP PUSH"; + + @Override + public void onCreate() { + super.onCreate(); + Log.d(LOG_TAG, "Receiver service created"); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.d(LOG_TAG, "Receiver service started"); + + byte[] body; + byte[] header; + body = intent.getByteArrayExtra("data"); + header = intent.getByteArrayExtra("header"); + + Log.d(LOG_TAG, "header:"); + Log.d(LOG_TAG, HexDump.dumpHexString(header)); + Log.d(LOG_TAG, "body:"); + Log.d(LOG_TAG, HexDump.dumpHexString(body)); + + DataVerify.SetLastReceivedPdu(body); + return START_STICKY; + } +} + + diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java new file mode 100644 index 0000000..9b0a36b --- /dev/null +++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java @@ -0,0 +1,2513 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.smspush.unitTests; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.provider.Telephony.Sms.Intents; +import android.test.ServiceTestCase; +import android.util.Log; +import android.util.Config; + +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.IWapPushManager; +import com.android.internal.telephony.WapPushManagerParams; +import com.android.internal.telephony.WspTypeDecoder; +import com.android.internal.util.HexDump; +import com.android.smspush.WapPushManager; + +import java.util.Random; + +/** + * This is a simple framework for a test of a Service. See {@link android.test.ServiceTestCase + * ServiceTestCase} for more information on how to write and extend service tests. + * + * To run this test, you can type: + * adb shell am instrument -w \ + * -e class com.android.smspush.unitTests.WapPushTest \ + * com.android.smspush.unitTests/android.test.InstrumentationTestRunner + */ +public class WapPushTest extends ServiceTestCase<WapPushManager> { + private static final String LOG_TAG = "WAP PUSH"; + private static final boolean LOCAL_LOGV = false; + private static final int TIME_WAIT = 100; + + protected int mAppIdValue = 0x8002; + protected String mAppIdName = "x-wap-application:*"; + protected int mContentTypeValue = 0x030a; + protected String mContentTypeName = "application/vnd.wap.sic"; + + protected String mPackageName; + protected String mClassName; + + protected byte[] mGsmHeader = { + (byte) 0x00, // sc address + (byte) 0x40, // TP-MTI + (byte) 0x04, // sender address length? + (byte) 0x81, (byte) 0x55, (byte) 0x45, // sender address? + (byte) 0x00, // data schema + (byte) 0x00, // proto ID + (byte) 0x01, (byte) 0x60, (byte) 0x12, (byte) 0x31, + (byte) 0x74, (byte) 0x34, (byte) 0x63 // time stamp + }; + + protected byte[] mUserDataHeader = { + (byte) 0x07, // UDH len + (byte) 0x06, // header len + (byte) 0x05, // port addressing type? + (byte) 0x00, // dummy + (byte) 0x0B, (byte) 0x84, // dest port + (byte) 0x23, (byte) 0xF0 // src port + }; + + protected byte[] mWspHeader; + + protected byte[] mMessageBody = { + (byte) 0x00, + (byte) 0x01, + (byte) 0x02, + (byte) 0x03, + (byte) 0x04, + (byte) 0x05, + (byte) 0x06, + (byte) 0x07, + (byte) 0x08, + (byte) 0x09, + (byte) 0x0a, + (byte) 0x0b, + (byte) 0x0c, + (byte) 0x0d, + (byte) 0x0e, + (byte) 0x0f + }; + + protected int mWspHeaderStart; + protected int mWspHeaderLen; + protected int mWspContentTypeStart; + + /** + * OMA application ID in binary form + * http://www.openmobilealliance.org/tech/omna/omna-push-app-id.aspx + */ + final int[] OMA_APPLICATION_ID_VALUES = new int[] { + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0A, + 0x8000, + 0x8001, + 0x8002, + 0x8003, + 0x8004, + 0x8005, + 0x8006, + 0x8007, + 0x8008, + 0x8009, + 0x800B, + 0x8010 + }; + + /** + * OMA application ID in string form + * http://www.openmobilealliance.org/tech/omna/omna-push-app-id.aspx + */ + final String[] OMA_APPLICATION_ID_NAMES = new String[] { + "x-wap-application:*", + "x-wap-application:push.sia", + "x-wap-application:wml.ua", + "x-wap-application:wta.ua", + "x-wap-application:mms.ua", + "x-wap-application:push.syncml", + "x-wap-application:loc.ua", + "x-wap-application:syncml.dm", + "x-wap-application:drm.ua", + "x-wap-application:emn.ua", + "x-wap-application:wv.ua", + "x-wap-microsoft:localcontent.ua", + "x-wap-microsoft:IMclient.ua", + "x-wap-docomo:imode.mail.ua", + "x-wap-docomo:imode.mr.ua", + "x-wap-docomo:imode.mf.ua", + "x-motorola:location.ua", + "x-motorola:now.ua", + "x-motorola:otaprov.ua", + "x-motorola:browser.ua", + "x-motorola:splash.ua", + "x-wap-nai:mvsw.command", + "x-wap-openwave:iota.ua" + }; + + /** + * OMA content type in binary form + * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx + */ + final int[] OMA_CONTENT_TYPE_VALUES = new int[] { + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0A, + 0x0B, + 0x0C, + 0x0D, + 0x0E, + 0x0F, + 0x10, + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x16, + 0x17, + 0x18, + 0x19, + 0x1A, + 0x1B, + 0x1C, + 0x1D, + 0x1E, + 0x1F, + 0x20, + 0x21, + 0x22, + 0x23, + 0x24, + 0x25, + 0x26, + 0x27, + 0x28, + 0x29, + 0x2A, + 0x2B, + 0x2C, + 0x2D, + 0x2E, + 0x2F, + 0x30, + 0x31, + 0x32, + 0x33, + 0x34, + 0x35, + 0x36, + 0x37, + 0x38, + 0x39, + 0x3A, + 0x3B, + 0x3C, + 0x3D, + 0x3E, + 0x3F, + 0x40, + 0x41, + 0x42, + 0x43, + 0x44, + 0x45, + 0x46, + 0x47, + 0x48, + 0x49, + 0x4A, + 0x4B, + 0x4C, + 0x4D, + 0x4E, + 0x4F, + 0x50, + 0x51, + 0x52, + 0x53, + 0x54, +// 0x55, +// 0x56, +// 0x57, +// 0x58, + 0x0201, + 0x0202, + 0x0203, + 0x0204, + 0x0205, + 0x0206, + 0x0207, + 0x0208, + 0x0209, + 0x020A, + 0x020B, + 0x020C, + 0x0300, + 0x0301, + 0x0302, + 0x0303, + 0x0304, + 0x0305, + 0x0306, + 0x0307, + 0x0308, + 0x0309, + 0x030A, + 0x030B, + 0x030C, + 0x030D, + 0x030E, + 0x030F, + 0x0310, + 0x0311, + 0x0312, + 0x0313, + 0x0314, + 0x0315, + 0x0316, + 0x0317, + 0x0318, + 0x0319, + 0x031A, + 0x031B + /*0x031C, + 0x031D*/ + }; + + /** + * OMA content type in string form + * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx + */ + final String[] OMA_CONTENT_TYPE_NAMES = new String[] { + "*/*", + "text/*", + "text/html", + "text/plain", + "text/x-hdml", + "text/x-ttml", + "text/x-vCalendar", + "text/x-vCard", + "text/vnd.wap.wml", + "text/vnd.wap.wmlscript", + "text/vnd.wap.wta-event", + "multipart/*", + "multipart/mixed", + "multipart/form-data", + "multipart/byterantes", + "multipart/alternative", + "application/*", + "application/java-vm", + "application/x-www-form-urlencoded", + "application/x-hdmlc", + "application/vnd.wap.wmlc", + "application/vnd.wap.wmlscriptc", + "application/vnd.wap.wta-eventc", + "application/vnd.wap.uaprof", + "application/vnd.wap.wtls-ca-certificate", + "application/vnd.wap.wtls-user-certificate", + "application/x-x509-ca-cert", + "application/x-x509-user-cert", + "image/*", + "image/gif", + "image/jpeg", + "image/tiff", + "image/png", + "image/vnd.wap.wbmp", + "application/vnd.wap.multipart.*", + "application/vnd.wap.multipart.mixed", + "application/vnd.wap.multipart.form-data", + "application/vnd.wap.multipart.byteranges", + "application/vnd.wap.multipart.alternative", + "application/xml", + "text/xml", + "application/vnd.wap.wbxml", + "application/x-x968-cross-cert", + "application/x-x968-ca-cert", + "application/x-x968-user-cert", + "text/vnd.wap.si", + "application/vnd.wap.sic", + "text/vnd.wap.sl", + "application/vnd.wap.slc", + "text/vnd.wap.co", + "application/vnd.wap.coc", + "application/vnd.wap.multipart.related", + "application/vnd.wap.sia", + "text/vnd.wap.connectivity-xml", + "application/vnd.wap.connectivity-wbxml", + "application/pkcs7-mime", + "application/vnd.wap.hashed-certificate", + "application/vnd.wap.signed-certificate", + "application/vnd.wap.cert-response", + "application/xhtml+xml", + "application/wml+xml", + "text/css", + "application/vnd.wap.mms-message", + "application/vnd.wap.rollover-certificate", + "application/vnd.wap.locc+wbxml", + "application/vnd.wap.loc+xml", + "application/vnd.syncml.dm+wbxml", + "application/vnd.syncml.dm+xml", + "application/vnd.syncml.notification", + "application/vnd.wap.xhtml+xml", + "application/vnd.wv.csp.cir", + "application/vnd.oma.dd+xml", + "application/vnd.oma.drm.message", + "application/vnd.oma.drm.content", + "application/vnd.oma.drm.rights+xml", + "application/vnd.oma.drm.rights+wbxml", + "application/vnd.wv.csp+xml", + "application/vnd.wv.csp+wbxml", + "application/vnd.syncml.ds.notification", + "audio/*", + "video/*", + "application/vnd.oma.dd2+xml", + "application/mikey", + "application/vnd.oma.dcd", + "application/vnd.oma.dcdc", +// "text/x-vMessage", +// "application/vnd.omads-email+wbxml", +// "text/x-vBookmark", +// "application/vnd.syncml.dm.notification", + "application/vnd.uplanet.cacheop-wbxml", + "application/vnd.uplanet.signal", + "application/vnd.uplanet.alert-wbxml", + "application/vnd.uplanet.list-wbxml", + "application/vnd.uplanet.listcmd-wbxml", + "application/vnd.uplanet.channel-wbxml", + "application/vnd.uplanet.provisioning-status-uri", + "x-wap.multipart/vnd.uplanet.header-set", + "application/vnd.uplanet.bearer-choice-wbxml", + "application/vnd.phonecom.mmc-wbxml", + "application/vnd.nokia.syncset+wbxml", + "image/x-up-wpng", + "application/iota.mmc-wbxml", + "application/iota.mmc-xml", + "application/vnd.syncml+xml", + "application/vnd.syncml+wbxml", + "text/vnd.wap.emn+xml", + "text/calendar", + "application/vnd.omads-email+xml", + "application/vnd.omads-file+xml", + "application/vnd.omads-folder+xml", + "text/directory;profile=vCard", + "application/vnd.wap.emn+wbxml", + "application/vnd.nokia.ipdc-purchase-response", + "application/vnd.motorola.screen3+xml", + "application/vnd.motorola.screen3+gzip", + "application/vnd.cmcc.setting+wbxml", + "application/vnd.cmcc.bombing+wbxml", + "application/vnd.docomo.pf", + "application/vnd.docomo.ub", + "application/vnd.omaloc-supl-init", + "application/vnd.oma.group-usage-list+xml", + "application/oma-directory+xml", + "application/vnd.docomo.pf2", + "application/vnd.oma.drm.roap-trigger+wbxml", + "application/vnd.sbm.mid2", + "application/vnd.wmf.bootstrap", + "application/vnc.cmcc.dcd+xml", + "application/vnd.sbm.cid", + "application/vnd.oma.bcast.provisioningtrigger", + /*"application/vnd.docomo.dm", + "application/vnd.oma.scidm.messages+xml"*/ + }; + + private IDataVerify mIVerify = null; + + ServiceConnection mConn = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + Log.v(LOG_TAG, "data verify interface connected."); + mIVerify = IDataVerify.Stub.asInterface(service); + } + public void onServiceDisconnected(ComponentName name) { + } + }; + + /** + * Main WapPushManager test module constructor + */ + public WapPushTest() { + super(WapPushManager.class); + mClassName = this.getClass().getName(); + mPackageName = this.getClass().getPackage().getName(); + } + + /** + * Initialize the verifier + */ + @Override + public void setUp() { + try { + super.setUp(); + // get verifier + getContext().bindService(new Intent(IDataVerify.class.getName()), + mConn, Context.BIND_AUTO_CREATE); + } catch (Exception e) { + Log.w(LOG_TAG, "super exception"); + } + // Log.d(LOG_TAG, "test setup"); + } + + private IWapPushManager mWapPush = null; + IWapPushManager getInterface() { + if (mWapPush != null) return mWapPush; + Intent startIntent = new Intent(); + startIntent.setClass(getContext(), WapPushManager.class); + IBinder service = bindService(startIntent); + + mWapPush = IWapPushManager.Stub.asInterface(service); + return mWapPush; + } + + /* + * All methods need to start with 'test'. + * Use various assert methods to pass/fail the test case. + */ + protected void utAddPackage(boolean need_sig, boolean more_proc) { + IWapPushManager iwapman = getInterface(); + + // insert new data + try { + assertTrue(iwapman.addPackage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc)); + } catch (RemoteException e) { + assertTrue(false); + } + + // verify the data + WapPushManager wpman = getService(); + assertTrue(wpman.verifyData(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc)); + } + + /** + * Add package test + */ + public void testAddPackage1() { + int originalAppIdValue = mAppIdValue; + int originalContentTypeValue = mContentTypeValue; + + utAddPackage(true, true); + mAppIdValue += 10; + utAddPackage(true, false); + mContentTypeValue += 20; + utAddPackage(false, true); + mContentTypeValue += 20; + utAddPackage(false, false); + + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + + // clean up data + try { + IWapPushManager iwapman = getInterface(); + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + mAppIdValue += 10; + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + mContentTypeValue += 20; + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + mContentTypeValue += 20; + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + } catch (RemoteException e) { + assertTrue(false); + } + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + } + + /** + * Add duprecated package test. + */ + public void testAddPackage2() { + try { + IWapPushManager iwapman = getInterface(); + + // set up data + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, 0, + false, false); + iwapman.addPackage(Integer.toString(mAppIdValue + 10), + Integer.toString(mContentTypeValue), mPackageName, mClassName, 0, + false, false); + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue + 10), mPackageName, mClassName, 0, + false, false); + + assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, 0, + false, false)); + assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue + 10), + Integer.toString(mContentTypeValue), mPackageName, mClassName, 0, + false, false)); + assertFalse(iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue + 10), mPackageName, mClassName, 0, + false, false)); + + // clean up data + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + iwapman.deletePackage(Integer.toString(mAppIdValue + 10), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue + 10), mPackageName, mClassName); + } catch (RemoteException e) { + assertTrue(false); + } + } + + protected void utUpdatePackage(boolean need_sig, boolean more_proc) { + IWapPushManager iwapman = getInterface(); + + // insert new data + try { + assertTrue(iwapman.updatePackage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc)); + } catch (RemoteException e) { + assertTrue(false); + } + + // verify the data + WapPushManager wpman = getService(); + assertTrue(wpman.verifyData( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_SERVICE, need_sig, more_proc)); + } + + /** + * Updating package test + */ + public void testUpdatePackage1() { + int originalAppIdValue = mAppIdValue; + int originalContentTypeValue = mContentTypeValue; + + // set up data + try { + IWapPushManager iwapman = getInterface(); + + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + 0, false, false); + mAppIdValue += 10; + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + 0, false, false); + mContentTypeValue += 20; + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + 0, false, false); + mContentTypeValue += 20; + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + 0, false, false); + } catch (RemoteException e) { + assertTrue(false); + } + + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + utUpdatePackage(false, false); + mAppIdValue += 10; + utUpdatePackage(false, true); + mContentTypeValue += 20; + utUpdatePackage(true, false); + mContentTypeValue += 20; + utUpdatePackage(true, true); + + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + + // clean up data + try { + IWapPushManager iwapman = getInterface(); + + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + mAppIdValue += 10; + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + mContentTypeValue += 20; + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + mContentTypeValue += 20; + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + } catch (RemoteException e) { + assertTrue(false); + } + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + } + + /** + * Updating invalid package test + */ + public void testUpdatePackage2() { + int originalAppIdValue = mAppIdValue; + int originalContentTypeValue = mContentTypeValue; + + try { + // set up data + IWapPushManager iwapman = getInterface(); + + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + 0, false, false); + assertFalse(iwapman.updatePackage( + Integer.toString(mAppIdValue + 10), + Integer.toString(mContentTypeValue), + mPackageName, mClassName, 0, false, false)); + assertFalse(iwapman.updatePackage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue + 10), + mPackageName, mClassName, 0, false, false)); + assertTrue(iwapman.updatePackage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + mPackageName + "dummy_data", mClassName, 0, false, false)); + assertTrue(iwapman.updatePackage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + mPackageName, mClassName + "dummy_data", 0, false, false)); + // clean up data + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, + mClassName + "dummy_data"); + } catch (RemoteException e) { + assertTrue(false); + } + } + + protected void utDeletePackage() { + IWapPushManager iwapman = getInterface(); + + try { + assertTrue(iwapman.deletePackage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + mPackageName, mClassName)); + } catch (RemoteException e) { + assertTrue(false); + } + + // verify the data + WapPushManager wpman = getService(); + assertTrue(!wpman.isDataExist( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + mPackageName, mClassName)); + } + + /** + * Deleting package test + */ + public void testDeletePackage1() { + int originalAppIdValue = mAppIdValue; + int originalContentTypeValue = mContentTypeValue; + + // set up data + try { + IWapPushManager iwapman = getInterface(); + + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + 0, false, false); + mAppIdValue += 10; + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + 0, false, false); + mContentTypeValue += 20; + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + 0, false, false); + mContentTypeValue += 20; + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + 0, false, false); + } catch (RemoteException e) { + assertTrue(false); + } + + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + utDeletePackage(); + mAppIdValue += 10; + utDeletePackage(); + mContentTypeValue += 20; + utDeletePackage(); + mContentTypeValue += 20; + utDeletePackage(); + + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + } + + /** + * Deleting invalid package test + */ + public void testDeletePackage2() { + int originalAppIdValue = mAppIdValue; + int originalContentTypeValue = mContentTypeValue; + + try { + // set up data + IWapPushManager iwapman = getInterface(); + + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + 0, false, false); + + assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue + 10), + Integer.toString(mContentTypeValue), mPackageName, mClassName)); + assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue + 20), mPackageName, mClassName)); + assertFalse(iwapman.deletePackage(Integer.toString(mAppIdValue + 10), + Integer.toString(mContentTypeValue + 20), mPackageName, mClassName)); + + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + + } catch (RemoteException e) { + assertTrue(false); + } + } + + + protected int encodeUint32(int uint32Val, byte[] arr, int start) { + int bit = 1; + int topbit = 0; + int encodeLen; + int tmpVal; + + assertTrue(uint32Val >= 0); + for (int i = 0; i < 31; i++) { + if ((bit & uint32Val) > 0) topbit = i; + bit = (bit << 1); + } + encodeLen = topbit/7 + 1; + if (arr == null) return encodeLen; + + //Log.d(LOG_TAG, "uint32Val = " + Integer.toHexString(uint32Val) + ", topbit = " + // + topbit + ", encodeLen = " + encodeLen); + + tmpVal = uint32Val; + for (int i = encodeLen - 1; i >= 0; i--) { + long val = 0; + if (i < encodeLen - 1) val = 0x80; + val |= tmpVal & 0x7f; + arr[start + i] = (byte) (val & 0xFF); + tmpVal = (tmpVal >> 7); + } + return encodeLen; + } + + protected int encodeShortInt(int sintVal, byte[] arr, int start) { + int encodeLen = 0; + + if (sintVal >= 0x80) return encodeLen; + encodeLen = 1; + arr[start] = (byte) (sintVal | 0x80); + return encodeLen; + } + + + /** + * Generate Random WSP header with integer application ID + */ + protected void createRandomWspHeader(byte[] arr, Random rd, int headerStart, + boolean noAppId) { + + boolean appIdAdded = false; + + Log.d(LOG_TAG, "headerStart = " + headerStart + ", appId = " + mAppIdValue + + "(" + Integer.toHexString(mAppIdValue) + ")"); + Log.d(LOG_TAG, "random arr length:" + arr.length); + String typename[] = new String[] { "short int", "long int", "string", "uint32"}; + + while (!appIdAdded) { + int type; + int index = headerStart; + int len = arr.length; + int i; + boolean addAppid = false; + int tmpVal = 0; + int tmpVal2 = 0; + + while (true) { + int add; + + /* + * field name + * 0: short int + * 1: long int + * 2: text + * (no uint for param value) + */ + type = rd.nextInt(3); + switch (type) { + case 0: // header short integer + if (index > 100 && !appIdAdded) addAppid = true; + add = 1; + break; + case 1: // header long int + add = 1 + rd.nextInt(29); + break; + default: // header string + add = 2 + rd.nextInt(10); + break; + } + if (index + add >= len) break; + + // fill header name + switch (type) { + case 0: // header short integer + if (!addAppid) { + do { + arr[index] = (byte) (0x80 | rd.nextInt(128)); + } while (arr[index] == (byte) 0xaf); + } else { + Log.d(LOG_TAG, "appId added."); + arr[index] = (byte) 0xaf; + // if noAppId case, appId fld must be decieved. + if (noAppId) arr[index]++; + } + break; + case 1: // header long int + arr[index] = (byte) (add - 1); + tmpVal2 = 0; + for (i = 1; i < add; i++) { + tmpVal = rd.nextInt(255); + tmpVal2 = (tmpVal2 << 8) | tmpVal; + arr[index + i] = (byte) tmpVal; + } + // don't set application id + if (tmpVal2 == 0x2f) arr[index + 1]++; + break; + default: // header string + for (i = 0; i < add - 1; i++) { + tmpVal = rd.nextInt(127); + if (tmpVal < 32) tmpVal= (32 + tmpVal); + arr[index + i] = (byte) tmpVal; + } + arr[index + i] = (byte) 0x0; + break; + } + + if (LOCAL_LOGV) { + Log.d(LOG_TAG, "field name index:" + index); + Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add); + if (type != 2) { + for (i = index; i< index + add; i++) { + System.out.print(Integer.toHexString(0xff & arr[i])); + System.out.print(' '); + } + } else { + System.out.print(Integer.toHexString(0xff & arr[index])); + System.out.print(' '); + String str = new String(arr, index + 1, add - 2); + for (i = 0; i < str.length(); i++) { + System.out.print(str.charAt(i)); + System.out.print(' '); + } + } + System.out.print('\n'); + } + index += add; + + + /* + * field value + * 0: short int + * 1: long int + * 2: text + * 3: uint + */ + if (addAppid) { + type = 1; + } else { + type = rd.nextInt(4); + } + switch (type) { + case 0: // header short integer + add = 1; + break; + case 1: // header long int + if (addAppid) { + int bit = 1; + int topBit = 0; + + for (i = 0; i < 31; i++) { + if ((mAppIdValue & bit) > 0) topBit = i; + bit = (bit << 1); + } + add = 2 + topBit/8; + } else { + add = 1 + rd.nextInt(29); + } + break; + case 2: // header string + add = 2 + rd.nextInt(10); + break; + default: // uint32 + add = 6; + } + if (index + add >= len) break; + + // fill field value + switch (type) { + case 0: // header short int + arr[index] = (byte) (0x80 | rd.nextInt(128)); + break; + case 1: // header long int + if (addAppid) { + addAppid = false; + appIdAdded = true; + + arr[index] = (byte) (add - 1); + tmpVal = mAppIdValue; + for (i = add; i > 1; i--) { + arr[index + i - 1] = (byte) (tmpVal & 0xff); + tmpVal = (tmpVal >> 8); + } + } else { + arr[index] = (byte) (add - 1); + for (i = 1; i < add; i++) { + arr[index + i] = (byte) rd.nextInt(255); + } + } + break; + case 2:// header string + for (i = 0; i < add - 1; i++) { + tmpVal = rd.nextInt(127); + if (tmpVal < 32) tmpVal= (32 + tmpVal); + arr[index + i] = (byte) tmpVal; + } + arr[index + i] = (byte) 0x0; + break; + default: // header uvarint + arr[index] = (byte) 31; + tmpVal = rd.nextInt(0x0FFFFFFF); + add = 1 + encodeUint32(tmpVal, null, index + 1); + encodeUint32(tmpVal, arr, index + 1); + break; + + } + + if (LOCAL_LOGV) { + Log.d(LOG_TAG, "field value index:" + index); + Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add); + if (type != 2) { + for (i = index; i< index + add; i++) { + System.out.print(Integer.toHexString(0xff & arr[i])); + System.out.print(' '); + } + } else { + System.out.print(Integer.toHexString(0xff & arr[index])); + System.out.print(' '); + String str = new String(arr, index + 1, add - 2); + for (i = 0; i < str.length(); i++) { + System.out.print(str.charAt(i)); + System.out.print(' '); + } + } + System.out.print('\n'); + } + index += add; + } + if (noAppId) break; + } + + Log.d(LOG_TAG, HexDump.dumpHexString(arr)); + } + + /** + * Generate Random WSP header with string application ID + */ + protected void createRandomWspHeaderStrAppId(byte[] arr, Random rd, int headerStart, + boolean randomStr) { + + boolean appIdAdded = false; + + Log.d(LOG_TAG, "random arr length:" + arr.length); + String typename[] = new String[] { "short int", "long int", "string", "uint32"}; + + while (!appIdAdded) { + int type; + int index = headerStart; + int len = arr.length; + int i; + boolean addAppid = false; + int tmpVal = 0; + int tmpVal2 = 0; + + while (true) { + int add; + + /* + * field name + * 0: short int + * 1: long int + * 2: text + * (no uint for param value) + */ + type = rd.nextInt(3); + switch (type) { + case 0: // header short integer + if (index > 100 && !appIdAdded) addAppid = true; + add = 1; + break; + case 1: // header long int + add = 1 + rd.nextInt(29); + break; + default: // header string + add = 2 + rd.nextInt(10); + break; + } + if (index + add >= len) break; + + // fill header name + switch (type) { + case 0: // header short integer + if (!addAppid) { + do { + arr[index] = (byte) (0x80 | rd.nextInt(128)); + } while (arr[index] == (byte) 0xaf); + } else { + Log.d(LOG_TAG, "appId added."); + arr[index] = (byte) 0xaf; + } + break; + case 1: // header long int + arr[index] = (byte) (add - 1); + tmpVal2 = 0; + for (i = 1; i < add; i++) { + tmpVal = rd.nextInt(255); + tmpVal2 = (tmpVal2 << 8) | tmpVal; + arr[index + i] = (byte) tmpVal; + } + // don't set application id + if (tmpVal2 == 0x2f) arr[index + 1]++; + break; + default: // header string + for (i = 0; i < add - 1; i++) { + tmpVal = rd.nextInt(127); + if (tmpVal < 32) tmpVal= (32 + tmpVal); + arr[index + i] = (byte) tmpVal; + } + arr[index + i] = (byte) 0x0; + break; + } + + if (LOCAL_LOGV) { + Log.d(LOG_TAG, "field name index:" + index); + Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add); + if (type != 2) { + for (i = index; i < index + add; i++) { + System.out.print(Integer.toHexString(0xff & arr[i])); + System.out.print(' '); + } + } else { + System.out.print(Integer.toHexString(0xff & arr[index])); + System.out.print(' '); + String str = new String(arr, index + 1, add - 2); + for (i = 0; i < str.length(); i++) { + System.out.print(str.charAt(i)); + System.out.print(' '); + } + } + System.out.print('\n'); + } + index += add; + + + /* + * field value + * 0: short int + * 1: long int + * 2: text + * 3: uint + */ + if (addAppid) { + type = 2; + } else { + type = rd.nextInt(4); + } + switch (type) { + case 0: // header short integer + add = 1; + break; + case 1: // header long int + add = 1 + rd.nextInt(29); + break; + case 2: // header string + if (addAppid) { + if (randomStr) { + add = 1 + rd.nextInt(10); + byte[] randStr= new byte[add]; + for (i = 0; i < add; i++) { + tmpVal = rd.nextInt(127); + if (tmpVal < 32) tmpVal= (32 + tmpVal); + randStr[i] = (byte) tmpVal; + } + mAppIdName = new String(randStr); + } + add = mAppIdName.length() + 1; + } else { + add = 2 + rd.nextInt(10); + } + break; + default: // uint32 + add = 6; + } + if (index + add >= len) break; + + // fill field value + switch (type) { + case 0: // header short int + arr[index] = (byte) (0x80 | rd.nextInt(128)); + break; + case 1: // header long int + arr[index] = (byte) (add - 1); + for (i = 1; i < add; i++) + arr[index + i] = (byte) rd.nextInt(255); + break; + case 2:// header string + if (addAppid) { + addAppid = false; + appIdAdded = true; + for (i = 0; i < add - 1; i++) { + arr[index + i] = (byte) (mAppIdName.charAt(i)); + } + Log.d(LOG_TAG, "mAppIdName added [" + mAppIdName + "]"); + } else { + for (i = 0; i < add - 1; i++) { + tmpVal = rd.nextInt(127); + if (tmpVal < 32) tmpVal= (32 + tmpVal); + arr[index + i] = (byte) tmpVal; + } + } + arr[index + i] = (byte) 0x0; + break; + default: // header uvarint + arr[index] = (byte) 31; + tmpVal = rd.nextInt(0x0FFFFFFF); + add = 1 + encodeUint32(tmpVal, null, index + 1); + encodeUint32(tmpVal, arr, index + 1); + break; + + } + + if (LOCAL_LOGV) { + Log.d(LOG_TAG, "field value index:" + index); + Log.d(LOG_TAG, "type:" + typename[type] + ", add:" + add); + if (type != 2) { + for (i = index; i < index + add; i++) { + System.out.print(Integer.toHexString(0xff & arr[i])); + System.out.print(' '); + } + } else { + System.out.print(Integer.toHexString(0xff & arr[index])); + System.out.print(' '); + String str = new String(arr, index + 1, add - 2); + for (i = 0; i < str.length(); i++) { + System.out.print(str.charAt(i)); + System.out.print(' '); + } + } + System.out.print('\n'); + } + index += add; + } + } + + Log.d(LOG_TAG, "headerStart = " + headerStart + ", mAppIdName = " + mAppIdName); + Log.d(LOG_TAG, HexDump.dumpHexString(arr)); + } + + protected byte[] createPDU(int testNum) { + byte[] array = null; + // byte[] wsp = null; + + switch (testNum) { + // sample pdu + case 1: + byte[] array1 = { + (byte) 0x00, // TID + (byte) 0x06, // Type = wap push + (byte) 0x00, // Length to be set later. + + // Content-Type + (byte) 0x03, (byte) 0x02, + (byte) ((mContentTypeValue >> 8) & 0xff), + (byte) (mContentTypeValue & 0xff), + + // Application-id + (byte) 0xaf, (byte) 0x02, + (byte) ((mAppIdValue >> 8) & 0xff), + (byte) (mAppIdValue& 0xff) + }; + array1[2] = (byte) (array1.length - 3); + mWspHeader = array1; + mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + 7; + mWspHeaderLen = array1.length; + break; + + // invalid wsp header + case 2: + byte[] array2 = { + (byte) 0x00, // invalid data + }; + mWspHeader = array2; + mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length; + mWspHeaderLen = array2.length; + break; + + // random wsp header + case 3: + Random rd = new Random(); + int arrSize = 150 + rd.nextInt(100); + byte[] array3 = new byte[arrSize]; + int hdrEncodeLen; + + array3[0] = (byte) 0x0; + array3[1] = (byte) 0x6; + hdrEncodeLen = encodeUint32(array3.length, null, 2); + hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2); + array3[hdrEncodeLen + 2] = (byte) 0x3; + array3[hdrEncodeLen + 3] = (byte) 0x2; + array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff); + array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff); + createRandomWspHeader(array3, rd, hdrEncodeLen + 6, false); + mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6; + mWspHeaderLen = array3.length; + + Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue + + "(" + Integer.toHexString(mContentTypeValue) + ")"); + + mWspHeader = array3; + break; + + // random wsp header w/o appid + case 4: + rd = new Random(); + arrSize = 150 + rd.nextInt(100); + array3 = new byte[arrSize]; + + array3[0] = (byte) 0x0; + array3[1] = (byte) 0x6; + hdrEncodeLen = encodeUint32(array3.length, null, 2); + hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2); + array3[hdrEncodeLen + 2] = (byte) 0x3; + array3[hdrEncodeLen + 3] = (byte) 0x2; + array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff); + array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff); + createRandomWspHeader(array3, rd, hdrEncodeLen + 6, true); + mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6; + mWspHeaderLen = array3.length; + + Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue + + "(" + Integer.toHexString(mContentTypeValue) + ")"); + + mWspHeader = array3; + break; + + // random wsp header w/ random appid string + case 5: + rd = new Random(); + arrSize = 150 + rd.nextInt(100); + array3 = new byte[arrSize]; + + array3[0] = (byte) 0x0; + array3[1] = (byte) 0x6; + hdrEncodeLen = encodeUint32(array3.length, null, 2); + hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2); + array3[hdrEncodeLen + 2] = (byte) 0x3; + array3[hdrEncodeLen + 3] = (byte) 0x2; + array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff); + array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff); + createRandomWspHeaderStrAppId(array3, rd, hdrEncodeLen + 6, true); + mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6; + mWspHeaderLen = array3.length; + + Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue + + "(" + Integer.toHexString(mContentTypeValue) + ")"); + + mWspHeader = array3; + break; + + // random wsp header w/ OMA appid string + case 6: + rd = new Random(); + arrSize = 150 + rd.nextInt(100); + array3 = new byte[arrSize]; + + array3[0] = (byte) 0x0; + array3[1] = (byte) 0x6; + hdrEncodeLen = encodeUint32(array3.length, null, 2); + hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2); + array3[hdrEncodeLen + 2] = (byte) 0x3; + array3[hdrEncodeLen + 3] = (byte) 0x2; + array3[hdrEncodeLen + 4] = (byte) ((mContentTypeValue >> 8) & 0xff); + array3[hdrEncodeLen + 5] = (byte) (mContentTypeValue & 0xff); + createRandomWspHeaderStrAppId(array3, rd, hdrEncodeLen + 6, false); + mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + hdrEncodeLen + 6; + mWspHeaderLen = array3.length; + + Log.d(LOG_TAG, "mContentTypeValue = " + mContentTypeValue + + "(" + Integer.toHexString(mContentTypeValue) + ")"); + + mWspHeader = array3; + break; + + // random wsp header w/ OMA content type + case 7: + rd = new Random(); + arrSize = 150 + rd.nextInt(100); + array3 = new byte[arrSize]; + + array3[0] = (byte) 0x0; + array3[1] = (byte) 0x6; + hdrEncodeLen = encodeUint32(array3.length, null, 2); + hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2); + + // encode content type + int contentLen = mContentTypeName.length(); + int next = 2 + hdrEncodeLen; + mWspContentTypeStart = mGsmHeader.length + mUserDataHeader.length + next; + // next += encodeUint32(contentLen, array3, next); + int i; + Log.d(LOG_TAG, "mContentTypeName = " + mContentTypeName + + ", contentLen = " + contentLen); + + for (i = 0; i < contentLen; i++) { + array3[next + i] = (byte) mContentTypeName.charAt(i); + } + array3[next + i] = (byte) 0x0; + + createRandomWspHeader(array3, rd, next + contentLen + 1, false); + mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + + next + contentLen + 1; + mWspHeaderLen = array3.length; + + mWspHeader = array3; + break; + + // random wsp header w/ OMA content type, OMA app ID + case 8: + rd = new Random(); + arrSize = 150 + rd.nextInt(100); + array3 = new byte[arrSize]; + + array3[0] = (byte) 0x0; + array3[1] = (byte) 0x6; + hdrEncodeLen = encodeUint32(array3.length, null, 2); + hdrEncodeLen = encodeUint32(array3.length - hdrEncodeLen - 2, array3, 2); + + // encode content type + contentLen = mContentTypeName.length(); + next = 2 + hdrEncodeLen; + mWspContentTypeStart = mGsmHeader.length + mUserDataHeader.length + next; + // next += encodeUint32(contentLen, array3, next); + Log.d(LOG_TAG, "mContentTypeName = " + mContentTypeName + + ", contentLen = " + contentLen); + + for (i = 0; i < contentLen; i++) { + array3[next + i] = (byte) mContentTypeName.charAt(i); + } + array3[next + i] = (byte) 0x0; + + createRandomWspHeaderStrAppId(array3, rd, next + contentLen + 1, false); + mWspHeaderStart = mGsmHeader.length + mUserDataHeader.length + + next + contentLen + 1; + mWspHeaderLen = array3.length; + + mWspHeader = array3; + break; + + default: + return null; + } + array = new byte[mGsmHeader.length + mUserDataHeader.length + mWspHeader.length + + mMessageBody.length]; + System.arraycopy(mGsmHeader, 0, array, 0, mGsmHeader.length); + System.arraycopy(mUserDataHeader, 0, array, + mGsmHeader.length, mUserDataHeader.length); + System.arraycopy(mWspHeader, 0, array, + mGsmHeader.length + mUserDataHeader.length, mWspHeader.length); + System.arraycopy(mMessageBody, 0, array, + mGsmHeader.length + mUserDataHeader.length + mWspHeader.length, + mMessageBody.length); + return array; + + } + + Intent createIntent(int pduType, int tranId) { + Intent intent = new Intent(); + intent.putExtra("transactionId", tranId); + intent.putExtra("pduType", pduType); + intent.putExtra("header", mGsmHeader); + intent.putExtra("data", mMessageBody); + // intent.putExtra("contentTypeParameters", null); + return intent; + } + + /** + * Message processing test, start activity + */ + public void testProcessMsg1() { + byte[] pdu = createPDU(1); + int headerLen = pdu.length - + (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length); + int pduType = 6; + int tranId = 0; + String originalPackageName = mPackageName; + String originalClassName = mClassName; + + try { + + mClassName = "com.android.smspush.unitTests.ReceiverActivity"; + + // set up data + IWapPushManager iwapman = getInterface(); + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_ACTIVITY, false, false); + + assertTrue((iwapman.processMessage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + createIntent(pduType, tranId)) + & WapPushManagerParams.MESSAGE_HANDLED) == + WapPushManagerParams.MESSAGE_HANDLED); + + // clean up data + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + + } catch (RemoteException e) { + assertTrue(false); + } + + mPackageName = originalPackageName; + mClassName = originalClassName; + } + + /** + * Message processing test, start service + */ + public void testProcessMsg2() { + byte[] pdu = createPDU(1); + int headerLen = pdu.length - (mGsmHeader.length + + mUserDataHeader.length + mMessageBody.length); + int pduType = 6; + int tranId = 0; + String originalPackageName = mPackageName; + String originalClassName = mClassName; + + try { + + mClassName = "com.android.smspush.unitTests.ReceiverService"; + + // set up data + IWapPushManager iwapman = getInterface(); + + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_SERVICE, false, false); + + assertTrue((iwapman.processMessage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + createIntent(pduType, tranId)) + & WapPushManagerParams.MESSAGE_HANDLED) == + WapPushManagerParams.MESSAGE_HANDLED); + + // clean up data + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + + } catch (RemoteException e) { + assertTrue(false); + } + + mPackageName = originalPackageName; + mClassName = originalClassName; + } + + /** + * Message processing test, no signature + */ + public void testProcessMsg3() { + byte[] pdu = createPDU(1); + int headerLen = pdu.length - + (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length); + int pduType = 6; + int tranId = 0; + String originalPackageName = mPackageName; + String originalClassName = mClassName; + + try { + + mPackageName = "com.android.development"; + mClassName = "com.android.development.Development"; + + // set up data + IWapPushManager iwapman = getInterface(); + + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_SERVICE, true, false); + + assertFalse((iwapman.processMessage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + createIntent(pduType, tranId)) + & WapPushManagerParams.MESSAGE_HANDLED) == + WapPushManagerParams.MESSAGE_HANDLED); + + // clean up data + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + + } catch (RemoteException e) { + assertTrue(false); + } + + mPackageName = originalPackageName; + mClassName = originalClassName; + } + + IDataVerify getVerifyInterface() { + while (mIVerify == null) { + // wait for the activity receive data. + try { + Thread.sleep(TIME_WAIT); + } catch (InterruptedException e) {} + } + return mIVerify; + } + + + /** + * Message processing test, received body data verification test + */ + public void testProcessMsg4() { + byte[] originalMessageBody = mMessageBody; + mMessageBody = new byte[] { + (byte) 0xee, + (byte) 0xff, + (byte) 0xee, + (byte) 0xff, + (byte) 0xee, + (byte) 0xff, + (byte) 0xee, + (byte) 0xff, + (byte) 0xee, + (byte) 0xff, + (byte) 0xee, + (byte) 0xff, + }; + + byte[] pdu = createPDU(1); + int headerLen = pdu.length - + (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length); + int pduType = 6; + int tranId = 0; + String originalPackageName = mPackageName; + String originalClassName = mClassName; + + try { + IWapPushManager iwapman = getInterface(); + IDataVerify dataverify = getVerifyInterface(); + + dataverify.resetData(); + + // set up data + mClassName = "com.android.smspush.unitTests.ReceiverActivity"; + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_ACTIVITY, false, false); + + iwapman.processMessage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + createIntent(pduType, tranId)); + + // clean up data + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + + assertTrue(dataverify.verifyData(mMessageBody)); + + // set up data + dataverify.resetData(); + mClassName = "com.android.smspush.unitTests.ReceiverService"; + mMessageBody = new byte[] { + (byte) 0xaa, + (byte) 0xbb, + (byte) 0x11, + (byte) 0x22, + (byte) 0xaa, + (byte) 0xbb, + (byte) 0x11, + (byte) 0x22, + (byte) 0xaa, + (byte) 0xbb, + (byte) 0x11, + (byte) 0x22, + (byte) 0xaa, + (byte) 0xbb, + (byte) 0x11, + (byte) 0x22, + (byte) 0xaa, + (byte) 0xbb, + (byte) 0x11, + (byte) 0x22, + (byte) 0xaa, + (byte) 0xbb, + (byte) 0x11, + (byte) 0x22, + }; + pdu = createPDU(1); + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_SERVICE, false, false); + + iwapman.processMessage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + createIntent(pduType, tranId)); + + // clean up data + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + + // Log.d(LOG_TAG, HexDump.dumpHexString(mMessageBody)); + assertTrue(dataverify.verifyData(mMessageBody)); + } catch (RemoteException e) { + assertTrue(false); + } + + mPackageName = originalPackageName; + mClassName = originalClassName; + mMessageBody = originalMessageBody; + } + + /** + * Message processing test, send invalid sms data + */ + public void testProcessMsg5() { + byte[] pdu = createPDU(2); + int headerLen = pdu.length - + (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length); + int pduType = 6; + int tranId = 0; + String originalPackageName = mPackageName; + String originalClassName = mClassName; + + try { + + mClassName = "com.android.smspush.unitTests.ReceiverActivity"; + + // set up data + IWapPushManager iwapman = getInterface(); + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_ACTIVITY, false, false); + + assertTrue((iwapman.processMessage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + createIntent(pduType, tranId)) + & WapPushManagerParams.MESSAGE_HANDLED) == + WapPushManagerParams.MESSAGE_HANDLED); + + // clean up data + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + + } catch (RemoteException e) { + assertTrue(false); + } + + mPackageName = originalPackageName; + mClassName = originalClassName; + } + + /** + * Message processing test, no receiver application + */ + public void testProcessMsg6() { + byte[] pdu = createPDU(1); + int headerLen = pdu.length - + (mGsmHeader.length + mUserDataHeader.length + mMessageBody.length); + int pduType = 6; + int tranId = 0; + String originalPackageName = mPackageName; + String originalClassName = mClassName; + + try { + + mClassName = "com.android.smspush.unitTests.NoReceiver"; + + // set up data + IWapPushManager iwapman = getInterface(); + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_ACTIVITY, false, false); + + assertFalse((iwapman.processMessage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + createIntent(pduType, tranId)) + & WapPushManagerParams.MESSAGE_HANDLED) == + WapPushManagerParams.MESSAGE_HANDLED); + + // clean up data + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + + // set up data + iwapman.addPackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_SERVICE, false, false); + + assertFalse((iwapman.processMessage( + Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), + createIntent(pduType, tranId)) + & WapPushManagerParams.MESSAGE_HANDLED) == + WapPushManagerParams.MESSAGE_HANDLED); + + // clean up data + iwapman.deletePackage(Integer.toString(mAppIdValue), + Integer.toString(mContentTypeValue), mPackageName, mClassName); + + } catch (RemoteException e) { + assertTrue(false); + } + + mPackageName = originalPackageName; + mClassName = originalClassName; + } + + /** + * WspTypeDecoder test, normal pdu + */ + public void testDecoder1() { + boolean res; + int originalAppIdValue = mAppIdValue; + Random rd = new Random(); + + for (int i = 0; i < 10; i++) { + mAppIdValue = rd.nextInt(0xFFFF); + byte[] pdu = createPDU(1); + WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu); + + res = pduDecoder.seekXWapApplicationId(mWspHeaderStart, + mWspHeaderStart + mWspHeaderLen - 1); + assertTrue(res); + + int index = (int) pduDecoder.getValue32(); + res = pduDecoder.decodeXWapApplicationId(index); + assertTrue(res); + + Log.d(LOG_TAG, "mAppIdValue: " + mAppIdValue + + ", val: " + pduDecoder.getValue32()); + assertTrue(mAppIdValue == (int) pduDecoder.getValue32()); + } + + mAppIdValue = originalAppIdValue; + } + + /** + * WspTypeDecoder test, no header + */ + public void testDecoder2() { + boolean res; + int originalAppIdValue = mAppIdValue; + Random rd = new Random(); + + mAppIdValue = rd.nextInt(0xFFFF); + byte[] pdu = createPDU(2); + WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu); + + res = pduDecoder.seekXWapApplicationId(mWspHeaderStart, + mWspHeaderStart + mWspHeaderLen - 1); + assertFalse(res); + + mAppIdValue = originalAppIdValue; + } + + /** + * WspTypeDecoder test, decode appid test + */ + public void testDecoder3() { + boolean res; + int originalAppIdValue = mAppIdValue; + int originalContentTypeValue = mContentTypeValue; + Random rd = new Random(); + + for (int i = 0; i < 100; i++) { + mAppIdValue = rd.nextInt(0x0FFFFFFF); + mContentTypeValue = rd.nextInt(0x0FFF); + byte[] pdu = createPDU(3); + WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu); + + res = pduDecoder.seekXWapApplicationId(mWspHeaderStart, + mWspHeaderStart + mWspHeaderLen - 1); + assertTrue(res); + + int index = (int) pduDecoder.getValue32(); + res = pduDecoder.decodeXWapApplicationId(index); + assertTrue(res); + + Log.d(LOG_TAG, "mAppIdValue: " + mAppIdValue + + ", val: " + pduDecoder.getValue32()); + assertTrue(mAppIdValue == (int) pduDecoder.getValue32()); + } + + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + } + + /* + public void testEnc() { + byte[] arr = new byte[20]; + int index = 0; + index += encodeUint32(0x87a5, arr, index); + index += encodeUint32(0x1, arr, index); + index += encodeUint32(0x9b, arr, index); + index += encodeUint32(0x10, arr, index); + index += encodeUint32(0xe0887, arr, index); + index += encodeUint32(0x791a23d0, arr, index); + + Log.d(LOG_TAG, HexDump.dumpHexString(arr)); + } + */ + + /** + * WspTypeDecoder test, no appid test + */ + public void testDecoder4() { + boolean res; + int originalAppIdValue = mAppIdValue; + int originalContentTypeValue = mContentTypeValue; + Random rd = new Random(); + + for (int i = 0; i < 100; i++) { + mAppIdValue = rd.nextInt(0x0FFFFFFF); + mContentTypeValue = rd.nextInt(0x0FFF); + byte[] pdu = createPDU(4); + WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu); + + res = pduDecoder.seekXWapApplicationId(mWspHeaderStart, + mWspHeaderStart + mWspHeaderLen - 1); + assertFalse(res); + + } + + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + } + + /** + * WspTypeDecoder test, decode string appid test + */ + public void testDecoder5() { + boolean res; + String originalAppIdName = mAppIdName; + int originalContentTypeValue = mContentTypeValue; + Random rd = new Random(); + + for (int i = 0; i < 10; i++) { + mAppIdValue = rd.nextInt(0x0FFFFFFF); + mContentTypeValue = rd.nextInt(0x0FFF); + byte[] pdu = createPDU(5); + WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu); + + res = pduDecoder.seekXWapApplicationId(mWspHeaderStart, + mWspHeaderStart + mWspHeaderLen - 1); + assertTrue(res); + + int index = (int) pduDecoder.getValue32(); + res = pduDecoder.decodeXWapApplicationId(index); + assertTrue(res); + + Log.d(LOG_TAG, "mAppIdValue: [" + mAppIdName + "], val: [" + + pduDecoder.getValueString() + "]"); + assertTrue(mAppIdName.equals(pduDecoder.getValueString())); + } + + mAppIdName = originalAppIdName; + mContentTypeValue = originalContentTypeValue; + } + + /** + * WspTypeDecoder test, decode string appid test + */ + public void testDecoder6() { + boolean res; + String originalAppIdName = mAppIdName; + int originalContentTypeValue = mContentTypeValue; + Random rd = new Random(); + + for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length; i++) { + mAppIdName = OMA_APPLICATION_ID_NAMES[i]; + mContentTypeValue = rd.nextInt(0x0FFF); + byte[] pdu = createPDU(6); + WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu); + + res = pduDecoder.seekXWapApplicationId(mWspHeaderStart, + mWspHeaderStart + mWspHeaderLen - 1); + assertTrue(res); + + int index = (int) pduDecoder.getValue32(); + res = pduDecoder.decodeXWapApplicationId(index); + assertTrue(res); + + Log.d(LOG_TAG, "mAppIdValue: [" + mAppIdName + "], val: [" + + pduDecoder.getValueString() + "]"); + assertTrue(mAppIdName.equals(pduDecoder.getValueString())); + } + + mAppIdName = originalAppIdName; + mContentTypeValue = originalContentTypeValue; + } + + /** + * WspTypeDecoder test, decode OMA content type + */ + public void testDecoder7() { + boolean res; + String originalAppIdName = mAppIdName; + int originalContentTypeValue = mContentTypeValue; + Random rd = new Random(); + + for (int i = 0; i < OMA_CONTENT_TYPE_NAMES.length; i++) { + mContentTypeName = OMA_CONTENT_TYPE_NAMES[i]; + byte[] pdu = createPDU(7); + WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu); + + res = pduDecoder.decodeContentType(mWspContentTypeStart); + assertTrue(res); + + Log.d(LOG_TAG, "mContentTypeName: [" + mContentTypeName + "], val: [" + + pduDecoder.getValueString() + "]"); + assertTrue(mContentTypeName.equals(pduDecoder.getValueString())); + } + + mAppIdName = originalAppIdName; + mContentTypeValue = originalContentTypeValue; + } + + + /** + * Copied from WapPushOverSms. + * The code flow is not changed from the original. + */ + public int dispatchWapPdu(byte[] pdu, IWapPushManager wapPushMan) { + + if (Config.DEBUG) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu)); + + int index = 0; + int transactionId = pdu[index++] & 0xFF; + int pduType = pdu[index++] & 0xFF; + int headerLength = 0; + + if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) && + (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) { + if (Config.DEBUG) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType); + return Intents.RESULT_SMS_HANDLED; + } + + WspTypeDecoder pduDecoder = new WspTypeDecoder(pdu); + + /** + * Parse HeaderLen(unsigned integer). + * From wap-230-wsp-20010705-a section 8.1.2 + * The maximum size of a uintvar is 32 bits. + * So it will be encoded in no more than 5 octets. + */ + if (pduDecoder.decodeUintvarInteger(index) == false) { + if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Length error."); + return Intents.RESULT_SMS_GENERIC_ERROR; + } + headerLength = (int) pduDecoder.getValue32(); + index += pduDecoder.getDecodedDataLength(); + + int headerStartIndex = index; + + /** + * Parse Content-Type. + * From wap-230-wsp-20010705-a section 8.4.2.24 + * + * Content-type-value = Constrained-media | Content-general-form + * Content-general-form = Value-length Media-type + * Media-type = (Well-known-media | Extension-Media) *(Parameter) + * Value-length = Short-length | (Length-quote Length) + * Short-length = <Any octet 0-30> (octet <= WAP_PDU_SHORT_LENGTH_MAX) + * Length-quote = <Octet 31> (WAP_PDU_LENGTH_QUOTE) + * Length = Uintvar-integer + */ + if (pduDecoder.decodeContentType(index) == false) { + if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Content-Type error."); + return Intents.RESULT_SMS_GENERIC_ERROR; + } + + String mimeType = pduDecoder.getValueString(); + long binaryContentType = pduDecoder.getValue32(); + index += pduDecoder.getDecodedDataLength(); + + byte[] header = new byte[headerLength]; + System.arraycopy(pdu, headerStartIndex, header, 0, header.length); + + byte[] intentData; + + if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) { + intentData = pdu; + } else { + int dataIndex = headerStartIndex + headerLength; + intentData = new byte[pdu.length - dataIndex]; + System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length); + } + + /** + * Seek for application ID field in WSP header. + * If application ID is found, WapPushManager substitute the message + * processing. Since WapPushManager is optional module, if WapPushManager + * is not found, legacy message processing will be continued. + */ + if (pduDecoder.seekXWapApplicationId(index, index + headerLength - 1)) { + index = (int) pduDecoder.getValue32(); + pduDecoder.decodeXWapApplicationId(index); + String wapAppId = pduDecoder.getValueString(); + if (wapAppId == null) { + wapAppId = Integer.toString((int) pduDecoder.getValue32()); + } + + String contentType = ((mimeType == null) ? + Long.toString(binaryContentType) : mimeType); + if (Config.DEBUG) Log.v(LOG_TAG, "appid found: " + wapAppId + ":" + contentType); + + try { + boolean processFurther = true; + // IWapPushManager wapPushMan = mWapConn.getWapPushManager(); + if (wapPushMan == null) { + if (Config.DEBUG) Log.w(LOG_TAG, "wap push manager not found!"); + } else { + Intent intent = new Intent(); + intent.putExtra("transactionId", transactionId); + intent.putExtra("pduType", pduType); + intent.putExtra("header", header); + intent.putExtra("data", intentData); + intent.putExtra("contentTypeParameters", + pduDecoder.getContentParameters()); + + int procRet = wapPushMan.processMessage(wapAppId, contentType, intent); + if (Config.DEBUG) Log.v(LOG_TAG, "procRet:" + procRet); + if ((procRet & WapPushManagerParams.MESSAGE_HANDLED) > 0 + && (procRet & WapPushManagerParams.FURTHER_PROCESSING) == 0) { + processFurther = false; + } + } + if (!processFurther) { + return Intents.RESULT_SMS_HANDLED; + } + } catch (RemoteException e) { + if (Config.DEBUG) Log.w(LOG_TAG, "remote func failed..."); + } + } + if (Config.DEBUG) Log.v(LOG_TAG, "fall back to existing handler"); + + return Activity.RESULT_OK; + } + + protected byte[] retrieveWspBody() { + byte[] array = new byte[mWspHeader.length + mMessageBody.length]; + + System.arraycopy(mWspHeader, 0, array, 0, mWspHeader.length); + System.arraycopy(mMessageBody, 0, array, mWspHeader.length, mMessageBody.length); + return array; + } + + protected String getContentTypeName(int ctypeVal) { + int i; + + for (i = 0; i < OMA_CONTENT_TYPE_VALUES.length; i++) { + if (ctypeVal == OMA_CONTENT_TYPE_VALUES[i]) { + return OMA_CONTENT_TYPE_NAMES[i]; + } + } + return null; + } + + protected boolean isContentTypeMapped(int ctypeVal) { + int i; + + for (i = 0; i < OMA_CONTENT_TYPE_VALUES.length; i++) { + if (ctypeVal == OMA_CONTENT_TYPE_VALUES[i]) return true; + } + return false; + } + + /** + * Integration test 1, simple case + */ + public void testIntegration1() { + boolean res; + int originalAppIdValue = mAppIdValue; + int originalContentTypeValue = mContentTypeValue; + String originalAppIdName = mAppIdName; + String originalContentTypeName = mContentTypeName; + String originalClassName = mClassName; + byte[] originalMessageBody = mMessageBody; + Random rd = new Random(); + + mMessageBody = new byte[100 + rd.nextInt(100)]; + rd.nextBytes(mMessageBody); + + byte[] pdu = createPDU(1); + byte[] wappushPdu = retrieveWspBody(); + + + mClassName = "com.android.smspush.unitTests.ReceiverActivity"; + // Phone dummy = new DummyPhone(getContext()); + // Phone gsm = PhoneFactory.getGsmPhone(); + // GSMPhone gsm = new GSMPhone(getContext(), new SimulatedCommands(), null, true); + // WapPushOverSms dispatcher = new WapPushOverSms(dummy, null); + + try { + // set up data + IWapPushManager iwapman = getInterface(); + IDataVerify dataverify = getVerifyInterface(); + + dataverify.resetData(); + + if (isContentTypeMapped(mContentTypeValue)) { + // content type is mapped + mContentTypeName = getContentTypeName(mContentTypeValue); + Log.d(LOG_TAG, "mContentTypeValue mapping " + + mContentTypeName + ":" + mContentTypeValue); + } else { + mContentTypeName = Integer.toString(mContentTypeValue); + } + iwapman.addPackage(Integer.toString(mAppIdValue), + mContentTypeName, mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_ACTIVITY, false, false); + + dispatchWapPdu(wappushPdu, iwapman); + + // clean up data + iwapman.deletePackage(Integer.toString(mAppIdValue), + mContentTypeName, mPackageName, mClassName); + + assertTrue(dataverify.verifyData(mMessageBody)); + } catch (RemoteException e) { + } + + + mClassName = originalClassName; + mAppIdName = originalAppIdName; + mContentTypeName = originalContentTypeName; + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + mMessageBody = originalMessageBody; + } + + /** + * Integration test 2, random mAppIdValue(int), all OMA content type + */ + public void testIntegration2() { + boolean res; + int originalAppIdValue = mAppIdValue; + int originalContentTypeValue = mContentTypeValue; + String originalAppIdName = mAppIdName; + String originalContentTypeName = mContentTypeName; + String originalClassName = mClassName; + byte[] originalMessageBody = mMessageBody; + Random rd = new Random(); + + IWapPushManager iwapman = getInterface(); + IDataVerify dataverify = getVerifyInterface(); + mClassName = "com.android.smspush.unitTests.ReceiverActivity"; + + for (int i = 0; i < OMA_CONTENT_TYPE_NAMES.length; i++) { + mContentTypeName = OMA_CONTENT_TYPE_NAMES[i]; + mAppIdValue = rd.nextInt(0x0FFFFFFF); + + mMessageBody = new byte[100 + rd.nextInt(100)]; + rd.nextBytes(mMessageBody); + + byte[] pdu = createPDU(7); + byte[] wappushPdu = retrieveWspBody(); + + try { + dataverify.resetData(); + // set up data + iwapman.addPackage(Integer.toString(mAppIdValue), + mContentTypeName, mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_ACTIVITY, false, false); + + dispatchWapPdu(wappushPdu, iwapman); + + // clean up data + iwapman.deletePackage(Integer.toString(mAppIdValue), + mContentTypeName, mPackageName, mClassName); + + if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) { + assertTrue(dataverify.verifyData(wappushPdu)); + } else { + assertTrue(dataverify.verifyData(mMessageBody)); + } + } catch (RemoteException e) { + } + } + + + mClassName = originalClassName; + mAppIdName = originalAppIdName; + mContentTypeName = originalContentTypeName; + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + mMessageBody = originalMessageBody; + } + + /** + * Integration test 3, iterate OmaApplication ID, random binary content type + */ + public void testIntegration3() { + boolean res; + int originalAppIdValue = mAppIdValue; + int originalContentTypeValue = mContentTypeValue; + String originalAppIdName = mAppIdName; + String originalContentTypeName = mContentTypeName; + String originalClassName = mClassName; + byte[] originalMessageBody = mMessageBody; + Random rd = new Random(); + + IWapPushManager iwapman = getInterface(); + IDataVerify dataverify = getVerifyInterface(); + mClassName = "com.android.smspush.unitTests.ReceiverService"; + + for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length; i++) { + mAppIdName = OMA_APPLICATION_ID_NAMES[i]; + mContentTypeValue = rd.nextInt(0x0FFF); + + mMessageBody = new byte[100 + rd.nextInt(100)]; + rd.nextBytes(mMessageBody); + + byte[] pdu = createPDU(6); + byte[] wappushPdu = retrieveWspBody(); + + try { + dataverify.resetData(); + // set up data + if (isContentTypeMapped(mContentTypeValue)) { + // content type is mapped to integer value + mContentTypeName = getContentTypeName(mContentTypeValue); + Log.d(LOG_TAG, "mContentTypeValue mapping " + + mContentTypeValue + ":" + mContentTypeName); + } else { + mContentTypeName = Integer.toString(mContentTypeValue); + } + + iwapman.addPackage(mAppIdName, + mContentTypeName, mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_SERVICE, false, false); + + dispatchWapPdu(wappushPdu, iwapman); + + // clean up data + iwapman.deletePackage(mAppIdName, + mContentTypeName, mPackageName, mClassName); + + if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) { + assertTrue(dataverify.verifyData(wappushPdu)); + } else { + assertTrue(dataverify.verifyData(mMessageBody)); + } + } catch (RemoteException e) { + } + } + + mClassName = originalClassName; + mAppIdName = originalAppIdName; + mContentTypeName = originalContentTypeName; + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + mMessageBody = originalMessageBody; + } + + /** + * Integration test 4, iterate OmaApplication ID, Oma content type + */ + public void testIntegration4() { + boolean res; + int originalAppIdValue = mAppIdValue; + int originalContentTypeValue = mContentTypeValue; + String originalAppIdName = mAppIdName; + String originalContentTypeName = mContentTypeName; + String originalClassName = mClassName; + byte[] originalMessageBody = mMessageBody; + Random rd = new Random(); + + IWapPushManager iwapman = getInterface(); + IDataVerify dataverify = getVerifyInterface(); + mClassName = "com.android.smspush.unitTests.ReceiverService"; + + for (int i = 0; i < OMA_APPLICATION_ID_NAMES.length + + OMA_CONTENT_TYPE_NAMES.length; i++) { + mAppIdName = OMA_APPLICATION_ID_NAMES[rd.nextInt(OMA_APPLICATION_ID_NAMES.length)]; + int contIndex = rd.nextInt(OMA_CONTENT_TYPE_NAMES.length); + mContentTypeName = OMA_CONTENT_TYPE_NAMES[contIndex]; + + mMessageBody = new byte[100 + rd.nextInt(100)]; + rd.nextBytes(mMessageBody); + + byte[] pdu = createPDU(8); + byte[] wappushPdu = retrieveWspBody(); + + try { + dataverify.resetData(); + // set up data + iwapman.addPackage(mAppIdName, + mContentTypeName, mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_SERVICE, false, false); + + dispatchWapPdu(wappushPdu, iwapman); + + // clean up data + iwapman.deletePackage(mAppIdName, + mContentTypeName, mPackageName, mClassName); + + if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) { + assertTrue(dataverify.verifyData(wappushPdu)); + } else { + assertTrue(dataverify.verifyData(mMessageBody)); + } + } catch (RemoteException e) { + } + } + + mClassName = originalClassName; + mAppIdName = originalAppIdName; + mContentTypeName = originalContentTypeName; + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + mMessageBody = originalMessageBody; + } + + /** + * Integration test 5, iterate binary OmaApplication ID, Oma binary content type + */ + public void testIntegration5() { + boolean res; + int originalAppIdValue = mAppIdValue; + int originalContentTypeValue = mContentTypeValue; + String originalAppIdName = mAppIdName; + String originalContentTypeName = mContentTypeName; + String originalClassName = mClassName; + byte[] originalMessageBody = mMessageBody; + Random rd = new Random(); + + IWapPushManager iwapman = getInterface(); + IDataVerify dataverify = getVerifyInterface(); + mClassName = "com.android.smspush.unitTests.ReceiverService"; + + for (int i = 0; i < OMA_APPLICATION_ID_VALUES.length + + OMA_CONTENT_TYPE_VALUES.length; i++) { + mAppIdValue = OMA_APPLICATION_ID_VALUES[rd.nextInt( + OMA_APPLICATION_ID_VALUES.length)]; + mContentTypeValue = + OMA_CONTENT_TYPE_VALUES[rd.nextInt(OMA_CONTENT_TYPE_VALUES.length)]; + + mMessageBody = new byte[100 + rd.nextInt(100)]; + rd.nextBytes(mMessageBody); + + byte[] pdu = createPDU(3); + byte[] wappushPdu = retrieveWspBody(); + + try { + dataverify.resetData(); + // set up data + if (isContentTypeMapped(mContentTypeValue)) { + // content type is mapped to integer value + mContentTypeName = getContentTypeName(mContentTypeValue); + Log.d(LOG_TAG, "mContentTypeValue mapping " + + mContentTypeValue + ":" + mContentTypeName); + } else { + mContentTypeName = Integer.toString(mContentTypeValue); + } + + iwapman.addPackage(Integer.toString(mAppIdValue), + mContentTypeName, mPackageName, mClassName, + WapPushManagerParams.APP_TYPE_SERVICE, false, false); + + dispatchWapPdu(wappushPdu, iwapman); + + // clean up data + iwapman.deletePackage(Integer.toString(mAppIdValue), + mContentTypeName, mPackageName, mClassName); + + if (mContentTypeName.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) { + assertTrue(dataverify.verifyData(wappushPdu)); + } else { + assertTrue(dataverify.verifyData(mMessageBody)); + } + } catch (RemoteException e) { + } + } + + mClassName = originalClassName; + mAppIdName = originalAppIdName; + mContentTypeName = originalContentTypeName; + mAppIdValue = originalAppIdValue; + mContentTypeValue = originalContentTypeValue; + mMessageBody = originalMessageBody; + } + +} diff --git a/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 @@ public class LockPatternKeyguardView extends KeyguardViewBase { */ 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 class LockPatternKeyguardView extends KeyguardViewBase { public void recreateMe(Configuration config) { mConfiguration = config; - recreateScreens(); + removeCallbacks(mRecreateRunnable); + post(mRecreateRunnable); } public void takeEmergencyCallAction() { @@ -463,6 +470,12 @@ public class LockPatternKeyguardView extends KeyguardViewBase { } @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 @@ void AudioMixer::process__genericResampling(state_t* state) { 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 @@ void AudioMixer::process__genericResampling(state_t* state) } 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 @@ class AlarmManagerService extends IAlarmManager.Stub { 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.IConnectivityManager; 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.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.GregorianCalendar; import java.util.List; +import java.net.InetAddress; +import java.net.UnknownHostException; @@ -907,6 +910,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { } /** + * @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 @@ public class ConnectivityService extends IConnectivityManager.Stub { * @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 @@ public class ConnectivityService extends IConnectivityManager.Stub { 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 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub { } } 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 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub { } /** 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 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub { } 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 @@ class MountService extends IMountService.Stub // 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 @@ final class NativeDaemonConnector implements Runnable { 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 @@ public class NotificationManagerService extends INotificationManager.Stub 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 @@ public class NotificationManagerService extends INotificationManager.Stub 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 @@ public class NotificationManagerService extends INotificationManager.Stub // 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 @@ public class ProcessStats { 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 @@ class WallpaperManagerService extends IWallpaperManager.Stub { 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 @@ public class WindowManagerService extends IWindowManager.Stub } catch (InterruptedException e) { } } + return thr.mService; } - - return thr.mService; } static class WMThread extends Thread { @@ -1482,6 +1481,7 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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.BroadcastReceiver; 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public class ActivityStack { // 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 @@ class AppWaitingForDebuggerDialog extends BaseErrorDialog { 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..b19bb5c 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 @@ class FactoryErrorDialog extends BaseErrorDialog { 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 @@ struct BatteryManagerConstants { jint healthDead; jint healthOverVoltage; jint healthUnspecifiedFailure; + jint healthCold; }; static BatteryManagerConstants gConstants; @@ -104,6 +105,7 @@ static jint getBatteryStatus(const char* status) 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 @@ static void setBooleanField(JNIEnv* env, jobject obj, const char* path, jfieldID jboolean value = false; if (readFromFile(path, buf, SIZE) > 0) { - if (buf[0] == '1') { + if (buf[0] != '0') { value = true; } } @@ -390,6 +392,9 @@ int register_android_server_BatteryService(JNIEnv* env) 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 @@ AGpsRilCallbacks sAGpsRilCallbacks = { 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 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, 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_set_position_mode(JNIEnv* e 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 @@ static void android_location_GpsLocationProvider_agps_set_reference_location_cel 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 @@ static void android_location_GpsLocationProvider_agps_set_reference_location_cel 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 @@ static void android_location_GpsLocationProvider_agps_send_ni_message(JNIEnv* en 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 jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject 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_inject_xtra_data(JNIEnv* env, j 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 @@ static void android_location_GpsLocationProvider_agps_data_conn_open(JNIEnv* env 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 jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* e 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 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const 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 class PhoneNumberUtils 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 @@ public class PhoneNumberUtils * 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 @@ public class PhoneNumberUtils } } + int pos = addPlusChar(phoneNumber); + if (pos >= 0 && ret.length() > pos) { + ret.insert(pos, '+'); + } + return ret.toString(); } @@ -304,6 +313,28 @@ public class PhoneNumberUtils } } + /** 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 @@ public class PhoneNumberUtils && text.charAt(2) == '1') { formatType = FORMAT_JAPAN; } else { - return; + formatType = FORMAT_UNKNOWN; } } @@ -1130,6 +1161,9 @@ public class PhoneNumberUtils case FORMAT_JAPAN: formatJapaneseNumber(text); return; + case FORMAT_UNKNOWN: + removeDashes(text); + return; } } @@ -1165,14 +1199,7 @@ public class PhoneNumberUtils 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 @@ public class PhoneNumberUtils 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 @@ public final class SmsManager { } 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 @@ public class AdnRecord implements Parcelable { 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 @@ public final class AdnRecordCache extends Handler implements IccConstants { } 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 @@ public final class AdnRecordCache extends Handler implements IccConstants { 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 @@ public final class AdnRecordCache extends Handler implements IccConstants { 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 @@ public abstract class BaseCommands implements CommandsInterface { 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 @@ public abstract class BaseCommands implements CommandsInterface { 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 @@ public interface CommandsInterface { 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 @@ interface ISms { 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 @@ public abstract class IccCard { 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 @@ public abstract class IccCard { 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 @@ public abstract class IccCard { * @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 @@ public interface IccConstants { 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 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub { 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 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub { 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 @@ public class IccSmsInterfaceManagerProxy extends ISms.Stub { 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 @@ public class IccUtils { 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 class IccUtils { */ 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 @@ public class IccUtils { 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 @@ public interface Phone { 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 abstract class PhoneBase extends Handler implements Phone { } } + 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 @@ public abstract class PhoneBase extends Handler implements Phone { 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 class PhoneProxy extends Handler implements Phone { 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 @@ class RILRequest { this.mNext = sPool; sPool = this; sPoolSize++; + mResult = null; } } } @@ -370,6 +371,11 @@ public final class RIL extends BaseCommands implements CommandsInterface { 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 @@ public final class RIL extends BaseCommands implements CommandsInterface { 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 @@ public final class RIL extends BaseCommands implements CommandsInterface { 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 @@ public final class RIL extends BaseCommands implements CommandsInterface { 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 @@ public final class RIL extends BaseCommands implements CommandsInterface { 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 @@ public final class RIL extends BaseCommands implements CommandsInterface { 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 @@ public final class RIL extends BaseCommands implements CommandsInterface { 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 @@ public final class RIL extends BaseCommands implements CommandsInterface { 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 @@ public final class RIL extends BaseCommands implements CommandsInterface { 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 @@ public final class RIL extends BaseCommands implements CommandsInterface { 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 @@ public final class RIL extends BaseCommands implements CommandsInterface { 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 @@ public abstract class SMSDispatcher extends Handler { /** 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 @@ public abstract class SMSDispatcher extends Handler { obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); } break; + + case EVENT_NEW_BROADCAST_SMS: + handleBroadcastSms((AsyncResult)msg.obj); + break; } } @@ -1004,4 +1011,17 @@ public abstract class SMSDispatcher extends Handler { } } }; + + 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 @@ public class WapPushOverSms { */ 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 class WapPushOverSms { */ 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 @@ public class WapPushOverSms { 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 @@ public class WapPushOverSms { * 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 @@ public class WapPushOverSms { * 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; - 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); + 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); + } - mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH"); - } + /** + * 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()); + } - 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); + String contentType = ((mimeType == null) ? + Long.toString(binaryContentType) : mimeType); + if (Config.DEBUG) Log.v(LOG_TAG, "appid found: " + wapAppId + ":" + contentType); - 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); + try { + boolean processFurther = true; + IWapPushManager wapPushMan = mWapConn.getWapPushManager(); - mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH"); - } + 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()); - 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); + 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(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS); + 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, permission); - 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 class WspTypeDecoder { 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 - - 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_MIME_TYPES = + new HashMap<Integer, String>(); + + 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 @@ public class WspTypeDecoder { * @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 @@ public class WspTypeDecoder { } /** + * 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 @@ public class WspTypeDecoder { * * @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 @@ public class WspTypeDecoder { 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 @@ public class WspTypeDecoder { * * @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 @@ public class WspTypeDecoder { * * @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 @@ public class WspTypeDecoder { * * @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 @@ public class WspTypeDecoder { 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 @@ public class WspTypeDecoder { index++; } - dataLength = index - startIndex + 1; + dataLength = index - startIndex + 1; stringValue = new String(wspData, startIndex, dataLength - 1); return rtrn; @@ -227,7 +388,7 @@ public class WspTypeDecoder { * * @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,29 +403,160 @@ public class WspTypeDecoder { * * @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; - - if (decodeValueLength(startIndex) == false) { - return decodeConstrainedEncoding(startIndex); + contentParameters = new HashMap<String, String>(); + + 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; } - mediaPrefixLength = getDecodedDataLength(); - mediaFieldLength = getValue32(); - if (decodeIntegerValue(startIndex + mediaPrefixLength) == true) { - dataLength += mediaPrefixLength; - stringValue = null; + 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; } - if (decodeExtensionMedia(startIndex + mediaPrefixLength) == true) { - dataLength += mediaPrefixLength; + } + + /** + * 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; } - return false; } /** @@ -274,7 +566,7 @@ public class WspTypeDecoder { * * @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 @@ public class WspTypeDecoder { * * @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 @@ public class WspTypeDecoder { * * @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 @@ public class WspTypeDecoder { } /** + * 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 @@ public class WspTypeDecoder { * * @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 class WspTypeDecoder { 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 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 @@ public interface AppInterface { 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 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 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 class StkCmdMessage implements Parcelable { public TextMessage callMsg; } - StkCmdMessage(CommandParams cmdParams) { + CatCmdMessage(CommandParams cmdParams) { mCmdDet = cmdParams.cmdDet; switch(getCmdType()) { case SET_UP_MENU: @@ -88,7 +88,7 @@ public class StkCmdMessage implements Parcelable { } } - 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 class StkCmdMessage implements Parcelable { } } - 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 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 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 @@ public abstract class StkLog { } 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 @@ public abstract class StkLog { 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 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 @@ public class StkResponseMessage { 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 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.AsyncResult; 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 @@ class RilMessage { * * {@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 @@ public class StkService extends Handler implements AppInterface { 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 @@ public class StkService extends Handler implements AppInterface { 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 @@ public class StkService extends Handler implements AppInterface { 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 @@ public class StkService extends Handler implements AppInterface { * */ 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 @@ public class StkService extends Handler implements AppInterface { * */ 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 @@ public class StkService extends Handler implements AppInterface { } 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 @@ public class StkService extends Handler implements AppInterface { 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 @@ public class StkService extends Handler implements AppInterface { // 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 @@ public class StkService extends Handler implements AppInterface { } /** - * 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; + 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. + 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"); } - 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; - - // 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"); + return sInstance; } - return sInstance; } /** @@ -496,7 +558,7 @@ public class StkService extends Handler implements AppInterface { 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 @@ public class StkService extends Handler implements AppInterface { 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 @@ public class StkService extends Handler implements AppInterface { 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 @@ public class StkService extends Handler implements AppInterface { 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 @@ public class StkService extends Handler implements AppInterface { // 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 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 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 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { 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 @@ class CommandParamsFactory extends Handler { * 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 @@ class CommandParamsFactory extends Handler { * 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 @@ class CommandParamsFactory extends Handler { * @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 @@ class CommandParamsFactory extends Handler { } 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 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 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 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 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 @@ class IconLoader extends Handler { 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 @@ class IconLoader extends Handler { 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 @@ class IconLoader extends Handler { 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 @@ class IconLoader extends Handler { } 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 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 @@ public class ImageDescriptor { 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 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 class Input implements Parcelable { public boolean echo; public boolean yesNo; public boolean helpAvailable; + public Duration duration; Input() { text = ""; @@ -49,6 +50,7 @@ public class Input implements Parcelable { echo = false; yesNo = false; helpAvailable = false; + duration = null; } private Input(Parcel in) { @@ -63,6 +65,7 @@ public class Input implements Parcelable { 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 @@ public class Input implements Parcelable { 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 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 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 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.ArrayList; 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 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 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 @@ abstract class ResponseData { * 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 @@ class GetInkeyInputResponseData extends ResponseData { } // 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 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 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 @@ package com.android.internal.telephony.gsm.stk; * * {@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 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 @@ import android.os.Message; /** * 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 @@ class RilMessageDecoder extends HierarchicalStateMachine { * @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 @@ class RilMessageDecoder extends HierarchicalStateMachine { } 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 @@ class RilMessageDecoder extends HierarchicalStateMachine { 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 @@ class RilMessageDecoder extends HierarchicalStateMachine { 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 @@ class RilMessageDecoder extends HierarchicalStateMachine { 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 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 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 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 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 index b64e777..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 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 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 @@ abstract class ValueParser { */ 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.telephony.SignalStrength; 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 @@ public class CDMAPhone extends PhoneBase { 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 @@ public class CDMAPhone extends PhoneBase { 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 @@ public class CDMAPhone extends PhoneBase { mCM.unregisterForNVReady(this); //EVENT_NV_READY mSST.unregisterForNetworkAttach(this); //EVENT_REGISTERED_TO_NETWORK mCM.unSetOnSuppServiceNotification(this); + removeCallbacks(mExitEcmRunnable); mPendingMmis.clear(); @@ -235,6 +239,7 @@ public class CDMAPhone extends PhoneBase { mRuimSmsInterfaceManager.dispose(); mSubInfo.dispose(); mEriManager.dispose(); + mCcatService.dispose(); } } @@ -250,6 +255,8 @@ public class CDMAPhone extends PhoneBase { 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 @@ final class CdmaSMSDispatcher extends SMSDispatcher { 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 index 2cad6cc..5b6bc1f 100644..100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java @@ -552,32 +552,53 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { } @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 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { 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 @@ public class RuimSmsInterfaceManager extends IccSmsInterfaceManager { 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 index 6f024ed..54cf612 100755..100644 --- 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.SmsMessageBase; 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 @@ public class SmsMessage extends SmsMessageBase { 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 @@ public class SmsMessage extends SmsMessageBase { 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 @@ public class SmsMessage extends SmsMessageBase { 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 @@ public class SmsMessage extends SmsMessageBase { // link the the filled objects to the SMS env.origAddress = addr; + env.origSubaddress = subaddr; msg.originatingAddress = addr; msg.mEnvelope = env; @@ -255,6 +278,7 @@ public class SmsMessage extends SmsMessageBase { 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 @@ public class SmsMessage extends SmsMessageBase { } /** + * 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 @@ public class SmsMessage extends SmsMessageBase { 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 @@ public final class BearerData { 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/cdma/sms/CdmaSmsSubaddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java new file mode 100644 index 0000000..f9cebf5 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java @@ -0,0 +1,27 @@ +/* + * 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. + * 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.cdma.sms; + +public class CdmaSmsSubaddress { + public int type; + + public byte odd; + + 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 @@ public final class SmsEnvelope{ /** * 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.CF_REASON_UNCONDI 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.PhoneProxy; 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 @@ public class GSMPhone extends PhoneBase { GsmSMSDispatcher mSMS; SIMRecords mSIMRecords; SimCard mSimCard; - StkService mStkService; + CatService mStkService; ArrayList <GsmMmiCode> mPendingMMIs = new ArrayList<GsmMmiCode>(); SimPhoneBookInterfaceManager mSimPhoneBookIntManager; SimSmsInterfaceManager mSimSmsIntManager; @@ -150,7 +150,7 @@ public class GSMPhone extends PhoneBase { 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 class GSMPhone extends PhoneBase { } 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 @@ public class GSMPhone extends PhoneBase { // 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 @@ public class GSMPhone extends PhoneBase { 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 @@ public final class GsmMmiCode extends Handler implements MmiCode { //***** 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 @@ public final class GsmMmiCode extends Handler implements MmiCode { } /** - * 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.app.PendingIntent.CanceledException; 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 @@ final class GsmSMSDispatcher extends SMSDispatcher { GsmSMSDispatcher(GSMPhone phone) { super(phone); mGsmPhone = phone; + + ((BaseCommands)mCm).setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null); } /** @@ -392,4 +400,162 @@ final class GsmSMSDispatcher extends SMSDispatcher { 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 @@ final class GsmServiceStateTracker extends ServiceStateTracker { 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 @@ public final class SIMFileHandler extends IccFileHandler implements IccConstants 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 @@ public final class SIMRecords extends IccRecords { * 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 @@ public final class SIMRecords extends IccRecords { 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 @@ public final class SIMRecords extends IccRecords { 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 @@ public final class SIMRecords extends IccRecords { 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 @@ public final class SIMRecords extends IccRecords { ((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 @@ public final class SIMRecords extends IccRecords { 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 @@ public final class SIMRecords extends IccRecords { 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 @@ public final class SIMRecords extends IccRecords { 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 @@ public final class SIMRecords extends IccRecords { 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 com.android.internal.telephony.SmsRawData; 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 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { 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 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { 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 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { 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 android.util.Log; 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 @@ import static android.telephony.SmsMessage.MessageClass; 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 @@ public class SmsMessage extends SmsMessageBase{ 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 @@ public class SmsMessage extends SmsMessageBase{ // 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 @@ public class SmsMessage extends SmsMessageBase{ } 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 @@ public class SmsMessage extends SmsMessageBase{ 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 index 6458fda..ec3d20a 100644..100755 --- a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java +++ b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java @@ -53,6 +53,7 @@ public class UsimPhoneBookManager extends Handler implements IccConstants { 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 @@ public class UsimPhoneBookManager extends Handler implements IccConstants { 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 @@ public class UsimPhoneBookManager extends Handler implements IccConstants { 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 @@ public class AdnRecordTest extends TestCase { 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 @@ public class SMSDispatcherTest extends AndroidTestCase { 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 @@ public class SimUtilsTest extends TestCase { 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 @@ public class IsolatedContext extends ContextWrapper { } @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 @@ public abstract class SingleLaunchActivityTestCase<T extends Activity> 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/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 Binary files differdeleted file mode 100755 index 47c79d1..0000000 --- a/tests/BrowserTestPlugin/res/drawable/browser_test_plugin.png +++ /dev/null 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..a5d4857 --- /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.view.View; 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 @@ public abstract class FileList extends ListActivity 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 @@ package com.android.dumprendertree; import com.android.dumprendertree.forwarder.ForwardService; +import android.os.Environment; import android.util.Log; import java.io.BufferedOutputStream; @@ -32,11 +33,17 @@ import java.util.regex.Pattern; 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 @@ package com.android.dumprendertree; 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 @@ class MyTestRecorder { 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 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh 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.app.Instrumentation; 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 @@ import java.io.PrintStream; 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..9c4b572 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java @@ -18,6 +18,7 @@ package com.android.dumprendertree; import android.content.Intent; import android.os.Bundle; +import android.os.Environment; import android.util.Log; import java.io.BufferedOutputStream; @@ -28,7 +29,8 @@ public class Menu extends FileList { 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 @@ package com.android.dumprendertree; 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 @@ public class ReliabilityTest extends ActivityInstrumentationTestCase2<Reliabilit 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.CompressFormat; 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 @@ public class TestShellActivity extends Activity implements LayoutTestController 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.File; import java.io.FileReader; import java.io.IOException; +import android.os.Environment; import android.util.Log; public class ForwardService { @@ -33,7 +34,8 @@ public class ForwardService { 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.content.Intent; 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 @@ public class TrackerActivity extends ListActivity { } 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 @@ public class NotificationTestList extends TestActivity 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 @@ bool AaptGroupEntry::getMncName(const char* name, 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 @@ public: 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 @@ public: 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 @@ private: 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 @@ void usage(void) " 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 @@ int main(int argc, char* const argv[]) 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 writeLayoutClasses( 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 @@ static status_t writeSymbolClass( 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 @@ static status_t writeSymbolClass( 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 @@ static status_t writeSymbolClass( 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 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, "\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 @@ status_t compileResourceFile(Bundle* bundle, } } } 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 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, // 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 @@ status_t ResourceTable::addBag(const SourcePos& sourcePos, // 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 @@ void printXMLBlock(ResXMLTree* block) 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 @@ public class WifiStateTracker extends NetworkStateTracker { 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 @@ public class WifiStateTracker extends NetworkStateTracker { 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) { |