diff options
218 files changed, 17353 insertions, 1118 deletions
diff --git a/api/current.xml b/api/current.xml index 179c515..f33f205 100644 --- a/api/current.xml +++ b/api/current.xml @@ -22799,6 +22799,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" @@ -57104,6 +57116,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" @@ -59976,6 +60024,1615 @@ </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="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> +</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_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_NOT_SUPPORTED" + type="int" + transient="false" + volatile="false" + value="6" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TYPE_NO_INTERNET_CONNECTION" + type="int" + transient="false" + volatile="false" + value="9" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TYPE_OUT_OF_MEMORY" + type="int" + transient="false" + volatile="false" + value="8" + 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_RIGHTS_NOT_INSTALLED" + type="int" + transient="false" + volatile="false" + value="4" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TYPE_RIGHTS_RENEWAL_NOT_ALLOWED" + type="int" + transient="false" + volatile="false" + value="5" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TYPE_WAIT_FOR_RIGHTS" + type="int" + transient="false" + volatile="false" + value="7" + 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="_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="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="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="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="path" type="java.lang.String"> +</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="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="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="loadPlugIns" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</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="android.drm.DrmInfoStatus" + 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="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="removeRights" + return="void" + 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="saveRights" + return="void" + 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="setOnInfoListener" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="infoListener" type="android.drm.DrmManagerClient.OnInfoListener"> +</parameter> +</method> +<method name="unloadPlugIns" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +</class> +<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" @@ -208292,6 +209949,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..9fe1fb8 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,12 +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) || + (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0)) { + if ((mZip.open(USER_BOOTANIMATION_FILE) != NO_ERROR) || + (mZip.open(SYSTEM_BOOTANIMATION_FILE) != NO_ERROR)) { + mAndroidAnimation = false; } } diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c index 22d5b39..d380a27 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_RADIO, "radio.phone" }, { AID_RADIO, "radio.sms" }, { AID_RADIO, "radio.phonesubinfo" }, diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java index 2714de5..61a8fc3 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 c01516f..539d50a 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; @@ -85,11 +84,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; @@ -263,18 +260,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) { @@ -439,7 +436,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( @@ -450,7 +447,7 @@ class ContextImpl extends Context { return mFilesDir; } } - + @Override public File getExternalFilesDir(String type) { synchronized (mSync) { @@ -482,7 +479,7 @@ class ContextImpl extends Context { return dir; } } - + @Override public File getCacheDir() { synchronized (mSync) { @@ -502,7 +499,7 @@ class ContextImpl extends Context { } return mCacheDir; } - + @Override public File getExternalCacheDir() { synchronized (mSync) { @@ -524,7 +521,7 @@ class ContextImpl extends Context { return mExternalCacheDir; } } - + @Override public File getFileStreamPath(String name) { return makeFilename(getFilesDir(), name); @@ -565,7 +562,7 @@ class ContextImpl extends Context { return (list != null) ? list : EMPTY_FILE_LIST; } - + private File getDatabasesDir() { synchronized (mSync) { if (mDatabasesDir == null) { @@ -577,7 +574,7 @@ class ContextImpl extends Context { return mDatabasesDir; } } - + @Override public Drawable getWallpaper() { return getWallpaperManager().getDrawable(); @@ -645,7 +642,7 @@ class ContextImpl extends Context { } catch (RemoteException e) { } } - + @Override public void sendBroadcast(Intent intent) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); @@ -1558,15 +1555,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; } @@ -1644,7 +1641,7 @@ class ContextImpl extends Context { public boolean releaseProvider(IContentProvider provider) { return mMainThread.releaseProvider(provider); } - + private final ActivityThread mMainThread; } @@ -1677,7 +1674,7 @@ class ContextImpl extends Context { throw new RuntimeException("Package manager has died", e); } } - + @Override public String[] canonicalToCurrentPackageNames(String[] names) { try { @@ -1686,7 +1683,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 @@ -1708,8 +1705,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; } @@ -1875,7 +1873,7 @@ class ContextImpl extends Context { throw new RuntimeException("Package manager has died", e); } } - + @Override public boolean hasSystemFeature(String name) { try { @@ -1884,7 +1882,7 @@ class ContextImpl extends Context { throw new RuntimeException("Package manager has died", e); } } - + @Override public int checkPermission(String permName, String pkgName) { try { @@ -1956,9 +1954,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); @@ -2362,7 +2360,7 @@ class ContextImpl extends Context { } } } - + private static final class ResourceName { final String packageName; final int iconId; @@ -2534,7 +2532,7 @@ class ContextImpl extends Context { } } @Override - public void clearApplicationUserData(String packageName, + public void clearApplicationUserData(String packageName, IPackageDataObserver observer) { try { mPM.clearApplicationUserData(packageName, observer); @@ -2543,7 +2541,7 @@ class ContextImpl extends Context { } } @Override - public void deleteApplicationCacheFiles(String packageName, + public void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer) { try { mPM.deleteApplicationCacheFiles(packageName, observer); @@ -2568,9 +2566,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); @@ -2615,7 +2613,7 @@ class ContextImpl extends Context { // Should never happen! } } - + @Override public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { @@ -2634,7 +2632,7 @@ class ContextImpl extends Context { // Should never happen! } } - + @Override public int getPreferredActivities(List<IntentFilter> outFilters, List<ComponentName> outActivities, String packageName) { @@ -2645,7 +2643,7 @@ class ContextImpl extends Context { } return 0; } - + @Override public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) { @@ -2675,7 +2673,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/SearchDialog.java b/core/java/android/app/SearchDialog.java index cd22fa1..1f115e8 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -1133,7 +1133,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 4a91a8c..53d6a37 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -45,7 +45,7 @@ import android.util.Log; * This BluetoothHeadset object is not immediately bound to the * BluetoothHeadset service. Use the ServiceListener interface to obtain a * notification when it is bound, this is especially important if you wish to - * immediately call methods on BluetootHeadset after construction. + * immediately call methods on BluetoothHeadset after construction. * * Android only supports one connected Bluetooth Headset at a time. * @@ -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 */ @@ -108,7 +145,7 @@ public final class BluetoothHeadset { public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1; - /** Connection canceled before completetion. */ + /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; /** Values for {@link #EXTRA_DISCONNECT_INITIATOR} */ diff --git a/core/java/android/bluetooth/HeadsetBase.java b/core/java/android/bluetooth/HeadsetBase.java index e2935c9..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 exisiting 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; @@ -128,7 +129,7 @@ public final class HeadsetBase { (System.currentTimeMillis() - timestamp) + " ms"); if (result.getResultCode() == AtCommandResult.ERROR) { - Log.i(TAG, "Error pocessing <" + input + ">"); + Log.i(TAG, "Error processing <" + input + ">"); } sendURC(result.toString()); @@ -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/res/Resources.java b/core/java/android/content/res/Resources.java index 9bb3b75..a6513aa 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,41 @@ 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()) + + ")"); + } + } + } + } + cache.clear(); + } + /** * Update the system resources configuration if they have previously * been initialized. @@ -1661,13 +1677,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 +1747,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 +1770,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 +1784,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/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/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/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 39681912..1cfe419 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 @@ -328,8 +331,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 eb7117b..004cdb7 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; @@ -489,17 +490,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 7803bf2..eef2f7d 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 e4f3d5c..5d4b099 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..22c30a5 100644 --- a/core/java/android/net/Proxy.java +++ b/core/java/android/net/Proxy.java @@ -16,12 +16,18 @@ 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.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; + import junit.framework.Assert; /** @@ -120,4 +126,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) { + if (host.equalsIgnoreCase("localhost")) { + return true; + } + if (InetAddress.getByName(host).isLoopbackAddress()) { + return true; + } + } + } catch (UnknownHostException uex) { + // Ignore (INetworkSystem.ipStringToByteArray) + } 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/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/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/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/provider/Settings.java b/core/java/android/provider/Settings.java index 1dd2420..27df082 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3620,7 +3620,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) { @@ -3678,7 +3678,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); @@ -3730,7 +3730,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 bf9e854..fa5cd8b 100644 --- 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 @@ -1526,7 +1536,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 e05fe7b..611a803 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) { 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..a95dad7 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> @@ -1595,40 +1613,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/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/View.java b/core/java/android/view/View.java index b794a6a..3be18ab 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/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 24b1158..b7c902b 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -4113,6 +4113,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(); 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/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/TextView.java b/core/java/android/widget/TextView.java index 6278192..b0ab72c 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4579,6 +4579,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; @@ -4594,10 +4597,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) { @@ -4661,6 +4662,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; } } @@ -6258,8 +6263,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 107b145..4a0617c 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -435,6 +435,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/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java index d1aff2a..e07c54f 100644 --- a/core/java/com/android/internal/app/ShutdownThread.java +++ b/core/java/com/android/internal/app/ShutdownThread.java @@ -134,7 +134,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; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index b033fad..66149ac 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -971,7 +971,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 { @@ -1018,9 +1018,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 21f0c93..8edfe52 100644 --- a/core/java/com/google/android/mms/pdu/PduParser.java +++ b/core/java/com/google/android/mms/pdu/PduParser.java @@ -163,6 +163,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/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index feb0dad..6575b9a 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); @@ -87,13 +86,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; } @@ -107,16 +116,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; @@ -222,9 +221,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/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/values/config.xml b/core/res/res/values/config.xml index d120dd0..98bd271 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -254,6 +254,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> 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/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/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 651edbc..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> @@ -56,7 +56,7 @@ page.title=Developer Forums As you write your post, please do the following: <ol> <li><strong>Read -the <a href="http://source.android.com/discuss/android-discussion-groups-charter">mailing list charter</a></strong> that covers the community guidelines. +the <a href="http://source.android.com/community/groups-charter.html">mailing list charter</a></strong> that covers the community guidelines. </li> <li><strong>Select the most appropriate mailing list for your question</strong>. There are several different lists for developers, described below.</li> diff --git a/drm/common/Android.mk b/drm/common/Android.mk new file mode 100644 index 0000000..808b2c2 --- /dev/null +++ b/drm/common/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:= \ + DrmConstraints.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..11ce410 --- /dev/null +++ b/drm/common/DrmConstraints.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/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) { + LOGV("DrmConstraints::KeyIterator::KeyIterator"); +} + +DrmConstraints::KeyIterator& DrmConstraints::KeyIterator::operator=( + const DrmConstraints::KeyIterator& keyIterator) { + LOGV("DrmConstraints::KeyIterator::operator="); + 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) { + LOGV("DrmConstraints::Iterator::Iterator"); +} + +DrmConstraints::Iterator& DrmConstraints::Iterator::operator=( + const DrmConstraints::Iterator& iterator) { + LOGV("DrmConstraints::Iterator::operator="); + 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/drm/common/DrmConvertedStatus.cpp b/drm/common/DrmConvertedStatus.cpp new file mode 100644 index 0000000..5d035f5 --- /dev/null +++ b/drm/common/DrmConvertedStatus.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/DrmConvertedStatus.h> + +using namespace android; + +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..70398e8 --- /dev/null +++ b/drm/common/DrmEngineBase.cpp @@ -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. + */ + +#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); +} + +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); +} + +void 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); +} + +void DrmEngineBase::consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) { + onConsumeRights(uniqueId, decryptHandle, action, reserve); +} + +void DrmEngineBase::setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) { + onSetPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position); +} + +bool DrmEngineBase::validateAction( + int uniqueId, const String8& path, + int action, const ActionDescription& description) { + return onValidateAction(uniqueId, path, action, description); +} + +void DrmEngineBase::removeRights(int uniqueId, const String8& path) { + onRemoveRights(uniqueId, path); +} + +void DrmEngineBase::removeAllRights(int uniqueId) { + onRemoveAllRights(uniqueId); +} + +void DrmEngineBase::openConvertSession(int uniqueId, int convertId) { + 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); +} + +void DrmEngineBase::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) { + onCloseDecryptSession(uniqueId, decryptHandle); +} + +void DrmEngineBase::initializeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) { + onInitializeDecryptUnit(uniqueId, decryptHandle, decryptUnitId, headerInfo); +} + +status_t DrmEngineBase::decrypt( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer) { + return onDecrypt(uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer); +} + +void DrmEngineBase::finalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) { + 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..eb58129 --- /dev/null +++ b/drm/common/DrmInfoEvent.cpp @@ -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. + */ + +#define LOG_NDEBUG 0 +#define LOG_TAG "DrmInfoEvent" +#include "utils/Log.h" + +#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..f3a1516 --- /dev/null +++ b/drm/common/DrmInfoStatus.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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, const DrmBuffer* _drmBuffer, const String8& _mimeType) : + statusCode(_statusCode), + drmBuffer(_drmBuffer), + mimeType(_mimeType) { + +} + diff --git a/drm/common/DrmRights.cpp b/drm/common/DrmRights.cpp new file mode 100644 index 0000000..dc1e6c5 --- /dev/null +++ b/drm/common/DrmRights.cpp @@ -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. + */ + +#include <drm/DrmRights.h> + +using namespace android; + +DrmRights::DrmRights(const String8& rightsFilePath, const String8& mimeType, + const String8& accountId, const String8& subscriptionId) { + /** + * TODO Read DrmRights from rights file + */ +} + +DrmRights::DrmRights(const DrmBuffer& rightsData, const String8& mimeType, + const String8& accountId, const String8& subscriptionId) : + mData(rightsData), + mMimeType(mimeType), + mAccountId(accountId), + mSubscriptionId(subscriptionId) { +} + +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..35e83fc --- /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 (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 (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..7ce45e7 --- /dev/null +++ b/drm/common/IDrmIOService.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "IDrmIOService" +#include <utils/Log.h> + +#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..4fc828a --- /dev/null +++ b/drm/common/IDrmManagerService.cpp @@ -0,0 +1,1358 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/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; + +status_t BpDrmManagerService::loadPlugIns(int uniqueId) { + LOGV("load plugins"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + remote()->transact(LOAD_PLUGINS, data, &reply); + return reply.readInt32(); +} + +status_t BpDrmManagerService::loadPlugIns(int uniqueId, const String8& plugInDirPath) { + LOGV("load plugins from path"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + data.writeString8(plugInDirPath); + + remote()->transact(LOAD_PLUGINS_FROM_PATH, data, &reply); + return reply.readInt32(); +} + +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::unloadPlugIns(int uniqueId) { + LOGV("unload plugins"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + remote()->transact(UNLOAD_PLUGINS, 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; +} + +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 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, 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; +} + +void 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); +} + +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(); +} + +void 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); +} + +void 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); +} + +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()); +} + +void 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); +} + +void BpDrmManagerService::removeAllRights(int uniqueId) { + LOGV("removeAllRights"); + Parcel data, reply; + + data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor()); + data.writeInt32(uniqueId); + + remote()->transact(REMOVE_ALL_RIGHTS, data, &reply); +} + +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; + + const String16 interfaceDescriptor = IDrmManagerService::getInterfaceDescriptor(); + LOGV("BpDrmManagerService::openDecryptSession: InterfaceDescriptor name is %s", + interfaceDescriptor.string()); + data.writeInterfaceToken(interfaceDescriptor); + data.writeInt32(uniqueId); + data.writeFileDescriptor(fd); + data.writeInt32(offset); + data.writeInt32(length); + + LOGV("try to invoke remote onTransact() with code OPEN_DECRYPT_SESSION"); + 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; +} + +void 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; +} + +void 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); +} + +status_t BpDrmManagerService::decrypt( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer) { + 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); + + 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; +} + +void 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); +} + +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 LOAD_PLUGINS: + { + LOGV("BnDrmManagerService::onTransact :LOAD_PLUGINS"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + status_t status = loadPlugIns(data.readInt32()); + + reply->writeInt32(status); + return DRM_NO_ERROR; + + } + + case LOAD_PLUGINS_FROM_PATH: + { + LOGV("BnDrmManagerService::onTransact :LOAD_PLUGINS_FROM_PATH"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + status_t status = loadPlugIns(data.readInt32(), data.readString8()); + + reply->writeInt32(status); + 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 UNLOAD_PLUGINS: + { + LOGV("BnDrmManagerService::onTransact :UNLOAD_PLUGINS"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + status_t status = unloadPlugIns(data.readInt32()); + + 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 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->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)); + + saveRights(uniqueId, drmRights, + ((rightsPath == String8("NULL")) ? String8("") : rightsPath), + ((contentPath == String8("NULL")) ? String8("") : contentPath)); + + 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; + } + + consumeRights(uniqueId, &handle, data.readInt32(), static_cast<bool>(data.readInt32())); + + 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; + } + + setPlaybackStatus(uniqueId, &handle, data.readInt32(), data.readInt32()); + + 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); + + removeRights(data.readInt32(), data.readString8()); + + return DRM_NO_ERROR; + } + + case REMOVE_ALL_RIGHTS: + { + LOGV("BnDrmManagerService::onTransact :REMOVE_ALL_RIGHTS"); + CHECK_INTERFACE(IDrmManagerService, data, reply); + + removeAllRights(data.readInt32()); + + 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 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; + } + + closeDecryptSession(uniqueId, handle); + 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); + + initializeDecryptUnit(uniqueId, &handle, decryptUnitId, headerInfo); + + 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); + + const status_t status = decrypt(uniqueId, &handle, decryptUnitId, encBuffer, &decBuffer); + + 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; + 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; + } + + finalizeDecryptUnit(uniqueId, &handle, data.readInt32()); + + 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..0a69115 --- /dev/null +++ b/drm/common/IDrmServiceListener.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "IDrmServiceListener" +#include <utils/Log.h> + +#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..4319c1c --- /dev/null +++ b/drm/common/ReadWriteUtils.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <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/FileMap.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) { + FileMap* fileMap = new FileMap(); + if (fileMap->create(filePath.string(), fd, 0, sb.st_size, true)) { + char* addr = (char*)fileMap->getDataPtr(); + string.append(addr, sb.st_size); + fileMap->release(); + } + } + fclose(file); + } + return string; +} + +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)) { + FileMap* fileMap = NULL; + fileMap = new FileMap(); + if (fileMap->create(filePath.string(), fd, 0, size, false)) { + char* addr = (char*)fileMap->getDataPtr(); + memcpy(addr, data.string(), size); + fileMap->release(); + } + } + 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 offset = lseek(fd, 0, SEEK_END); + if (FAILURE != offset) { + int newEntrySize = data.size(); + int fileSize = offset + newEntrySize; + + if (FAILURE != ftruncate(fd, fileSize)) { + FileMap* fileMap = NULL; + fileMap = new FileMap(); + if (fileMap->create(filePath.string(), fd, offset, fileSize, false)) { + char* addr = (char*)fileMap->getDataPtr(); + memcpy(addr, data.string(), data.size()); + fileMap->release(); + } + } + } + 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..67cfd39 --- /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..44886f9 --- /dev/null +++ b/drm/drmserver/DrmManager.cpp @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/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; + +const String8 DrmManager::EMPTY_STRING(""); + +DrmManager::DrmManager() : + mDecryptSessionId(0), + mConvertId(0) { + +} + +DrmManager::~DrmManager() { + +} + +status_t DrmManager::loadPlugIns(int uniqueId) { + String8 pluginDirPath("/system/lib/drm/plugins/native"); + return loadPlugIns(uniqueId, pluginDirPath); +} + +status_t DrmManager::loadPlugIns(int uniqueId, const String8& plugInDirPath) { + if (mSupportInfoToPlugInIdMap.isEmpty()) { + mPlugInManager.loadPlugIns(plugInDirPath); + + initializePlugIns(uniqueId); + + populate(uniqueId); + } else { + initializePlugIns(uniqueId); + } + + return DRM_NO_ERROR; +} + +status_t DrmManager::setDrmServiceListener( + int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) { + Mutex::Autolock _l(mLock); + mServiceListeners.add(uniqueId, drmServiceListener); + return DRM_NO_ERROR; +} + +status_t DrmManager::unloadPlugIns(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); + } + + mConvertSessionMap.clear(); + mDecryptSessionMap.clear(); + mSupportInfoToPlugInIdMap.clear(); + mPlugInManager.unloadPlugIns(); + return DRM_NO_ERROR; +} + +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; +} + +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(uniqueId); + 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 (NULL != path) { + 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; +} + +void DrmManager::saveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath) { + const String8 plugInId = getSupportedPlugInId(drmRights.getMimeType()); + if (EMPTY_STRING != plugInId) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + rDrmEngine.saveRights(uniqueId, drmRights, rightsPath, contentPath); + } +} + +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; +} + +void DrmManager::consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) { + if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId); + drmEngine->consumeRights(uniqueId, decryptHandle, action, reserve); + } +} + +void DrmManager::setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) { + + if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId); + drmEngine->setPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position); + } +} + +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; +} + +void DrmManager::removeRights(int uniqueId, const String8& path) { + const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path); + if (EMPTY_STRING != plugInId) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId); + rDrmEngine.removeRights(uniqueId, path); + } +} + +void DrmManager::removeAllRights(int uniqueId) { + Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList(); + + for (unsigned int index = 0; index < plugInIdList.size(); index++) { + IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index)); + rDrmEngine.removeAllRights(uniqueId); + } +} + +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); + + Mutex::Autolock _l(mConvertLock); + ++mConvertId; + convertId = mConvertId; + mConvertSessionMap.add(mConvertId, &rDrmEngine); + + rDrmEngine.openConvertSession(uniqueId, mConvertId); + } + 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(uniqueId); + 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) { + LOGV("Entering DrmManager::openDecryptSession"); + status_t result = DRM_ERROR_CANNOT_HANDLE; + Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList(); + + DecryptHandle* handle = new DecryptHandle(); + if (NULL != handle) { + Mutex::Autolock _l(mDecryptLock); + 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); + + LOGV("plug-in %s return value = %d", plugInId.string(), result); + + if (DRM_NO_ERROR == result) { + ++mDecryptSessionId; + mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine); + LOGV("plug-in %s is selected", plugInId.string()); + break; + } + } + } + + if (DRM_ERROR_CANNOT_HANDLE == result) { + delete handle; handle = NULL; + LOGE("DrmManager::openDecryptSession: no capable plug-in found"); + } + + return handle; +} + +void DrmManager::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) { + if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId); + drmEngine->closeDecryptSession(uniqueId, decryptHandle); + + mDecryptSessionMap.removeItem(decryptHandle->decryptId); + } +} + +void DrmManager::initializeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) { + if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId); + drmEngine->initializeDecryptUnit(uniqueId, decryptHandle, decryptUnitId, headerInfo); + } +} + +status_t DrmManager::decrypt(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer) { + status_t status = DRM_ERROR_UNKNOWN; + if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId); + status = drmEngine->decrypt(uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer); + } + return status; +} + +void DrmManager::finalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) { + if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) { + IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId); + drmEngine->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId); + } +} + +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; +} + +void DrmManager::initializePlugIns(int uniqueId) { + 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::populate(int uniqueId) { + Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList(); + + for (unsigned int i = 0; i < plugInPathList.size(); ++i) { + String8 plugInPath = plugInPathList[i]; + DrmSupportInfo* info = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(uniqueId); + if (NULL != info) { + mSupportInfoToPlugInIdMap.add(*info, plugInPath); + } + } +} + +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..9d000e9 --- /dev/null +++ b/drm/drmserver/DrmManagerService.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <errno.h> +#include <utils/threads.h> +#include <binder/IServiceManager.h> +#include <sys/stat.h> +#include "DrmManagerService.h" +#include "DrmManager.h" + +using namespace android; + +#define SUCCESS 0 +#define DRM_DIRECTORY_PERMISSION 0700 + +void DrmManagerService::instantiate() { + LOGV("instantiate"); + + int res = mkdir("/data/drm/plugins", DRM_DIRECTORY_PERMISSION);
+ if (SUCCESS == res || EEXIST == errno) {
+ res = mkdir("/data/drm/plugins/native", DRM_DIRECTORY_PERMISSION);
+ if (SUCCESS == res || EEXIST == errno) {
+ res = mkdir("/data/drm/plugins/native/databases", DRM_DIRECTORY_PERMISSION);
+ if (SUCCESS == res || EEXIST == errno) {
+ defaultServiceManager()
+ ->addService(String16("drm.drmManager"), new DrmManagerService());
+ }
+ }
+ } +} + +DrmManagerService::DrmManagerService() { + LOGV("created"); + mDrmManager = NULL; + mDrmManager = new DrmManager(); +} + +DrmManagerService::~DrmManagerService() { + LOGV("Destroyed"); + delete mDrmManager; mDrmManager = NULL; +} + +status_t DrmManagerService::loadPlugIns(int uniqueId) { + LOGV("Entering load plugins"); + return mDrmManager->loadPlugIns(uniqueId); +} + +status_t DrmManagerService::loadPlugIns(int uniqueId, const String8& plugInDirPath) { + LOGV("Entering load plugins from path"); + return mDrmManager->loadPlugIns(uniqueId, plugInDirPath); +} + +status_t DrmManagerService::setDrmServiceListener( + int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) { + LOGV("Entering setDrmServiceListener"); + mDrmManager->setDrmServiceListener(uniqueId, drmServiceListener); + return DRM_NO_ERROR; +} + +status_t DrmManagerService::unloadPlugIns(int uniqueId) { + LOGV("Entering unload plugins"); + return mDrmManager->unloadPlugIns(uniqueId); +} + +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); +} + +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); +} + +void 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); +} + +void DrmManagerService::consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) { + LOGV("Entering consumeRights"); + mDrmManager->consumeRights(uniqueId, decryptHandle, action, reserve); +} + +void DrmManagerService::setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) { + LOGV("Entering setPlaybackStatus"); + 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); +} + +void DrmManagerService::removeRights(int uniqueId, const String8& path) { + LOGV("Entering removeRights"); + mDrmManager->removeRights(uniqueId, path); +} + +void DrmManagerService::removeAllRights(int uniqueId) { + LOGV("Entering removeAllRights"); + 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"); + return mDrmManager->openDecryptSession(uniqueId, fd, offset, length); +} + +void DrmManagerService::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) { + LOGV("Entering closeDecryptSession"); + mDrmManager->closeDecryptSession(uniqueId, decryptHandle); +} + +void DrmManagerService::initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo) { + LOGV("Entering initializeDecryptUnit"); + mDrmManager->initializeDecryptUnit(uniqueId,decryptHandle, decryptUnitId, headerInfo); +} + +status_t DrmManagerService::decrypt( + int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer) { + LOGV("Entering decrypt"); + return mDrmManager->decrypt(uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer); +} + +void DrmManagerService::finalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) { + LOGV("Entering finalizeDecryptUnit"); + 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..367c9bd --- /dev/null +++ b/drm/drmserver/StringTokenizer.cpp @@ -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. + */ + +#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) { + LOGV("StringTokenizer::Iterator::Iterator"); +} + +StringTokenizer::Iterator& StringTokenizer::Iterator::operator=( + const StringTokenizer::Iterator& iterator) { + LOGV("StringTokenizer::Iterator::operator="); + 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/DrmEvent.java b/drm/java/android/drm/DrmEvent.java new file mode 100644 index 0000000..44c4b43 --- /dev/null +++ b/drm/java/android/drm/DrmEvent.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + 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..be1b009 --- /dev/null +++ b/drm/java/android/drm/DrmInfoEvent.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 = 0x0000001; + /** + * TYPE_REMOVE_RIGHTS, when the rights needs to be removed completely. + */ + public static final int TYPE_REMOVE_RIGHTS = 0x0000002; + /** + * TYPE_RIGHTS_INSTALLED, when the rights are downloaded and installed ok. + */ + public static final int TYPE_RIGHTS_INSTALLED = 0x0000003; + /** + * TYPE_RIGHTS_NOT_INSTALLED, when something went wrong installing the rights. + */ + public static final int TYPE_RIGHTS_NOT_INSTALLED = 0x0000004; + /** + * TYPE_RIGHTS_RENEWAL_NOT_ALLOWED, when the server rejects renewal of rights. + */ + public static final int TYPE_RIGHTS_RENEWAL_NOT_ALLOWED = 0x0000005; + /** + * TYPE_NOT_SUPPORTED, when answer from server can not be handled by the native agent. + */ + public static final int TYPE_NOT_SUPPORTED = 0x0000006; + /** + * 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 = 0x0000007; + /** + * 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 = 0x0000008; + /** + * 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 = 0x0000009; + + /** + * 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..7e9ca3e --- /dev/null +++ b/drm/java/android/drm/DrmInfoStatus.java @@ -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. + */ + +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 String mimeType; + public final ProcessedData data; + + /** + * constructor to create DrmInfoStatus object with given parameters + * + * @param _statusCode Status of the communication + * @param _data The processed data + * @param _mimeType MIME type + */ + public DrmInfoStatus(int _statusCode, ProcessedData _data, String _mimeType) { + statusCode = _statusCode; + 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..7ec70da --- /dev/null +++ b/drm/java/android/drm/DrmManagerClient.java @@ -0,0 +1,487 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.ContentValues; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +/** + * Interface of DRM Framework. + * Java application will instantiate this class + * to access DRM agent through DRM Framework. + * + */ +public class DrmManagerClient { + private static final String TAG = "DrmManager"; + + 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); + } + + private static final int STATE_UNINITIALIZED = 0x00000000; + private static final int STATE_INITIALIZED = 0x00000001; + + private int mUniqueId; + private int mNativeContext; + private EventHandler mEventHandler; + private OnInfoListener mOnInfoListener; + private int mCurrentState = STATE_UNINITIALIZED; + + /** + * {@hide} + */ + public static void notify(Object thisReference, int uniqueId, int infoType, String message) { + DrmManagerClient instance = (DrmManagerClient)((WeakReference)thisReference).get(); + + if (null != instance && null != instance.mEventHandler) { + Message m = instance.mEventHandler.obtainMessage( + EventHandler.INFO_EVENT_TYPE, uniqueId, infoType, message); + instance.mEventHandler.sendMessage(m); + } + } + + private class EventHandler extends Handler { + public static final int INFO_EVENT_TYPE = 1; + + public EventHandler(Looper looper) { + super(looper); + } + + public void handleMessage(Message msg) { + + switch (msg.what) { + case EventHandler.INFO_EVENT_TYPE: + int uniqueId = msg.arg1; + int infoType = msg.arg2; + String message = msg.obj.toString(); + + if (infoType == DrmInfoEvent.TYPE_REMOVE_RIGHTS) { + try { + DrmUtils.removeFile(message); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (null != mOnInfoListener) { + DrmInfoEvent event = new DrmInfoEvent(uniqueId, infoType, message); + mOnInfoListener.onInfo(DrmManagerClient.this, event); + } + return; + default: + Log.e(TAG, "Unknown message type " + msg.what); + return; + } + } + } + + /** + * To instantiate DrmManagerClient + * + * @param context context of the caller + */ + public DrmManagerClient(Context context) { + Looper looper; + + if (null != (looper = Looper.myLooper())) { + mEventHandler = new EventHandler(looper); + } else if (null != (looper = Looper.getMainLooper())) { + mEventHandler = new EventHandler(looper); + } else { + mEventHandler = null; + } + + // save the unique id + mUniqueId = hashCode(); + } + + /** + * Register a callback to be invoked when the caller required to receive + * necessary information + * + * @param infoListener + */ + public void setOnInfoListener(OnInfoListener infoListener) { + synchronized(this) { + if (null != infoListener) { + mOnInfoListener = infoListener; + } + } + } + + /** + * Initializes DrmFramework, which loads all available plug-ins + * in the default plug-in directory path + * + */ + public void loadPlugIns() { + if (getState() == STATE_UNINITIALIZED) { + _loadPlugIns(mUniqueId, new WeakReference<DrmManagerClient>(this)); + + mCurrentState = STATE_INITIALIZED; + } + } + + /** + * Finalize DrmFramework, which release resources associated with each plug-in + * and unload all plug-ins. + */ + public void unloadPlugIns() { + if (getState() == STATE_INITIALIZED) { + _unloadPlugIns(mUniqueId); + + mCurrentState = STATE_UNINITIALIZED; + } + } + + /** + * Retrieves informations about all the plug-ins registered with DrmFramework. + * + * @return Array of DrmEngine plug-in strings + */ + public String[] getAvailableDrmEngines() { + if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + + 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"); + } else if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + return _getConstraints(mUniqueId, path, action); + } + + /** + * 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 + * @throws IOException if failed to save rights information in the given path + */ + public void saveRights( + DrmRights drmRights, String rightsPath, String contentPath) throws IOException { + if (null == drmRights || !drmRights.isValid() + || null == contentPath || contentPath.equals("")) { + throw new IllegalArgumentException("Given drmRights or contentPath is not valid"); + } else if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + if (null != rightsPath && !rightsPath.equals("")) { + DrmUtils.writeToFile(rightsPath, drmRights.getData()); + } + _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"); + } else if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + _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. false will be returned in case + * the state is uninitialized + */ + 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"); + } else if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + return _canHandle(mUniqueId, path, mimeType); + } + + /** + * Executes given drm information based on its type + * + * @param drmInfo Information needs to be processed + * @return DrmInfoStatus Instance as a result of processing given input + */ + public DrmInfoStatus processDrmInfo(DrmInfo drmInfo) { + if (null == drmInfo || !drmInfo.isValid()) { + throw new IllegalArgumentException("Given drmInfo is invalid/null"); + } else if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + return _processDrmInfo(mUniqueId, drmInfo); + } + + /** + * 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"); + } else if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + return _acquireDrmInfo(mUniqueId, drmInfoRequest); + } + + /** + * 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"); + } else if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + return _getDrmObjectType(mUniqueId, 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"); + } else if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + return _getOriginalMimeType(mUniqueId, path); + } + + /** + * 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 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"); + } else if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + return _checkRightsStatus(mUniqueId, path, action); + } + + /** + * Removes the rights associated with the given protected content + * + * @param path Path of the protected content + */ + public void removeRights(String path) { + if (null == path || path.equals("")) { + throw new IllegalArgumentException("Given path should be non null"); + } else if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + _removeRights(mUniqueId, path); + } + + /** + * Removes all the rights information of every plug-in associated with + * DRM framework. Will be used in master reset + */ + public void removeAllRights() { + if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + _removeAllRights(mUniqueId); + } + + /** + * 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"); + } else if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + 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"); + } else if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + 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) { + if (getState() == STATE_UNINITIALIZED) { + throw new IllegalStateException("Not Initialized yet"); + } + return _closeConvertSession(mUniqueId, convertId); + } + + private int getState() { + return mCurrentState; + } + + // private native interfaces + private native void _loadPlugIns(int uniqueId, Object weak_this); + + private native void _unloadPlugIns(int uniqueId); + + private native void _installDrmEngine(int uniqueId, String engineFilepath); + + private native ContentValues _getConstraints(int uniqueId, String path, int usage); + + 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 void _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 void _removeRights(int uniqueId, String path); + + private native void _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..5e5397c --- /dev/null +++ b/drm/java/android/drm/DrmUtils.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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); + 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..bc5a7bf --- /dev/null +++ b/drm/jni/android_drm_DrmManagerClient.cpp @@ -0,0 +1,755 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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-JNI" +#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/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 { + LOGD("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_loadPlugIns( + JNIEnv* env, jobject thiz, jint uniqueId, jobject weak_thiz) { + LOGV("load plugins - Enter"); + + sp<DrmManagerClientImpl> drmManager = DrmManagerClientImpl::create(&uniqueId); + + // Set the listener to DrmManager + sp<DrmManagerClient::OnInfoListener> listener = new JNIOnInfoListener(env, thiz, weak_thiz); + drmManager->setOnInfoListener(uniqueId, listener); + + setDrmManagerClientImpl(env, thiz, drmManager); + + getDrmManagerClientImpl(env, thiz)->loadPlugIns(uniqueId); + LOGV("load plugins - Exit"); +} + +static void android_drm_DrmManagerClient_unloadPlugIns(JNIEnv* env, jobject thiz, jint uniqueId) { + LOGV("unload plugins - Enter"); + sp<DrmManagerClientImpl> client = getDrmManagerClientImpl(env, thiz); + client->unloadPlugIns(uniqueId); + client->setOnInfoListener(uniqueId, NULL); + DrmManagerClientImpl::remove(uniqueId); + + sp<DrmManagerClientImpl> oldClient = setDrmManagerClientImpl(env, thiz, NULL); + if (oldClient != NULL) { + oldClient->setOnInfoListener(uniqueId, NULL); + } + + LOGV("unload plugins - 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 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 void android_drm_DrmManagerClient_saveRights( + JNIEnv* env, jobject thiz, jint uniqueId, + jobject drmRights, jstring rightsPath, jstring contentPath) { + LOGV("saveRights - Enter"); + 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")); + getDrmManagerClientImpl(env, thiz) + ->saveRights(uniqueId, rights, Utility::getStringValue(env, rightsPath), + Utility::getStringValue(env, contentPath)); + } + + delete mData; mData = NULL; + LOGV("saveRights - Exit"); +} + +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); + LOGD("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; + + 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>", "(ILandroid/drm/ProcessedData;Ljava/lang/String;)V"); + + drmInfoStatus = env->NewObject(localRef, constructorId, statusCode, 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); + LOGD("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 void android_drm_DrmManagerClient_removeRights( + JNIEnv* env, jobject thiz, jint uniqueId, jstring path) { + LOGV("removeRights Enter"); + getDrmManagerClientImpl(env, thiz)->removeRights(uniqueId, Utility::getStringValue(env, path)); + LOGV("removeRights Exit"); +} + +static void android_drm_DrmManagerClient_removeAllRights( + JNIEnv* env, jobject thiz, jint uniqueId) { + LOGV("removeAllRights Enter"); + getDrmManagerClientImpl(env, thiz)->removeAllRights(uniqueId); + LOGV("removeAllRights Exit"); +} + +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[] = { + + {"_loadPlugIns", "(ILjava/lang/Object;)V", + (void*)android_drm_DrmManagerClient_loadPlugIns}, + + {"_unloadPlugIns", "(I)V", + (void*)android_drm_DrmManagerClient_unloadPlugIns}, + + {"_getConstraints", "(ILjava/lang/String;I)Landroid/content/ContentValues;", + (void*)android_drm_DrmManagerClient_getConstraintsFromContent}, + + {"_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;)V", + (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;)V", + (void*)android_drm_DrmManagerClient_removeRights}, + + {"_removeAllRights", "(I)V", + (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..06c7c50 --- /dev/null +++ b/drm/libdrmframework/DrmManagerClient.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DrmManagerClient(Native)" +#include <utils/Log.h> + +#include <utils/String8.h> +#include <binder/IServiceManager.h> +#include <drm/DrmManagerClient.h> + +#include "DrmManagerClientImpl.h" + +using namespace android; + +DrmManagerClient::DrmManagerClient() { + int uniqueId = 0; + mDrmManagerClientImpl = NULL; + + mDrmManagerClientImpl = DrmManagerClientImpl::create(&uniqueId); + mUniqueId = uniqueId; + + loadPlugIns(); +} + +DrmManagerClient::~DrmManagerClient() { + unloadPlugIns(); + DrmManagerClientImpl::remove(mUniqueId); + + delete mDrmManagerClientImpl; mDrmManagerClientImpl = NULL; +} + +status_t DrmManagerClient::loadPlugIns() { + return mDrmManagerClientImpl->loadPlugIns(mUniqueId); +} + +status_t DrmManagerClient::setOnInfoListener( + const sp<DrmManagerClient::OnInfoListener>& infoListener) { + return mDrmManagerClientImpl->setOnInfoListener(mUniqueId, infoListener); +} + +status_t DrmManagerClient::unloadPlugIns() { + return mDrmManagerClientImpl->unloadPlugIns(mUniqueId); +} + +DrmConstraints* DrmManagerClient::getConstraints(const String8* path, const int action) { + return mDrmManagerClientImpl->getConstraints(mUniqueId, path, action); +} + +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); +} + +void 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); +} + +void DrmManagerClient::consumeRights(DecryptHandle* decryptHandle, int action, bool reserve) { + mDrmManagerClientImpl->consumeRights(mUniqueId, decryptHandle, action, reserve); +} + +void DrmManagerClient::setPlaybackStatus( + DecryptHandle* decryptHandle, int playbackStatus, int position) { + mDrmManagerClientImpl->setPlaybackStatus(mUniqueId, decryptHandle, playbackStatus, position); +} + +bool DrmManagerClient::validateAction( + const String8& path, int action, const ActionDescription& description) { + return mDrmManagerClientImpl->validateAction(mUniqueId, path, action, description); +} + +void DrmManagerClient::removeRights(const String8& path) { + mDrmManagerClientImpl->removeRights(mUniqueId, path); +} + +void DrmManagerClient::removeAllRights() { + 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); +} + +void DrmManagerClient::closeDecryptSession(DecryptHandle* decryptHandle) { + mDrmManagerClientImpl->closeDecryptSession(mUniqueId, decryptHandle); +} + +void DrmManagerClient::initializeDecryptUnit( + DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) { + mDrmManagerClientImpl->initializeDecryptUnit( + mUniqueId, decryptHandle, decryptUnitId, headerInfo); +} + +status_t DrmManagerClient::decrypt( + DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer) { + return mDrmManagerClientImpl->decrypt( + mUniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer); +} + +void DrmManagerClient::finalizeDecryptUnit(DecryptHandle* decryptHandle, int decryptUnitId) { + mDrmManagerClientImpl->finalizeDecryptUnit(mUniqueId, decryptHandle, decryptUnitId); +} + +ssize_t DrmManagerClient::pread( + DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset) { + 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..7274b49 --- /dev/null +++ b/drm/libdrmframework/DrmManagerClientImpl.cpp @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; +Vector<int> DrmManagerClientImpl::mUniqueIdVector; +sp<IDrmManagerService> DrmManagerClientImpl::mDrmManagerService; +const String8 DrmManagerClientImpl::EMPTY_STRING(""); + +DrmManagerClientImpl* DrmManagerClientImpl::create(int* pUniqueId) { + if (0 == *pUniqueId) { + int uniqueId = 0; + bool foundUniqueId = false; + srand(time(NULL)); + + while (!foundUniqueId) { + const int size = mUniqueIdVector.size(); + uniqueId = rand() % 100; + + int index = 0; + for (; index < size; ++index) { + if (mUniqueIdVector.itemAt(index) == uniqueId) { + foundUniqueId = false; + break; + } + } + if (index == size) { + foundUniqueId = true; + } + } + *pUniqueId = uniqueId; + } + mUniqueIdVector.push(*pUniqueId); + return new DrmManagerClientImpl(); +} + +void DrmManagerClientImpl::remove(int uniqueId) { + for (int i = 0; i < mUniqueIdVector.size(); i++) { + if (uniqueId == mUniqueIdVector.itemAt(i)) { + mUniqueIdVector.removeAt(i); + break; + } + } +} + +DrmManagerClientImpl::DrmManagerClientImpl() { + +} + +DrmManagerClientImpl::~DrmManagerClientImpl() { + +} + +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; +} + +status_t DrmManagerClientImpl::loadPlugIns(int uniqueId) { + return getDrmManagerService()->loadPlugIns(uniqueId); +} + +status_t DrmManagerClientImpl::loadPlugIns(int uniqueId, const String8& plugInDirPath) { + status_t status = DRM_ERROR_UNKNOWN; + if (EMPTY_STRING != plugInDirPath) { + status = getDrmManagerService()->loadPlugIns(uniqueId, plugInDirPath); + } + return status; +} + +status_t DrmManagerClientImpl::setOnInfoListener( + int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener) { + Mutex::Autolock _l(mLock); + mOnInfoListener = infoListener; + return getDrmManagerService()->setDrmServiceListener(uniqueId, this); +} + +status_t DrmManagerClientImpl::unloadPlugIns(int uniqueId) { + return getDrmManagerService()->unloadPlugIns(uniqueId); +} + +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; +} + +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; +} + +void DrmManagerClientImpl::saveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath) { + if (EMPTY_STRING != contentPath) { + getDrmManagerService()->saveRights(uniqueId, drmRights, rightsPath, contentPath); + } +} + +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; +} + +void DrmManagerClientImpl::consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) { + if (NULL != decryptHandle) { + getDrmManagerService()->consumeRights(uniqueId, decryptHandle, action, reserve); + } +} + +void DrmManagerClientImpl::setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) { + if (NULL != decryptHandle) { + getDrmManagerService()->setPlaybackStatus( + uniqueId, decryptHandle, playbackStatus, position); + } +} + +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; +} + +void DrmManagerClientImpl::removeRights(int uniqueId, const String8& path) { + if (EMPTY_STRING != path) { + getDrmManagerService()->removeRights(uniqueId, path); + } +} + +void DrmManagerClientImpl::removeAllRights(int uniqueId) { + 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) { + LOGV("Entering DrmManagerClientImpl::openDecryptSession"); + return getDrmManagerService()->openDecryptSession(uniqueId, fd, offset, length); +} + +void DrmManagerClientImpl::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) { + if (NULL != decryptHandle) { + getDrmManagerService()->closeDecryptSession( uniqueId, decryptHandle); + } +} + +void DrmManagerClientImpl::initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo) { + if ((NULL != decryptHandle) && (NULL != headerInfo)) { + getDrmManagerService()->initializeDecryptUnit( + uniqueId, decryptHandle, decryptUnitId, headerInfo); + } +} + +status_t DrmManagerClientImpl::decrypt(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer) { + status_t status = DRM_ERROR_UNKNOWN; + if ((NULL != decryptHandle) && (NULL != encBuffer) + && (NULL != decBuffer) && (NULL != *decBuffer)) { + status = getDrmManagerService()->decrypt( + uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer); + } + return status; +} + +void DrmManagerClientImpl::finalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) { + if (NULL != decryptHandle) { + getDrmManagerService()->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId); + } +} + +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..2ba9e99 --- /dev/null +++ b/drm/libdrmframework/include/DrmManager.h @@ -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. + */ + +#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 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: + + status_t loadPlugIns(int uniqueId); + + status_t loadPlugIns(int uniqueId, const String8& plugInDirPath); + + status_t setDrmServiceListener( + int uniqueId, const sp<IDrmServiceListener>& drmServiceListener); + + status_t unloadPlugIns(int uniqueId); + + status_t installDrmEngine(int uniqueId, const String8& drmEngineFile); + + DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action); + + bool canHandle(int uniqueId, const String8& path, const String8& mimeType); + + DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo); + + DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest); + + void 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); + + void consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve); + + void setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position); + + bool validateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description); + + void removeRights(int uniqueId, const String8& path); + + void 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); + + void closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle); + + void initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo); + + status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* encBuffer,DrmBuffer** decBuffer); + + void 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); + + void populate(int uniqueId); + + bool canHandle(int uniqueId, const String8& path); + + void initializePlugIns(int uniqueId); + +private: + 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..e70e6e11 --- /dev/null +++ b/drm/libdrmframework/include/DrmManagerClientImpl.h @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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: + /** + * Initialize DRM Manager + * load available plug-ins from default plugInDirPath + * + * @param[in] uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t loadPlugIns(int uniqueId); + + /** + * Finalize DRM Manager + * release resources associated with each plug-in + * unload all plug-ins and etc. + * + * @param[in] uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t unloadPlugIns(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); + + /** + * 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 + */ + void 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. + */ + void 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. + */ + void 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 + */ + void 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 + */ + void 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); + + /** + * Close the decrypt session for the given handle + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + */ + void 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 + */ + void 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 + * @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); + + /** + * 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 + */ + void 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: + /** + * Initialize DRM Manager + * load available plug-ins from plugInDirPath + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] plugInDirPath Directory from where to load plug-ins + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t loadPlugIns(int uniqueId, const String8& plugInDirPath); + + /** + * 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 Vector<int> mUniqueIdVector; + 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..fef121c --- /dev/null +++ b/drm/libdrmframework/include/DrmManagerService.h @@ -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. + */ + +#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: + status_t loadPlugIns(int uniqueId); + + status_t loadPlugIns(int uniqueId, const String8& plugInDirPath); + + status_t setDrmServiceListener( + int uniqueId, const sp<IDrmServiceListener>& drmServiceListener); + + status_t unloadPlugIns(int uniqueId); + + status_t installDrmEngine(int uniqueId, const String8& drmEngineFile); + + DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action); + + bool canHandle(int uniqueId, const String8& path, const String8& mimeType); + + DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo); + + DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest); + + void 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); + + void consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve); + + void setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position); + + bool validateAction(int uniqueId, const String8& path, + int action, const ActionDescription& description); + + void removeRights(int uniqueId, const String8& path); + + void 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); + + void closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle); + + void initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo); + + status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer); + + void 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..a4d128a --- /dev/null +++ b/drm/libdrmframework/include/IDrmManagerService.h @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 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 { + LOAD_PLUGINS = IBinder::FIRST_CALL_TRANSACTION, + LOAD_PLUGINS_FROM_PATH, + SET_DRM_SERVICE_LISTENER, + UNLOAD_PLUGINS, + INSTALL_DRM_ENGINE, + GET_CONSTRAINTS_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, + CLOSE_DECRYPT_SESSION, + INITIALIZE_DECRYPT_UNIT, + DECRYPT, + FINALIZE_DECRYPT_UNIT, + PREAD + }; + +public: + DECLARE_META_INTERFACE(DrmManagerService); + +public: + virtual status_t loadPlugIns(int uniqueId) = 0; + + virtual status_t loadPlugIns(int uniqueId, const String8& plugInDirPath) = 0; + + virtual status_t setDrmServiceListener( + int uniqueId, const sp<IDrmServiceListener>& infoListener) = 0; + + virtual status_t unloadPlugIns(int uniqueId) = 0; + + virtual status_t installDrmEngine(int uniqueId, const String8& drmEngineFile) = 0; + + virtual DrmConstraints* getConstraints( + int uniqueId, const String8* path, const int action) = 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 void 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 void consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) = 0; + + virtual void 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 void removeRights(int uniqueId, const String8& path) = 0; + + virtual void 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 void closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) = 0; + + virtual void 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) = 0; + + virtual void 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 status_t loadPlugIns(int uniqueId); + + virtual status_t loadPlugIns(int uniqueId, const String8& plugInDirPath); + + virtual status_t setDrmServiceListener( + int uniqueId, const sp<IDrmServiceListener>& infoListener); + + virtual status_t unloadPlugIns(int uniqueId); + + virtual status_t installDrmEngine(int uniqueId, const String8& drmEngineFile); + + virtual DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action); + + 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 void 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 void consumeRights( + int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve); + + virtual void setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position); + + virtual bool validateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description); + + virtual void removeRights(int uniqueId, const String8& path); + + virtual void 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 void closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle); + + virtual void 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); + + virtual void 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..022149e --- /dev/null +++ b/drm/libdrmframework/include/ReadWriteUtils.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 __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); + /** + * 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/include/DrmEngineBase.h b/drm/libdrmframework/plugins/common/include/DrmEngineBase.h new file mode 100644 index 0000000..667958a --- /dev/null +++ b/drm/libdrmframework/plugins/common/include/DrmEngineBase.h @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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); + + 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); + + void 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); + + void consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve); + + void setPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position); + + bool validateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description); + + void removeRights(int uniqueId, const String8& path); + + void removeAllRights(int uniqueId); + + void 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); + + void closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle); + + void initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo); + + status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer); + + void 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; + + /** + * 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 + */ + virtual void 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. + */ + virtual void 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. + */ + virtual void 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 + */ + virtual void 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 + */ + virtual void 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 + */ + virtual void 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; + + /** + * Close the decrypt session for the given handle + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + */ + virtual void 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 + */ + virtual void 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 + * @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) = 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 + */ + virtual void 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..0d52f66 --- /dev/null +++ b/drm/libdrmframework/plugins/common/include/IDrmEngine.h @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 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 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 + */ + virtual void 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. + */ + virtual void 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. + */ + virtual void 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 + */ + virtual void 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 + */ + virtual void 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 + */ + virtual void 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; + + /** + * Close the decrypt session for the given handle + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + */ + virtual void 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 + */ + virtual void 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 + * @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) = 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 + */ + virtual void 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/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..d2c7852 --- /dev/null +++ b/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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); + + 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); + + void 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); + + void onConsumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve); + + void onSetPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position); + + bool onValidateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description); + + void onRemoveRights(int uniqueId, const String8& path); + + void onRemoveAllRights(int uniqueId); + + void 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); + + void onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle); + + void onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo); + + status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer); + + void 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..2655d0b --- /dev/null +++ b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/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() { + +} + +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, emptyBuffer, drmInfo->getMimeType()); + break; + } + case DrmInfoRequest::TYPE_UNREGISTRATION_INFO: { + const DrmBuffer* emptyBuffer = new DrmBuffer(); + drmInfoStatus + = new DrmInfoStatus(DrmInfoStatus::STATUS_OK, 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, 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; +} + +void DrmPassthruPlugIn::onSaveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath) { + LOGD("DrmPassthruPlugIn::onSaveRights : %d", uniqueId); +} + +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; +} + +void DrmPassthruPlugIn::onConsumeRights(int uniqueId, DecryptHandle* decryptHandle, + int action, bool reserve) { + LOGD("DrmPassthruPlugIn::onConsumeRights() : %d", uniqueId); +} + +void DrmPassthruPlugIn::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle, + int playbackStatus, int position) { + LOGD("DrmPassthruPlugIn::onSetPlaybackStatus() : %d", uniqueId); +} + +bool DrmPassthruPlugIn::onValidateAction(int uniqueId, const String8& path, + int action, const ActionDescription& description) { + LOGD("DrmPassthruPlugIn::onValidateAction() : %d", uniqueId); + return true; +} + +void DrmPassthruPlugIn::onRemoveRights(int uniqueId, const String8& path) { + LOGD("DrmPassthruPlugIn::onRemoveRights() : %d", uniqueId); +} + +void DrmPassthruPlugIn::onRemoveAllRights(int uniqueId) { + LOGD("DrmPassthruPlugIn::onRemoveAllRights() : %d", uniqueId); +} + +void DrmPassthruPlugIn::onOpenConvertSession(int uniqueId, int convertId) { + LOGD("DrmPassthruPlugIn::onOpenConvertSession() : %d", uniqueId); +} + +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; +} + +void 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; + } +} + +void DrmPassthruPlugIn::onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo) { + LOGD("DrmPassthruPlugIn::onInitializeDecryptUnit() : %d", uniqueId); +} + +status_t DrmPassthruPlugIn::onDecrypt(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer) { + 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; +} + +void DrmPassthruPlugIn::onFinalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) { + LOGD("DrmPassthruPlugIn::onFinalizeDecryptUnit() : %d", uniqueId); +} + +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 345f810..76cde73 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/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..5e8817c --- /dev/null +++ b/include/drm/DrmInfoEvent.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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: + //! 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 = 0x0000001; + //! TYPE_REMOVE_RIGHTS, when the rights needs to be removed completely. + static const int TYPE_REMOVE_RIGHTS = 0x0000002; + //! TYPE_RIGHTS_INSTALLED, when the rights are downloaded and installed ok. + static const int TYPE_RIGHTS_INSTALLED = 0x0000003; + //! TYPE_RIGHTS_NOT_INSTALLED, when something went wrong installing the rights + static const int TYPE_RIGHTS_NOT_INSTALLED = 0x0000004; + //! TYPE_RIGHTS_RENEWAL_NOT_ALLOWED, when the server rejects renewal of rights + static const int TYPE_RIGHTS_RENEWAL_NOT_ALLOWED = 0x0000005; + //! TYPE_NOT_SUPPORTED, when answer from server can not be handled by the native agent + static const int TYPE_NOT_SUPPORTED = 0x0000006; + //! 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 = 0x0000007; + //! 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 = 0x0000008; + //! 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 = 0x0000009; + +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..806aea1 --- /dev/null +++ b/include/drm/DrmInfoStatus.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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] _drmBuffer Rights information + * @param[in] _mimeType MIME type + */ + DrmInfoStatus(int _statusCode, const DrmBuffer* _drmBuffer, const String8& _mimeType); + + /** + * Destructor for DrmInfoStatus + */ + virtual ~DrmInfoStatus() { + + } + +public: + int statusCode; + 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..7d14c44 --- /dev/null +++ b/include/drm/DrmManagerClient.h @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <binder/IInterface.h> +#include "drm_framework_common.h" + +namespace android { + +class DrmInfo; +class DrmRights; +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 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); + + /** + * Close the decrypt session for the given handle + * + * @param[in] decryptHandle Handle for the decryption session + */ + void 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. + */ + void 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. + */ + void 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 + */ + void 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 + * @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); + + /** + * 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 + */ + void 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); + + /** + * 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 + */ + void 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 + */ + void removeRights(const String8& path); + + /** + * Removes all the rights information of each plug-in associated with + * DRM framework. Will be used in master reset + * + */ + void 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: + /** + * Initialize DRM Manager + * load available plug-ins from default plugInDirPath + * + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t loadPlugIns(); + + /** + * Finalize DRM Manager + * release resources associated with each plug-in + * unload all plug-ins and etc. + * + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ + status_t unloadPlugIns(); + +private: + int mUniqueId; + DrmManagerClientImpl* mDrmManagerClientImpl; +}; + +}; + +#endif /* __DRM_MANAGER_CLIENT_H__ */ + diff --git a/include/drm/DrmRights.h b/include/drm/DrmRights.h new file mode 100644 index 0000000..e04a066 --- /dev/null +++ b/include/drm/DrmRights.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; +}; + +}; + +#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..8b8a9f5 --- /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) + * + * DecryptAPI::TYPE_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) + * + * DecryptAPI::TYPE_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/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/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 013f8fc..3333268 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -1215,8 +1215,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/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp index 163b2db..460b74f 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/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/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index b8b9e53..a0615ef 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -27,6 +27,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.os.Binder; import android.os.Handler; @@ -50,6 +51,8 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.GregorianCalendar; import java.util.List; +import java.net.InetAddress; +import java.net.UnknownHostException; /** * @hide @@ -805,6 +808,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 @@ -814,6 +819,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; @@ -823,11 +847,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/MountService.java b/services/java/com/android/server/MountService.java index cfba07a..131dded 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -1096,7 +1096,7 @@ class MountService extends IMountService.Stub private void setUmsEnabling(boolean enable) { synchronized (mListeners) { - mUmsEnabling = true; + mUmsEnabling = enable; } } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 0a5b72b..518cfd9 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; @@ -452,6 +452,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 @@ -1070,17 +1081,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/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index d7a3596..51467e8 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -1482,6 +1482,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 diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 34753e7..955d5dd 100644 --- 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; @@ -1110,7 +1111,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(); @@ -3750,10 +3751,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; + } + } } } } @@ -6420,7 +6423,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; } 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/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp index 0240748..fdf9abc 100644 --- a/services/surfaceflinger/LayerBuffer.cpp +++ b/services/surfaceflinger/LayerBuffer.cpp @@ -495,7 +495,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 b4a3c95..ddfc520 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/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 8e03c5a..5de0426 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/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java index d3a34ec..90f9e8c 100644 --- a/telephony/java/com/android/internal/telephony/IccCard.java +++ b/telephony/java/com/android/internal/telephony/IccCard.java @@ -672,12 +672,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/IccUtils.java b/telephony/java/com/android/internal/telephony/IccUtils.java index 71936f1..957eddd 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)); } diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index 9afade3..3dd9a01 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -1710,4 +1710,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 ff28773..1da9d4d 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -1018,6 +1018,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. */ @@ -1026,4 +1033,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 e1511e6..5e7dcb0 100644 --- a/telephony/java/com/android/internal/telephony/PhoneProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java @@ -839,4 +839,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 3d410fd..569ac5c 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -2339,7 +2339,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; @@ -2481,8 +2481,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; @@ -2490,8 +2490,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; @@ -2499,8 +2499,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; @@ -2508,8 +2508,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; @@ -3406,6 +3406,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); } @@ -3419,6 +3421,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/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java index a636a4b..2f5d3ec 100644 --- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java +++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java @@ -24,7 +24,6 @@ import android.provider.Telephony.Sms.Intents; import android.util.Config; import android.util.Log; - /** * WAP push handler class. * @@ -59,7 +58,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 +67,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 +80,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,136 +101,44 @@ 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; - 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 (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Unknown Content-Type = " + mimeType); - return Intents.RESULT_SMS_HANDLED; - } - } - 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; - } + index += pduDecoder.getDecodedDataLength(); - 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; + String permission; - 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); - - mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH"); - } + if (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); + } - 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); + 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_PUSH_CO); + intent.setType(mimeType); intent.putExtra("transactionId", transactionId); intent.putExtra("pduType", pduType); intent.putExtra("header", header); - intent.putExtra("data", pdu); + intent.putExtra("data", intentData); + intent.putExtra("contentTypeParameters", pduDecoder.getContentParameters()); - mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH"); - } + mSmsDispatcher.dispatch(intent, permission); - private void dispatchWapPdu_MMS(byte[] pdu, int transactionId, int pduType, - int headerStartIndex, int headerLength) { - byte[] header = new byte[headerLength]; - System.arraycopy(pdu, headerStartIndex, header, 0, header.length); - int dataIndex = headerStartIndex + headerLength; - byte[] data = new byte[pdu.length - dataIndex]; - System.arraycopy(pdu, dataIndex, data, 0, data.length); - - Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION); - intent.setType(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS); - intent.putExtra("transactionId", transactionId); - intent.putExtra("pduType", pduType); - intent.putExtra("header", header); - intent.putExtra("data", data); - - mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_MMS"); + return Activity.RESULT_OK; } -} - +}
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java index 336bc82..6bf6b13 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,35 +31,176 @@ 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 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 int PARAMETER_ID_X_WAP_APPLICATION_ID = 0x2f; + 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>(); + + 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; } @@ -69,17 +211,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); } @@ -87,13 +229,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) { @@ -111,7 +273,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; @@ -120,10 +282,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; } @@ -134,7 +296,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) { @@ -150,10 +312,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) { @@ -175,7 +337,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) { @@ -185,22 +347,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; @@ -212,7 +374,7 @@ public class WspTypeDecoder { index++; } - dataLength = index - startIndex + 1; + dataLength = index - startIndex + 1; stringValue = new String(wspData, startIndex, dataLength - 1); return rtrn; @@ -225,7 +387,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) { @@ -240,29 +402,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; } /** @@ -272,7 +565,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); @@ -285,7 +578,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); @@ -298,7 +591,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) { @@ -315,7 +609,7 @@ public class WspTypeDecoder { * * @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); @@ -328,7 +622,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); @@ -354,4 +648,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..1e23e34 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,17 @@ 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; + 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 +137,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 +147,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 +161,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 +242,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 +298,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 +314,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 +331,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 +358,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,35 +504,35 @@ 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) { + public static CatService getInstance(CommandsInterface ci, IccRecords ir, + Context context, IccFileHandler fh, IccCard ic) { if (sInstance == null) { - if (ci == null || sr == null || context == null || fh == null - || sc == null) { + if (ci == null || ir == null || context == null || fh == null + || ic == null) { return null; } - HandlerThread thread = new HandlerThread("Stk Telephony service"); + HandlerThread thread = new HandlerThread("Cat 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; + sInstance = new CatService(ci, ir, context, fh, ic); + CatLog.d(sInstance, "NEW sInstance"); + } else if ((ir != null) && (mIccRecords != ir)) { + CatLog.d(sInstance, "Reinitialize the Service with SIMRecords"); + mIccRecords = ir; // re-Register for SIM ready event. - mSimRecords.registerForRecordsLoaded(sInstance, MSG_ID_SIM_LOADED, null); - StkLog.d(sInstance, "sr changed reinitialize and return current sInstance"); + mIccRecords.registerForRecordsLoaded(sInstance, MSG_ID_ICC_RECORDS_LOADED, null); + CatLog.d(sInstance, "sr changed reinitialize and return current sInstance"); } else { - StkLog.d(sInstance, "Return current sInstance"); + CatLog.d(sInstance, "Return current sInstance"); } return sInstance; } @@ -496,7 +554,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 +571,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 +593,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 +606,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 +621,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 f48c956..f31bf24 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/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index 689a972..43971ff 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) { @@ -1480,4 +1482,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 aa16fa3..fe7a5cb 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 = "#"; @@ -446,22 +453,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/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 d711a80..c80c608 100644 --- 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; // ***** Constructor @@ -559,6 +562,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; @@ -994,6 +1004,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); @@ -1017,6 +1043,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. @@ -1247,6 +1279,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)); @@ -1426,8 +1461,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; @@ -1464,4 +1503,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/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index 50dd402..9a3c476 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -930,6 +930,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: diff --git a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java index 41e527c..b642541 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/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/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 21f3be4..37976ee 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/Resource.cpp b/tools/aapt/Resource.cpp index 9c5fcda..c8ba904 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -835,7 +835,9 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) bool hasErrors = false; if (drawables != NULL) { - err = preProcessImages(bundle, assets, drawables); + if (bundle->getOutputAPKFile() != NULL) { + err = preProcessImages(bundle, assets, drawables); + } if (err == NO_ERROR) { err = makeFileResources(bundle, assets, &table, drawables, "drawable"); if (err != NO_ERROR) { diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt index 09b392b..c59e20d 100644 --- a/tools/layoutlib/create/README.txt +++ b/tools/layoutlib/create/README.txt @@ -4,68 +4,196 @@ - Description - --------------- -makeLayoutLib generates a library used by the Eclipse graphical layout editor +Layoutlib_create generates a JAR library used by the Eclipse graphical layout editor to perform layout. - - Usage - --------- - ./makeLayoutLib path/to/android.jar destination.jar + ./layoutlib_create path/to/android.jar destination.jar + + +- Design Overview - +------------------- + +Layoutlib_create uses the "android.jar" containing all the Java code used by Android +as generated by the Android build, right before the classes are converted to a DEX format. + +The Android JAR can't be used directly in Eclipse: +- it contains references to native code (which we want to avoid in Eclipse), +- some classes need to be overridden, for example all the drawing code that is + replaced by Java 2D calls in Eclipse. +- some of the classes that need to be changed are final and/or we need access + to their private internal state. + +Consequently this tool: +- parses the input JAR, +- modifies some of the classes directly using some bytecode manipulation, +- filters some packages and removes some that we don't want to end in the output JAR, +- injects some new classes, +- and generates a modified JAR file that is suitable for the Android plugin + for Eclipse to perform rendering. + +The ASM library is used to do the bytecode modification using its visitor pattern API. + +The layoutlib_create is *NOT* generic. There is no configuration file. Instead all the +configuration is done in the main() method and the CreateInfo structure is expected to +change with the Android platform as new classes are added, changed or removed. + +The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the +platform, that provides all the necessary missing implementation for rendering graphics +in Eclipse. - Implementation Notes - ------------------------ -The goal of makeLayoutLib is to list all the classes from the input jar and create a new -jar that only keeps certain classes and create stubs for all their dependencies. +The tool works in two phases: +- first analyze the input jar (AsmAnalyzer class) +- then generate the output jar (AsmGenerator class), + + +- Analyzer +---------- + +The goal of the analyzer is to create a graph of all the classes from the input JAR +with their dependencies and then only keep the ones we want. + +To do that, the analyzer is created with a list of base classes to keep -- everything +that derives from these is kept. Currently the one such class is android.view.View: +since we want to render layouts, anything that is sort of the view needs to be kept. + +The analyzer is also given a list of class names to keep in the output. +This is done using shell-like glob patterns that filter on the fully-qualified +class names, for example "android.*.R**" ("*" does not matches dots whilst "**" does, +and "." and "$" are interpreted as-is). +In practice we almost but not quite request the inclusion of full packages. + +With this information, the analyzer parses the input zip to find all the classes. +All classes deriving from the requested bases classes are kept. +All classes which name matched the glob pattern are kept. +The analysis then finds all the dependencies of the classes that are to be kept +using an ASM visitor on the class, the field types, the method types and annotations types. +Classes that belong to the current JRE are excluded. + +The output of the analyzer is a set of ASM ClassReader instances which are then +fed to the generator. + + +- Generator +----------- -First the input jar is parsed to find all the classes defined. +The generator is constructed from a CreateInfo struct that acts as a config file +and lists: +- the classes to inject in the output JAR -- these classes are directly implemented + in layoutlib_create and will be used to interface with the renderer in Eclipse. +- specific methods to override (see method stubs details below). +- specific methods to remove based on their return type. +- specific classes to rename. -In the Main(), the following list of classes are hardcoded (TODO config file later): -- keep all classes that derive from android.view.View. -- keep all classes in the android.view and android.widget packages (sub-packages excluded). -- keep specific classes such as android.policy.PhoneLayoutInflater. +Each of these are specific strategies we use to be able to modify the Android code +to fit within the Eclipse renderer. These strategies are explained beow. -For each class to keep, their dependencies are examined using BCEL. -A dependency is defined as a class needed to instantiate the given class that should be kept, -directly or indirectly. So a dependency is a class that is used by the input class, that is -defined in the input jar and that is not part of the current JRE. +The core method of the generator is transform(): it takes an input ASM ClassReader +and modifies it to produce a byte array suitable for the final JAR file. -Dependencies are computed recursively. +The first step of the transformation is changing the name of the class in case +we requested the class to be renamed. This uses the RenameClassAdapter to also rename +all inner classes and references in methods and types. Note that other classes are +not transformed and keep referencing the original name. -Once all dependencies are found, the final jar can be created. -There are three kind of classes to write: -- classes that are to be kept as-is. They are just dumped in the new jar unchanged. -- classes that are to be kept yet contain native methods or fields. -- classes that are just dependencies. We don't want to expose their implementation in the final - jar. +The TransformClassAdapter is then used to process the potentially renamed class. +All protected or private classes are market as public. +All classes are made non-final. +Interfaces are left as-is. -The implementation of native methods and all methods of mock classes is replaced by a stub -that throws UnsupportedOperationException. +If a method has a return type that must be erased, the whole method is skipped. +Methods are also changed from protected/private to public. +The code of the methods is then kept as-is, except for native methods which are +replaced by a stub. Methods that are to be overridden are also replaced by a stub. -Incidentally, the access level of native and mock classes needs to be changed in order for -native methods to be later overridden. Methods that are "final private native" must become -non-final, non-native and at most protected. Package-default access is changed to public. -Classes that are final are made non-final. Abstract methods are left untouched. +Finally fields are also visited and changed from protected/private to public. +- Method stubs +-------------- ----- -20080617 Replace Class +As indicated above, all native and overridden methods are replaced by a stub. +We don't have the code to replace with in layoutlib_create. +Instead the StubMethodAdapter replaces the code of the method by a call to +OverrideMethod.invokeX(). When using the final JAR, the bridge can register +listeners from these overridden method calls based on the method signatures. -Some classes are basically wrappers over native objects. -Subclassing doesn't work as most methods are either static or we don't -control object creation. In this scenario the idea is to be able to -replace classes in the final jar. +The listeners are currently pretty basic: we only pass the signature of the +method being called, its caller object and a flag indicating whether the +method was native. We do not currently provide the parameters. The listener +can however specify the return value of the overridden method. -Example: android.graphics.Paint would get renamed to OriginalPaint -in the generated jar. Then in the bridge we'll introduce a replacement -Paint class that derives from OriginalPaint. +An extension being worked on is to actually replace these listeners by +direct calls to a delegate class, complete with parameters. + + +- Strategies +------------ + +We currently have 4 strategies to deal with overriding the rendering code +and make it run in Eclipse. Most of these strategies are implemented hand-in-hand +by the bridge (which runs in Eclipse) and the generator. + + +1- Class Injection + +This is the easiest: we currently inject 4 classes, namely: +- OverrideMethod and its associated MethodListener and MethodAdapter are used + to intercept calls to some specific methods that are stubbed out and change + their return value. +- CreateInfo class, which configured the generator. Not used yet, but could + in theory help us track what the generator changed. + + +2- Overriding methods + +As explained earlier, the creator doesn't have any replacement code for +methods to override. Instead it removes the original code and replaces it +by a call to a specific OveriddeMethod.invokeX(). The bridge then registers +a listener on the method signature and can provide an implementation. + + +3- Renaming classes + +This simply changes the name of a class in its definition, as well as all its +references in internal inner classes and methods. +Calls from other classes are not modified -- they keep referencing the original +class name. This allows the bridge to literally replace an implementation. + +An example will make this easier: android.graphics.Paint is the main drawing +class that we need to replace. To do so, the generator renames Paint to _original_Paint. +Later the bridge provides its own replacement version of Paint which will be used +by the rest of the Android stack. The replacement version of Paint can still use +(either by inheritance or delegation) all the original non-native code of _original_Paint +if it so desires. + +Some of the Android classes are basically wrappers over native objects and since +we don't have the native code in Eclipse, we need to provide a full alternate +implementation. Sub-classing doesn't work as some native methods are static and +we don't control object creation. This won't rename/replace the inner static methods of a given class. +4- Method erasure based on return type + +This is mostly an implementation detail of the bridge: in the Paint class +mentioned above, some inner static classes are used to pass around +attributes (e.g. FontMetrics, or the Style enum) and all the original implementation +is native. + +In this case we have a strategy that tells the generator that anything returning, for +example, the inner class Paint$Style in the Paint class should be discarded and the +bridge will provide its own implementation. + +-- +end diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java new file mode 100755 index 0000000..0689c92 --- /dev/null +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java @@ -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. + */ + +package com.android.tools.layoutlib.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Denotes a parameter or field can be null. + * <p/> + * When decorating a method call parameter, this denotes the parameter can + * legitimately be null and the method will gracefully deal with it. Typically used + * on optional parameters. + * <p/> + * When decorating a method, this denotes the method might legitimately return null. + * <p/> + * This is a marker annotation and it has no specific attributes. + */ +@Retention(RetentionPolicy.SOURCE) +public @interface Nullable { +} diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java new file mode 100755 index 0000000..e4e016b --- /dev/null +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java @@ -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. + */ + +package com.android.tools.layoutlib.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Denotes that the class, method or field has its visibility relaxed so + * that unit tests can access it. + * <p/> + * The <code>visibility</code> argument can be used to specific what the original + * visibility should have been if it had not been made public or package-private for testing. + * The default is to consider the element private. + */ +@Retention(RetentionPolicy.SOURCE) +public @interface VisibleForTesting { + /** + * Intended visibility if the element had not been made public or package-private for + * testing. + */ + enum Visibility { + /** The element should be considered protected. */ + PROTECTED, + /** The element should be considered package-private. */ + PACKAGE, + /** The element should be considered private. */ + PRIVATE + } + + /** + * Intended visibility if the element had not been made public or package-private for testing. + * If not specified, one should assume the element originally intended to be private. + */ + Visibility visibility() default Visibility.PRIVATE; +} diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java index 5424efa..722dce2 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java @@ -16,6 +16,9 @@ package com.android.tools.layoutlib.create; +import com.android.tools.layoutlib.annotations.VisibleForTesting; +import com.android.tools.layoutlib.annotations.VisibleForTesting.Visibility; + import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassVisitor; @@ -27,13 +30,18 @@ import org.objectweb.asm.Opcodes; * Indicates if a class contains any native methods. */ public class ClassHasNativeVisitor implements ClassVisitor { - + private boolean mHasNativeMethods = false; - + public boolean hasNativeMethods() { return mHasNativeMethods; } + @VisibleForTesting(visibility=Visibility.PRIVATE) + protected void setHasNativeMethods(boolean hasNativeMethods, String methodName) { + mHasNativeMethods = hasNativeMethods; + } + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { // pass @@ -65,7 +73,9 @@ public class ClassHasNativeVisitor implements ClassVisitor { public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - mHasNativeMethods |= ((access & Opcodes.ACC_NATIVE) != 0); + if ((access & Opcodes.ACC_NATIVE) != 0) { + setHasNativeMethods(true, name); + } return null; } diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java new file mode 100644 index 0000000..d6916ae --- /dev/null +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.tools.layoutlib.create; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.objectweb.asm.ClassReader; + +import java.io.IOException; +import java.util.ArrayList; + + +/** + * Tests {@link ClassHasNativeVisitor}. + */ +public class ClassHasNativeVisitorTest { + + @Test + public void testHasNative() throws IOException { + MockClassHasNativeVisitor cv = new MockClassHasNativeVisitor(); + ClassReader cr = new ClassReader( + "com.android.tools.layoutlib.create.ClassHasNativeVisitorTest$ClassWithNative"); + + cr.accept(cv, 0 /* flags */); + assertArrayEquals(new String[] { "native_method" }, cv.getMethodsFound()); + assertTrue(cv.hasNativeMethods()); + } + + @Test + public void testHasNoNative() throws IOException { + MockClassHasNativeVisitor cv = new MockClassHasNativeVisitor(); + ClassReader cr = new ClassReader( + "com.android.tools.layoutlib.create.ClassHasNativeVisitorTest$ClassWithoutNative"); + + cr.accept(cv, 0 /* flags */); + assertArrayEquals(new String[0], cv.getMethodsFound()); + assertFalse(cv.hasNativeMethods()); + } + + /** + * Overrides {@link ClassHasNativeVisitor} to collec the name of the native methods found. + */ + private static class MockClassHasNativeVisitor extends ClassHasNativeVisitor { + private ArrayList<String> mMethodsFound = new ArrayList<String>(); + + public String[] getMethodsFound() { + return mMethodsFound.toArray(new String[mMethodsFound.size()]); + } + + @Override + protected void setHasNativeMethods(boolean hasNativeMethods, String methodName) { + if (hasNativeMethods) { + mMethodsFound.add(methodName); + } + super.setHasNativeMethods(hasNativeMethods, methodName); + } + } + + /** + * Dummy test class with a native method. + */ + public static class ClassWithNative { + public ClassWithNative() { + } + + public void callTheNativeMethod() { + native_method(); + } + + private native void native_method(); + } + + /** + * Dummy test class with no native method. + */ + public static class ClassWithoutNative { + public ClassWithoutNative() { + } + + public void someMethod() { + } + } +} diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index 9d27bde..5220713 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. @@ -1475,7 +1476,7 @@ public class WifiStateTracker extends NetworkStateTracker { int netId = -1; String[] lines = reply.split("\n"); for (String line : lines) { - String[] prop = line.split(" *= *"); + String[] prop = line.split(" *= *", 2); if (prop.length < 2) continue; String name = prop[0]; @@ -2460,11 +2461,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) { |