diff options
126 files changed, 10056 insertions, 1344 deletions
@@ -159,6 +159,14 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/view/IInputMethodClient.aidl \ core/java/com/android/internal/view/IInputMethodManager.aidl \ core/java/com/android/internal/view/IInputMethodSession.aidl \ + core/java/com/trustedlogic/trustednfc/android/ILlcpConnectionlessSocket.aidl \ + core/java/com/trustedlogic/trustednfc/android/ILlcpServiceSocket.aidl \ + core/java/com/trustedlogic/trustednfc/android/ILlcpSocket.aidl \ + core/java/com/trustedlogic/trustednfc/android/INdefTag.aidl \ + core/java/com/trustedlogic/trustednfc/android/INfcManager.aidl \ + core/java/com/trustedlogic/trustednfc/android/INfcTag.aidl \ + core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl \ + core/java/com/trustedlogic/trustednfc/android/IP2pTarget.aidl \ location/java/android/location/IGeocodeProvider.aidl \ location/java/android/location/IGpsStatusListener.aidl \ location/java/android/location/IGpsStatusProvider.aidl \ diff --git a/api/current.xml b/api/current.xml index 876fe00..a37a533 100644 --- a/api/current.xml +++ b/api/current.xml @@ -24488,6 +24488,623 @@ </parameter> </method> </class> +<class name="DownloadManager" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="enqueue" + return="long" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="request" type="android.app.DownloadManager.Request"> +</parameter> +</method> +<method name="openDownloadedFile" + return="android.os.ParcelFileDescriptor" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="id" type="long"> +</parameter> +<exception name="FileNotFoundException" type="java.io.FileNotFoundException"> +</exception> +</method> +<method name="query" + return="android.database.Cursor" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="query" type="android.app.DownloadManager.Query"> +</parameter> +</method> +<method name="remove" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="id" type="long"> +</parameter> +</method> +<field name="ACTION_DOWNLOAD_COMPLETE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.DOWNLOAD_COMPLETE"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ACTION_NOTIFICATION_CLICKED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ACTION_VIEW_DOWNLOADS" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.VIEW_DOWNLOADS"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_BYTES_DOWNLOADED_SO_FAR" + type="java.lang.String" + transient="false" + volatile="false" + value=""bytes_so_far"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_DESCRIPTION" + type="java.lang.String" + transient="false" + volatile="false" + value=""description"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_ERROR_CODE" + type="java.lang.String" + transient="false" + volatile="false" + value=""error_code"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_ID" + type="java.lang.String" + transient="false" + volatile="false" + value=""_id"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_LAST_MODIFIED_TIMESTAMP" + type="java.lang.String" + transient="false" + volatile="false" + value=""last_modified_timestamp"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_LOCAL_URI" + type="java.lang.String" + transient="false" + volatile="false" + value=""local_uri"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_MEDIA_TYPE" + type="java.lang.String" + transient="false" + volatile="false" + value=""media_type"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_STATUS" + type="java.lang.String" + transient="false" + volatile="false" + value=""status"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_TITLE" + type="java.lang.String" + transient="false" + volatile="false" + value=""title"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_TOTAL_SIZE_BYTES" + type="java.lang.String" + transient="false" + volatile="false" + value=""total_size"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_URI" + type="java.lang.String" + transient="false" + volatile="false" + value=""uri"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_CANNOT_RESUME" + type="int" + transient="false" + volatile="false" + value="1008" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_DEVICE_NOT_FOUND" + type="int" + transient="false" + volatile="false" + value="1007" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_FILE_ALREADY_EXISTS" + type="int" + transient="false" + volatile="false" + value="1009" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_FILE_ERROR" + type="int" + transient="false" + volatile="false" + value="1001" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_HTTP_DATA_ERROR" + type="int" + transient="false" + volatile="false" + value="1004" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_INSUFFICIENT_SPACE" + type="int" + transient="false" + volatile="false" + value="1006" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_TOO_MANY_REDIRECTS" + type="int" + transient="false" + volatile="false" + value="1005" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_UNHANDLED_HTTP_CODE" + type="int" + transient="false" + volatile="false" + value="1002" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_UNKNOWN" + type="int" + transient="false" + volatile="false" + value="1000" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_DOWNLOAD_ID" + type="java.lang.String" + transient="false" + volatile="false" + value=""extra_download_id"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="STATUS_FAILED" + type="int" + transient="false" + volatile="false" + value="16" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="STATUS_PAUSED" + type="int" + transient="false" + volatile="false" + value="4" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="STATUS_PENDING" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="STATUS_RUNNING" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="STATUS_SUCCESSFUL" + type="int" + transient="false" + volatile="false" + value="8" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="DownloadManager.Query" + extends="java.lang.Object" + abstract="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="DownloadManager.Query" + type="android.app.DownloadManager.Query" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="setFilterById" + return="android.app.DownloadManager.Query" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="id" type="long"> +</parameter> +</method> +<method name="setFilterByStatus" + return="android.app.DownloadManager.Query" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="flags" type="int"> +</parameter> +</method> +</class> +<class name="DownloadManager.Request" + extends="java.lang.Object" + abstract="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="DownloadManager.Request" + type="android.app.DownloadManager.Request" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uri" type="android.net.Uri"> +</parameter> +</constructor> +<method name="addRequestHeader" + return="android.app.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="header" type="java.lang.String"> +</parameter> +<parameter name="value" type="java.lang.String"> +</parameter> +</method> +<method name="setAllowedNetworkTypes" + return="android.app.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="flags" type="int"> +</parameter> +</method> +<method name="setAllowedOverRoaming" + return="android.app.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="allowed" type="boolean"> +</parameter> +</method> +<method name="setDescription" + return="android.app.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="description" type="java.lang.CharSequence"> +</parameter> +</method> +<method name="setDestinationInExternalFilesDir" + return="android.app.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="dirType" type="java.lang.String"> +</parameter> +<parameter name="subPath" type="java.lang.String"> +</parameter> +</method> +<method name="setDestinationInExternalPublicDir" + return="android.app.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="dirType" type="java.lang.String"> +</parameter> +<parameter name="subPath" type="java.lang.String"> +</parameter> +</method> +<method name="setDestinationUri" + return="android.app.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uri" type="android.net.Uri"> +</parameter> +</method> +<method name="setMimeType" + return="android.app.DownloadManager.Request" + 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="setShowRunningNotification" + return="android.app.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="show" type="boolean"> +</parameter> +</method> +<method name="setTitle" + return="android.app.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="title" type="java.lang.CharSequence"> +</parameter> +</method> +<method name="setVisibleInDownloadsUi" + return="android.app.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="isVisible" type="boolean"> +</parameter> +</method> +<field name="NETWORK_MOBILE" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="NETWORK_WIFI" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> <class name="ExpandableListActivity" extends="android.app.Activity" abstract="false" @@ -52002,6 +52619,118 @@ > </field> </class> +<class name="ObbInfo" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.os.Parcelable"> +</implements> +<method name="describeContents" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="writeToParcel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="dest" type="android.os.Parcel"> +</parameter> +<parameter name="parcelableFlags" type="int"> +</parameter> +</method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="OBB_OVERLAY" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="flags" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="packageName" + type="java.lang.String" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="version" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="ObbScanner" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="getObbInfo" + return="android.content.res.ObbInfo" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="filePath" type="java.lang.String"> +</parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +</class> <class name="Resources" extends="java.lang.Object" abstract="false" @@ -95347,623 +96076,6 @@ > </field> </class> -<class name="DownloadManager" - extends="java.lang.Object" - abstract="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<method name="enqueue" - return="long" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="request" type="android.net.DownloadManager.Request"> -</parameter> -</method> -<method name="openDownloadedFile" - return="android.os.ParcelFileDescriptor" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="id" type="long"> -</parameter> -<exception name="FileNotFoundException" type="java.io.FileNotFoundException"> -</exception> -</method> -<method name="query" - return="android.database.Cursor" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="query" type="android.net.DownloadManager.Query"> -</parameter> -</method> -<method name="remove" - return="void" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="id" type="long"> -</parameter> -</method> -<field name="ACTION_DOWNLOAD_COMPLETE" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.intent.action.DOWNLOAD_COMPLETE"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="ACTION_NOTIFICATION_CLICKED" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="ACTION_VIEW_DOWNLOADS" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.intent.action.VIEW_DOWNLOADS"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="COLUMN_BYTES_DOWNLOADED_SO_FAR" - type="java.lang.String" - transient="false" - volatile="false" - value=""bytes_so_far"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="COLUMN_DESCRIPTION" - type="java.lang.String" - transient="false" - volatile="false" - value=""description"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="COLUMN_ERROR_CODE" - type="java.lang.String" - transient="false" - volatile="false" - value=""error_code"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="COLUMN_ID" - type="java.lang.String" - transient="false" - volatile="false" - value=""_id"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="COLUMN_LAST_MODIFIED_TIMESTAMP" - type="java.lang.String" - transient="false" - volatile="false" - value=""last_modified_timestamp"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="COLUMN_LOCAL_URI" - type="java.lang.String" - transient="false" - volatile="false" - value=""local_uri"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="COLUMN_MEDIA_TYPE" - type="java.lang.String" - transient="false" - volatile="false" - value=""media_type"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="COLUMN_STATUS" - type="java.lang.String" - transient="false" - volatile="false" - value=""status"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="COLUMN_TITLE" - type="java.lang.String" - transient="false" - volatile="false" - value=""title"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="COLUMN_TOTAL_SIZE_BYTES" - type="java.lang.String" - transient="false" - volatile="false" - value=""total_size"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="COLUMN_URI" - type="java.lang.String" - transient="false" - volatile="false" - value=""uri"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="ERROR_CANNOT_RESUME" - type="int" - transient="false" - volatile="false" - value="1008" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="ERROR_DEVICE_NOT_FOUND" - type="int" - transient="false" - volatile="false" - value="1007" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="ERROR_FILE_ALREADY_EXISTS" - type="int" - transient="false" - volatile="false" - value="1009" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="ERROR_FILE_ERROR" - type="int" - transient="false" - volatile="false" - value="1001" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="ERROR_HTTP_DATA_ERROR" - type="int" - transient="false" - volatile="false" - value="1004" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="ERROR_INSUFFICIENT_SPACE" - type="int" - transient="false" - volatile="false" - value="1006" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="ERROR_TOO_MANY_REDIRECTS" - type="int" - transient="false" - volatile="false" - value="1005" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="ERROR_UNHANDLED_HTTP_CODE" - type="int" - transient="false" - volatile="false" - value="1002" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="ERROR_UNKNOWN" - type="int" - transient="false" - volatile="false" - value="1000" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="EXTRA_DOWNLOAD_ID" - type="java.lang.String" - transient="false" - volatile="false" - value=""extra_download_id"" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="STATUS_FAILED" - type="int" - transient="false" - volatile="false" - value="16" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="STATUS_PAUSED" - type="int" - transient="false" - volatile="false" - value="4" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="STATUS_PENDING" - type="int" - transient="false" - volatile="false" - value="1" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="STATUS_RUNNING" - type="int" - transient="false" - volatile="false" - value="2" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="STATUS_SUCCESSFUL" - type="int" - transient="false" - volatile="false" - value="8" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -</class> -<class name="DownloadManager.Query" - extends="java.lang.Object" - abstract="false" - static="true" - final="false" - deprecated="not deprecated" - visibility="public" -> -<constructor name="DownloadManager.Query" - type="android.net.DownloadManager.Query" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</constructor> -<method name="setFilterById" - return="android.net.DownloadManager.Query" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="id" type="long"> -</parameter> -</method> -<method name="setFilterByStatus" - return="android.net.DownloadManager.Query" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="flags" type="int"> -</parameter> -</method> -</class> -<class name="DownloadManager.Request" - extends="java.lang.Object" - abstract="false" - static="true" - final="false" - deprecated="not deprecated" - visibility="public" -> -<constructor name="DownloadManager.Request" - type="android.net.DownloadManager.Request" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="uri" type="android.net.Uri"> -</parameter> -</constructor> -<method name="addRequestHeader" - return="android.net.DownloadManager.Request" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="header" type="java.lang.String"> -</parameter> -<parameter name="value" type="java.lang.String"> -</parameter> -</method> -<method name="setAllowedNetworkTypes" - return="android.net.DownloadManager.Request" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="flags" type="int"> -</parameter> -</method> -<method name="setAllowedOverRoaming" - return="android.net.DownloadManager.Request" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="allowed" type="boolean"> -</parameter> -</method> -<method name="setDescription" - return="android.net.DownloadManager.Request" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="description" type="java.lang.CharSequence"> -</parameter> -</method> -<method name="setDestinationInExternalFilesDir" - return="android.net.DownloadManager.Request" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="context" type="android.content.Context"> -</parameter> -<parameter name="dirType" type="java.lang.String"> -</parameter> -<parameter name="subPath" type="java.lang.String"> -</parameter> -</method> -<method name="setDestinationInExternalPublicDir" - return="android.net.DownloadManager.Request" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="dirType" type="java.lang.String"> -</parameter> -<parameter name="subPath" type="java.lang.String"> -</parameter> -</method> -<method name="setDestinationUri" - return="android.net.DownloadManager.Request" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="uri" type="android.net.Uri"> -</parameter> -</method> -<method name="setMimeType" - return="android.net.DownloadManager.Request" - 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="setShowRunningNotification" - return="android.net.DownloadManager.Request" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="show" type="boolean"> -</parameter> -</method> -<method name="setTitle" - return="android.net.DownloadManager.Request" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="title" type="java.lang.CharSequence"> -</parameter> -</method> -<method name="setVisibleInDownloadsUi" - return="android.net.DownloadManager.Request" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="isVisible" type="boolean"> -</parameter> -</method> -<field name="NETWORK_MOBILE" - type="int" - transient="false" - volatile="false" - value="1" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="NETWORK_WIFI" - type="int" - transient="false" - volatile="false" - value="2" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -</class> <class name="LocalServerSocket" extends="java.lang.Object" abstract="false" @@ -129032,6 +129144,38 @@ </package> <package name="android.os.storage" > +<class name="OnObbStateChangeListener" + extends="java.lang.Object" + abstract="true" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="OnObbStateChangeListener" + type="android.os.storage.OnObbStateChangeListener" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="onObbStateChange" + 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> +<parameter name="state" type="java.lang.String"> +</parameter> +</method> +</class> <class name="StorageManager" extends="java.lang.Object" abstract="false" @@ -129082,6 +129226,8 @@ </parameter> <parameter name="key" type="java.lang.String"> </parameter> +<parameter name="listener" type="android.os.storage.OnObbStateChangeListener"> +</parameter> </method> <method name="unmountObb" return="boolean" @@ -129097,6 +129243,8 @@ </parameter> <parameter name="force" type="boolean"> </parameter> +<parameter name="listener" type="android.os.storage.OnObbStateChangeListener"> +</parameter> <exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException"> </exception> </method> diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 8b54871..f55b746 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -46,6 +46,7 @@ #include <media/mediametadataretriever.h> #include <media/stagefright/foundation/hexdump.h> +#include <media/stagefright/MPEG2TSWriter.h> #include <media/stagefright/MPEG4Writer.h> #include <fcntl.h> @@ -366,8 +367,13 @@ status_t DetectSyncSource::read( static void writeSourcesToMP4( Vector<sp<MediaSource> > &sources, bool syncInfoPresent) { +#if 0 sp<MPEG4Writer> writer = new MPEG4Writer(gWriteMP4Filename.string()); +#else + sp<MPEG2TSWriter> writer = + new MPEG2TSWriter(gWriteMP4Filename.string()); +#endif // at most one minute. writer->setMaxFileDuration(60000000ll); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 1bbf9ea..e93e684 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -67,7 +67,6 @@ import android.location.LocationManager; import android.media.AudioManager; import android.net.ConnectivityManager; import android.net.IConnectivityManager; -import android.net.DownloadManager; import android.net.ThrottleManager; import android.net.IThrottleManager; import android.net.Uri; @@ -103,6 +102,8 @@ import android.view.inputmethod.InputMethodManager; import android.accounts.AccountManager; import android.accounts.IAccountManager; import android.app.admin.DevicePolicyManager; +import com.trustedlogic.trustednfc.android.NfcManager; +import com.trustedlogic.trustednfc.android.INfcManager; import com.android.internal.os.IDropBoxManagerService; @@ -172,6 +173,7 @@ class ContextImpl extends Context { private static ThrottleManager sThrottleManager; private static WifiManager sWifiManager; private static LocationManager sLocationManager; + private static NfcManager sNfcManager; private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs = new HashMap<String, SharedPreferencesImpl>(); @@ -970,6 +972,8 @@ class ContextImpl extends Context { return getClipboardManager(); } else if (WALLPAPER_SERVICE.equals(name)) { return getWallpaperManager(); + } else if (NFC_SERVICE.equals(name)) { + return getNfcManager(); } else if (DROPBOX_SERVICE.equals(name)) { return getDropBoxManager(); } else if (DEVICE_POLICY_SERVICE.equals(name)) { @@ -1205,6 +1209,21 @@ class ContextImpl extends Context { return mDownloadManager; } + private NfcManager getNfcManager() + { + synchronized (sSync) { + if (sNfcManager == null) { + IBinder b = ServiceManager.getService(NFC_SERVICE); + if (b == null) { + return null; + } + INfcManager service = INfcManager.Stub.asInterface(b); + sNfcManager = new NfcManager(service, mMainThread.getHandler()); + } + } + return sNfcManager; + } + @Override public int checkPermission(String permission, int pid, int uid) { if (permission == null) { diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/app/DownloadManager.java index 1f220d2..c6fef4f 100644 --- a/core/java/android/net/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.net; +package android.app; import android.content.ContentResolver; import android.content.ContentUris; @@ -22,6 +22,8 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.CursorWrapper; +import android.net.ConnectivityManager; +import android.net.Uri; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.provider.BaseColumns; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 0100550..693be21 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1207,7 +1207,7 @@ public abstract class Context { * <dt> {@link #UI_MODE_SERVICE} ("uimode") * <dd> An {@link android.app.UiModeManager} for controlling UI modes. * <dt> {@link #DOWNLOAD_SERVICE} ("download") - * <dd> A {@link android.net.DownloadManager} for requesting HTTP downloads + * <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads * </dl> * * <p>Note: System services obtained via this API may be closely associated with @@ -1256,7 +1256,7 @@ public abstract class Context { * @see #UI_MODE_SERVICE * @see android.app.UiModeManager * @see #DOWNLOAD_SERVICE - * @see android.net.DownloadManager + * @see android.app.DownloadManager */ public abstract Object getSystemService(String name); @@ -1539,7 +1539,7 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a - * {@link android.net.DownloadManager} for requesting HTTP downloads. + * {@link android.app.DownloadManager} for requesting HTTP downloads. * * @see #getSystemService */ @@ -1555,6 +1555,16 @@ public abstract class Context { public static final String SIP_SERVICE = "sip"; /** + * Use with {@link #getSystemService} to retrieve an + * {@link com.trustedlogic.trustednfc.android.INfcManager.INfcManager} for + * accessing NFC methods. + * + * @see #getSystemService + * @hide + */ + public static final String NFC_SERVICE = "nfc"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/res/ObbInfo.java b/core/java/android/content/res/ObbInfo.java index 838c5ff..7b962e5 100644 --- a/core/java/android/content/res/ObbInfo.java +++ b/core/java/android/content/res/ObbInfo.java @@ -20,9 +20,9 @@ import android.os.Parcel; import android.os.Parcelable; /** - * Basic information about a Opaque Binary Blob (OBB) that reflects - * the info from the footer on the OBB file. - * @hide + * Basic information about a Opaque Binary Blob (OBB) that reflects the info + * from the footer on the OBB file. This information may be manipulated by a + * developer with the <code>obbtool</code> program in the Android SDK. */ public class ObbInfo implements Parcelable { /** Flag noting that this OBB is an overlay patch for a base OBB. */ @@ -43,7 +43,8 @@ public class ObbInfo implements Parcelable { */ public int flags; - public ObbInfo() { + // Only allow things in this package to instantiate. + /* package */ ObbInfo() { } public String toString() { diff --git a/core/java/android/content/res/ObbScanner.java b/core/java/android/content/res/ObbScanner.java index eb383c3..572b75e 100644 --- a/core/java/android/content/res/ObbScanner.java +++ b/core/java/android/content/res/ObbScanner.java @@ -16,25 +16,43 @@ package android.content.res; +import java.io.File; +import java.io.IOException; + /** - * Class to scan Opaque Binary Blob (OBB) files. - * @hide + * Class to scan Opaque Binary Blob (OBB) files. Use this to get information + * about an OBB file for use in a program via {@link ObbInfo}. */ public class ObbScanner { // Don't allow others to instantiate this class private ObbScanner() {} - public static ObbInfo getObbInfo(String filePath) { + /** + * Scan a file for OBB information. + * + * @param filePath path to the OBB file to be scanned. + * @return ObbInfo object information corresponding to the file path + * @throws IllegalArgumentException if the OBB file couldn't be found + * @throws IOException if the OBB file couldn't be read + */ + public static ObbInfo getObbInfo(String filePath) throws IOException { if (filePath == null) { - return null; + throw new IllegalArgumentException("file path cannot be null"); } - ObbInfo obbInfo = new ObbInfo(); - if (!getObbInfo_native(filePath, obbInfo)) { - throw new IllegalArgumentException("Could not read OBB file: " + filePath); + final File obbFile = new File(filePath); + if (!obbFile.exists()) { + throw new IllegalArgumentException("OBB file does not exist: " + filePath); } + + final String canonicalFilePath = obbFile.getCanonicalPath(); + + ObbInfo obbInfo = new ObbInfo(); + getObbInfo_native(canonicalFilePath, obbInfo); + return obbInfo; } - private native static boolean getObbInfo_native(String filePath, ObbInfo obbInfo); + private native static void getObbInfo_native(String filePath, ObbInfo obbInfo) + throws IOException; } diff --git a/core/java/android/os/storage/OnObbStateChangeListener.java b/core/java/android/os/storage/OnObbStateChangeListener.java new file mode 100644 index 0000000..a2d0a56 --- /dev/null +++ b/core/java/android/os/storage/OnObbStateChangeListener.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.os.storage; + +/** + * Used for receiving notifications from {@link StorageManager}. + */ +public abstract class OnObbStateChangeListener { + /** + * Called when an OBB has changed states. + * + * @param path path to the OBB file the state change has happened on + * @param state the current state of the OBB + */ + public void onObbStateChange(String path, String state) { + } +} diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index df0b69c..14da00a 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -23,13 +23,28 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; /** - * StorageManager is the interface to the systems storage service. + * StorageManager is the interface to the systems storage service. The storage + * manager handles storage-related items such as Opaque Binary Blobs (OBBs). + * <p> + * OBBs contain a filesystem that maybe be encrypted on disk and mounted + * on-demand from an application. OBBs are a good way of providing large amounts + * of binary assets without packaging them into APKs as they may be multiple + * gigabytes in size. However, due to their size, they're most likely stored in + * a shared storage pool accessible from all programs. The system does not + * guarantee the security of the OBB file itself: if any program modifies the + * OBB, there is no guarantee that a read from that OBB will produce the + * expected output. + * <p> * Get an instance of this class by calling - * {@link android.content.Context#getSystemService(java.lang.String)} with an argument - * of {@link android.content.Context#STORAGE_SERVICE}. + * {@link android.content.Context#getSystemService(java.lang.String)} with an + * argument of {@link android.content.Context#STORAGE_SERVICE}. */ public class StorageManager @@ -75,11 +90,113 @@ public class StorageManager /** * Binder listener for OBB action results. */ - private final ObbActionBinderListener mObbActionListener = new ObbActionBinderListener(); - private class ObbActionBinderListener extends IObbActionListener.Stub { + private final ObbActionListener mObbActionListener = new ObbActionListener(); + + private class ObbActionListener extends IObbActionListener.Stub { + private List<WeakReference<ObbListenerDelegate>> mListeners = new LinkedList<WeakReference<ObbListenerDelegate>>(); + @Override public void onObbResult(String filename, String status) throws RemoteException { - Log.i(TAG, "filename = " + filename + ", result = " + status); + synchronized (mListeners) { + final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator(); + while (iter.hasNext()) { + final WeakReference<ObbListenerDelegate> ref = iter.next(); + + final ObbListenerDelegate delegate = (ref == null) ? null : ref.get(); + if (delegate == null) { + iter.remove(); + continue; + } + + delegate.sendObbStateChanged(filename, status); + } + } + } + + public void addListener(OnObbStateChangeListener listener) { + if (listener == null) { + return; + } + + synchronized (mListeners) { + final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator(); + while (iter.hasNext()) { + final WeakReference<ObbListenerDelegate> ref = iter.next(); + + final ObbListenerDelegate delegate = (ref == null) ? null : ref.get(); + if (delegate == null) { + iter.remove(); + continue; + } + + /* + * If we're already in the listeners, we don't need to be in + * there again. + */ + if (listener.equals(delegate.getListener())) { + return; + } + } + + final ObbListenerDelegate delegate = new ObbListenerDelegate(listener); + mListeners.add(new WeakReference<ObbListenerDelegate>(delegate)); + } + } + } + + /** + * Private class containing sender and receiver code for StorageEvents. + */ + private class ObbListenerDelegate { + private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef; + private final Handler mHandler; + + ObbListenerDelegate(OnObbStateChangeListener listener) { + mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener); + mHandler = new Handler(mTgtLooper) { + @Override + public void handleMessage(Message msg) { + final OnObbStateChangeListener listener = getListener(); + if (listener == null) { + return; + } + + StorageEvent e = (StorageEvent) msg.obj; + + if (msg.what == StorageEvent.EVENT_OBB_STATE_CHANGED) { + ObbStateChangedStorageEvent ev = (ObbStateChangedStorageEvent) e; + listener.onObbStateChange(ev.path, ev.state); + } else { + Log.e(TAG, "Unsupported event " + msg.what); + } + } + }; + } + + OnObbStateChangeListener getListener() { + if (mObbEventListenerRef == null) { + return null; + } + return mObbEventListenerRef.get(); + } + + void sendObbStateChanged(String path, String state) { + ObbStateChangedStorageEvent e = new ObbStateChangedStorageEvent(path, state); + mHandler.sendMessage(e.getMessage()); + } + } + + /** + * Message sent during an OBB status change event. + */ + private class ObbStateChangedStorageEvent extends StorageEvent { + public final String path; + public final String state; + + public ObbStateChangedStorageEvent(String path, String state) { + super(EVENT_OBB_STATE_CHANGED); + this.path = path; + this.state = state; } } @@ -88,8 +205,9 @@ public class StorageManager * and the target looper handler. */ private class StorageEvent { - public static final int EVENT_UMS_CONNECTION_CHANGED = 1; - public static final int EVENT_STORAGE_STATE_CHANGED = 2; + static final int EVENT_UMS_CONNECTION_CHANGED = 1; + static final int EVENT_STORAGE_STATE_CHANGED = 2; + static final int EVENT_OBB_STATE_CHANGED = 3; private Message mMessage; @@ -300,19 +418,27 @@ public class StorageManager * specified, it is supplied to the mounting process to be used in any * encryption used in the OBB. * <p> + * The OBB will remain mounted for as long as the StorageManager reference + * is held by the application. As soon as this reference is lost, the OBBs + * in use will be unmounted. The {@link OnObbStateChangeListener} registered with + * this call will receive all further OBB-related events until it goes out + * of scope. If the caller is not interested in whether the call succeeds, + * the <code>listener</code> may be specified as <code>null</code>. + * <p> * <em>Note:</em> you can only mount OBB files for which the OBB tag on the * file matches a package ID that is owned by the calling program's UID. - * That is, shared UID applications can obtain access to any other + * That is, shared UID applications can attempt to mount any other * application's OBB that shares its UID. - * <p> - * STOPSHIP document more; discuss lack of guarantees of security * * @param filename the path to the OBB file - * @param key decryption key + * @param key secret used to encrypt the OBB; may be <code>null</code> if no + * encryption was used on the OBB. * @return whether the mount call was successfully queued or not + * @throws IllegalArgumentException when the OBB is already mounted */ - public boolean mountObb(String filename, String key) { + public boolean mountObb(String filename, String key, OnObbStateChangeListener listener) { try { + mObbActionListener.addListener(listener); mMountService.mountObb(filename, key, mObbActionListener); return true; } catch (RemoteException e) { @@ -323,15 +449,20 @@ public class StorageManager } /** - * Unmount an Opaque Binary Blob (OBB) file. If the <code>force</code> flag - * is true, it will kill any application needed to unmount the given OBB. + * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the + * <code>force</code> flag is true, it will kill any application needed to + * unmount the given OBB (even the calling application). + * <p> + * The {@link OnObbStateChangeListener} registered with this call will receive all + * further OBB-related events until it goes out of scope. If the caller is + * not interested in whether the call succeeded, the listener may be + * specified as <code>null</code>. * <p> * <em>Note:</em> you can only mount OBB files for which the OBB tag on the * file matches a package ID that is owned by the calling program's UID. * That is, shared UID applications can obtain access to any other * application's OBB that shares its UID. * <p> - * STOPSHIP document more; discuss lack of guarantees of security * * @param filename path to the OBB file * @param force whether to kill any programs using this in order to unmount @@ -339,8 +470,10 @@ public class StorageManager * @return whether the unmount call was successfully queued or not * @throws IllegalArgumentException when OBB is not already mounted */ - public boolean unmountObb(String filename, boolean force) throws IllegalArgumentException { + public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener) + throws IllegalArgumentException { try { + mObbActionListener.addListener(listener); mMountService.unmountObb(filename, force, mObbActionListener); return true; } catch (RemoteException e) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1dd2420..90cd840 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1640,6 +1640,86 @@ public final class Settings { public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse"; /** + * Whether nfc is enabled/disabled + * 0=disabled. 1=enabled. + * @hide + */ + public static final String NFC_ON = "nfc_on"; + + /** + * Whether nfc secure element is enabled/disabled + * 0=disabled. 1=enabled. + * @hide + */ + public static final String NFC_SECURE_ELEMENT_ON = "nfc_secure_element_on"; + + /** + * Whether nfc secure element is enabled/disabled + * 0=disabled. 1=enabled. + * @hide + */ + public static final String NFC_SECURE_ELEMENT_ID = "nfc_secure_element_id"; + + /** + * LLCP LTO value + * @hide + */ + public static final String NFC_LLCP_LTO = "nfc_llcp_lto"; + + /** + * LLCP MIU value + * @hide + */ + public static final String NFC_LLCP_MIU = "nfc_llcp_miu"; + + /** + * LLCP WKS value + * @hide + */ + public static final String NFC_LLCP_WKS = "nfc_llcp_wks"; + + /** + * LLCP OPT value + * @hide + */ + public static final String NFC_LLCP_OPT = "nfc_llcp_opt"; + + /** + * NFC Discovery Reader A + * 0=disabled. 1=enabled. + * @hide + */ + public static final String NFC_DISCOVERY_A = "nfc_discovery_a"; + + /** + * NFC Discovery Reader B + * 0=disabled. 1=enabled. + * @hide + */ + public static final String NFC_DISCOVERY_B = "nfc_discovery_b"; + + /** + * NFC Discovery Reader Felica + * 0=disabled. 1=enabled. + * @hide + */ + public static final String NFC_DISCOVERY_F = "nfc_discovery_felica"; + + /** + * NFC Discovery Reader 15693 + * 0=disabled. 1=enabled. + * @hide + */ + public static final String NFC_DISCOVERY_15693 = "nfc_discovery_15693"; + + /** + * NFC Discovery NFCIP + * 0=disabled. 1=enabled. + * @hide + */ + public static final String NFC_DISCOVERY_NFCIP = "nfc_discovery_nfcip"; + + /** * Show pointer location on screen? * 0 = no * 1 = yes @@ -1804,7 +1884,19 @@ public final class Settings { SHOW_WEB_SUGGESTIONS, NOTIFICATION_LIGHT_PULSE, SIP_CALL_OPTIONS, - SIP_RECEIVE_CALLS + SIP_RECEIVE_CALLS, + NFC_ON, + NFC_SECURE_ELEMENT_ON, + NFC_SECURE_ELEMENT_ID, + NFC_LLCP_LTO, + NFC_LLCP_MIU, + NFC_LLCP_WKS, + NFC_LLCP_OPT, + NFC_DISCOVERY_A, + NFC_DISCOVERY_B, + NFC_DISCOVERY_F, + NFC_DISCOVERY_15693, + NFC_DISCOVERY_NFCIP, }; // Settings moved to Settings.Secure diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 7098bf3..24b1158 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -5016,7 +5016,7 @@ public class WebView extends AbsoluteLayout @Override public boolean onTouchEvent(MotionEvent ev) { - if (mNativeClass == 0 || !isClickable() || !isLongClickable()) { + if (mNativeClass == 0 || (!isClickable() && !isLongClickable())) { return false; } diff --git a/core/java/com/trustedlogic/trustednfc/android/ILlcpConnectionlessSocket.aidl b/core/java/com/trustedlogic/trustednfc/android/ILlcpConnectionlessSocket.aidl new file mode 100644 index 0000000..35746ad --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/ILlcpConnectionlessSocket.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.trustedlogic.trustednfc.android; + +import com.trustedlogic.trustednfc.android.LlcpPacket; + +/** + * TODO + * + * {@hide} + */ +interface ILlcpConnectionlessSocket +{ + + void close(int nativeHandle); + int getSap(int nativeHandle); + LlcpPacket receiveFrom(int nativeHandle); + int sendTo(int nativeHandle, in LlcpPacket packet); + +}
\ No newline at end of file diff --git a/core/java/com/trustedlogic/trustednfc/android/ILlcpServiceSocket.aidl b/core/java/com/trustedlogic/trustednfc/android/ILlcpServiceSocket.aidl new file mode 100644 index 0000000..5eb1f3c --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/ILlcpServiceSocket.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trustedlogic.trustednfc.android; + +/** + * TODO + * + * {@hide} + */ +interface ILlcpServiceSocket +{ + + int accept(int nativeHandle); + void close(int nativeHandle); + int getAcceptTimeout(int nativeHandle); + void setAcceptTimeout(int nativeHandle, int timeout); + +}
\ No newline at end of file diff --git a/core/java/com/trustedlogic/trustednfc/android/ILlcpSocket.aidl b/core/java/com/trustedlogic/trustednfc/android/ILlcpSocket.aidl new file mode 100644 index 0000000..e9169d8 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/ILlcpSocket.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trustedlogic.trustednfc.android; + +/** + * TODO + * + * {@hide} + */ +interface ILlcpSocket +{ + + int close(int nativeHandle); + int connect(int nativeHandle, int sap); + int connectByName(int nativeHandle, String sn); + int getConnectTimeout(int nativeHandle); + int getLocalSap(int nativeHandle); + int getLocalSocketMiu(int nativeHandle); + int getLocalSocketRw(int nativeHandle); + int getRemoteSocketMiu(int nativeHandle); + int getRemoteSocketRw(int nativeHandle); + int receive(int nativeHandle, out byte[] receiveBuffer); + int send(int nativeHandle, in byte[] data); + void setConnectTimeout(int nativeHandle, int timeout); + +} + diff --git a/core/java/com/trustedlogic/trustednfc/android/INdefTag.aidl b/core/java/com/trustedlogic/trustednfc/android/INdefTag.aidl new file mode 100644 index 0000000..1f8d1a4 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/INdefTag.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trustedlogic.trustednfc.android; + +import com.trustedlogic.trustednfc.android.NdefMessage; + +/** + * TODO + * + * {@hide} + */ +interface INdefTag +{ + + NdefMessage read(int nativeHandle); + boolean write(int nativeHandle, in NdefMessage msg); + +}
\ No newline at end of file diff --git a/core/java/com/trustedlogic/trustednfc/android/INfcManager.aidl b/core/java/com/trustedlogic/trustednfc/android/INfcManager.aidl new file mode 100644 index 0000000..ce36ab2 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/INfcManager.aidl @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trustedlogic.trustednfc.android; + +import com.trustedlogic.trustednfc.android.ILlcpSocket; +import com.trustedlogic.trustednfc.android.ILlcpServiceSocket; +import com.trustedlogic.trustednfc.android.ILlcpConnectionlessSocket; +import com.trustedlogic.trustednfc.android.INfcTag; +import com.trustedlogic.trustednfc.android.IP2pTarget; +import com.trustedlogic.trustednfc.android.IP2pInitiator; + + +/** + * Interface that allows controlling NFC activity. + * + * {@hide} + */ +interface INfcManager +{ + + ILlcpSocket getLlcpInterface(); + ILlcpConnectionlessSocket getLlcpConnectionlessInterface(); + ILlcpServiceSocket getLlcpServiceInterface(); + INfcTag getNfcTagInterface(); + IP2pTarget getP2pTargetInterface(); + IP2pInitiator getP2pInitiatorInterface(); + + void cancel(); + int createLlcpConnectionlessSocket(int sap); + int createLlcpServiceSocket(int sap, String sn, int miu, int rw, int linearBufferLength); + int createLlcpSocket(int sap, int miu, int rw, int linearBufferLength); + int deselectSecureElement(); + boolean disable(); + boolean enable(); + int getOpenTimeout(); + String getProperties(String param); + int[] getSecureElementList(); + int getSelectedSecureElement(); + boolean isEnabled(); + int openP2pConnection(); + int openTagConnection(); + int selectSecureElement(int seId); + void setOpenTimeout(int timeout); + int setProperties(String param, String value); + +} + diff --git a/core/java/com/trustedlogic/trustednfc/android/INfcTag.aidl b/core/java/com/trustedlogic/trustednfc/android/INfcTag.aidl new file mode 100644 index 0000000..79543c4 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/INfcTag.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.trustedlogic.trustednfc.android; + +import com.trustedlogic.trustednfc.android.NdefMessage; + +/** + * TODO + * + * {@hide} + */ +interface INfcTag +{ + + int close(int nativeHandle); + int connect(int nativeHandle); + String getType(int nativeHandle); + byte[] getUid(int nativeHandle); + boolean isNdef(int nativeHandle); + byte[] transceive(int nativeHandle, in byte[] data); + + NdefMessage read(int nativeHandle); + boolean write(int nativeHandle, in NdefMessage msg); +}
\ No newline at end of file diff --git a/core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl b/core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl new file mode 100644 index 0000000..96819ae --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trustedlogic.trustednfc.android; + +/** + * TODO + * + * {@hide} + */ +interface IP2pInitiator +{ + + byte[] getGeneralBytes(int nativeHandle); + int getMode(int nativeHandle); + byte[] receive(int nativeHandle); + boolean send(int nativeHandle, in byte[] data); + +}
\ No newline at end of file diff --git a/core/java/com/trustedlogic/trustednfc/android/IP2pTarget.aidl b/core/java/com/trustedlogic/trustednfc/android/IP2pTarget.aidl new file mode 100644 index 0000000..8dcdf18 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/IP2pTarget.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trustedlogic.trustednfc.android; + +/** + * TODO + * + * {@hide} + */ +interface IP2pTarget +{ + + byte[] getGeneralBytes(int nativeHandle); + int getMode(int nativeHandle); + int connect(int nativeHandle); + boolean disconnect(int nativeHandle); + byte[] transceive(int nativeHandle, in byte[] data); + +}
\ No newline at end of file diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpConnectionlessSocket.java b/core/java/com/trustedlogic/trustednfc/android/LlcpConnectionlessSocket.java new file mode 100644 index 0000000..0270626 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/LlcpConnectionlessSocket.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : LlcpConnectionLessSocket.java + * Original-Author : Trusted Logic S.A. (Daniel Tomas) + * Created : 18-02-2010 + */ + +package com.trustedlogic.trustednfc.android; + +import java.io.IOException; + +import com.trustedlogic.trustednfc.android.internal.ErrorCodes; + +import android.os.RemoteException; +import android.util.Log; + +/** + * LlcpConnectionlessSocket represents a LLCP Connectionless object to be used + * in a connectionless communication + * + * @since AA02.01 + * @hide + */ +public class LlcpConnectionlessSocket { + + + private static final String TAG = "LlcpConnectionlessSocket"; + + /** + * The handle returned by the NFC service and used to identify the LLCP connectionless socket in + * every call of this class. + * + * @hide + */ + protected int mHandle; + + + /** + * The entry point for LLCP Connectionless socket operations. + * + * @hide + */ + protected ILlcpConnectionlessSocket mService; + + + /** + * Internal constructor for the LlcpConnectionlessSocket class. + * + * @param service The entry point to the Nfc Service for LLCP Connectionless socket class. + * @param handle The handle returned by the NFC service and used to identify + * the socket in subsequent calls. + * @hide + */ + LlcpConnectionlessSocket(ILlcpConnectionlessSocket service, int handle) { + this.mService = service; + this.mHandle = handle; + } + + /** + * Send data to a specific LLCP Connectionless client + * + * @param packet Service Access Point number related to a LLCP + * Connectionless client and a data buffer to send + * @throws IOException if the LLCP link has been lost or deactivated. + * @since AA02.01 + */ + public void sendTo(LlcpPacket packet) throws IOException { + try { + int result = mService.sendTo(mHandle, packet); + // Handle potential errors + if (ErrorCodes.isError(result)) { + throw new IOException(); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in sendTo(): ", e); + } + } + + /** + * Receive data from a LLCP Connectionless client + * + * @return data data received from a specific LLCP Connectionless client + * @throws IOException if the LLCP link has been lost or deactivated. + * @see LlcpPacket + * @since AA02.01 + */ + public LlcpPacket receiveFrom() throws IOException { + try { + LlcpPacket packet = mService.receiveFrom(mHandle); + if (packet != null) { + return packet; + }else{ + // Handle potential errors + throw new IOException(); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in receiveFrom(): ", e); + } + return null; + } + + /** + * Close the created Connectionless socket. + * + * @since AA02.01 + */ + public void close() { + try { + mService.close(mHandle); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in close(): ", e); + } + } + + /** + * Returns the local Service Access Point number of the socket + * + * @return sap + * @since AA02.01 + */ + public int getSap() { + int sap = 0; + + try { + sap = mService.getSap(mHandle); + + } catch (RemoteException e) { + + e.printStackTrace(); + } + return sap; + } +} diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpException.java b/core/java/com/trustedlogic/trustednfc/android/LlcpException.java new file mode 100644 index 0000000..1e2e2da --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/LlcpException.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : LLCPException.java + * Original-Author : Trusted Logic S.A. (Daniel Tomas) + * Created : 24-02-2010 + */ + +package com.trustedlogic.trustednfc.android; + +/** + * Generic exception thrown in case something unexpected happened during a + * LLCP communication. + * + * @since AA02.01 + * @hide + */ +public class LlcpException extends Exception { + /** + * Constructs a new LlcpException with the current stack trace and the + * specified detail message. + * + * @param s the detail message for this exception. + */ + public LlcpException(String s) { + super(s); + } +} diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.aidl b/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.aidl new file mode 100644 index 0000000..297a1fe --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.trustedlogic.trustednfc.android; + +parcelable LlcpPacket; diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.java b/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.java new file mode 100644 index 0000000..af79023 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : LLCPPacket.java + * Original-Author : Trusted Logic S.A. (Daniel Tomas) + * Created : 25-02-2010 + */ + +package com.trustedlogic.trustednfc.android; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Represents a LLCP packet received in a LLCP Connectionless communication; + * + * @since AA02.01 + * @hide + */ +public class LlcpPacket implements Parcelable { + + private int mRemoteSap; + + private byte[] mDataBuffer; + + /** + * Creator class, needed when implementing from Parcelable + * {@hide} + */ + public static final Parcelable.Creator<LlcpPacket> CREATOR = new Parcelable.Creator<LlcpPacket>() { + public LlcpPacket createFromParcel(Parcel in) { + // Remote SAP + short sap = (short)in.readInt(); + + // Data Buffer + int dataLength = in.readInt(); + byte[] data = new byte[dataLength]; + in.readByteArray(data); + + return new LlcpPacket(sap, data); + } + + public LlcpPacket[] newArray(int size) { + return new LlcpPacket[size]; + } + }; + + + /** + * Creates a LlcpPacket to be sent to a remote Service Access Point number + * (SAP) + * + * @param sap Remote Service Access Point number + * @param data Data buffer + * @since AA02.01 + */ + public LlcpPacket(int sap, byte[] data) { + mRemoteSap = sap; + mDataBuffer = data; + } + + /** + * @hide + */ + public LlcpPacket() { + } + + /** + * Returns the remote Service Access Point number + * + * @return remoteSap + * @since AA02.01 + */ + public int getRemoteSap() { + return mRemoteSap; + } + + /** + * Returns the data buffer + * + * @return data + * @since AA02.01 + */ + public byte[] getDataBuffer() { + return mDataBuffer; + } + + /** + * (Parcelable) Describe the parcel + * {@hide} + */ + public int describeContents() { + return 0; + } + + /** + * (Parcelable) Convert current object to a Parcel + * {@hide} + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mRemoteSap); + dest.writeInt(mDataBuffer.length); + dest.writeByteArray(mDataBuffer); + } +} diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpServiceSocket.java b/core/java/com/trustedlogic/trustednfc/android/LlcpServiceSocket.java new file mode 100644 index 0000000..a152ecb1 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/LlcpServiceSocket.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : LLCPServerSocket.java + * Original-Author : Trusted Logic S.A. (Daniel Tomas) + * Created : 18-02-2010 + */ + +package com.trustedlogic.trustednfc.android; + +import java.io.IOException; + +import com.trustedlogic.trustednfc.android.internal.ErrorCodes; + +import android.os.RemoteException; +import android.util.Log; + +/** + * LlcpServiceSocket represents a LLCP Service to be used in a + * Connection-oriented communication + * + * @since AA02.01 + * @hide + */ +public class LlcpServiceSocket { + + private static final String TAG = "LlcpServiceSocket"; + + /** + * The handle returned by the NFC service and used to identify the LLCP + * Service socket in every call of this class. + * + * @hide + */ + protected int mHandle; + + /** + * The entry point for LLCP Service socket operations. + * + * @hide + */ + protected ILlcpServiceSocket mService; + + private ILlcpSocket mLlcpSocketService; + + static LlcpException convertErrorToLlcpException(int errorCode) { + return convertErrorToLlcpException(errorCode, null); + } + + static LlcpException convertErrorToLlcpException(int errorCode, + String message) { + if (message == null) { + message = ""; + } else { + message = " (" + message + ")"; + } + + switch (errorCode) { + case ErrorCodes.ERROR_SOCKET_CREATION: + return new LlcpException( + "Error during the creation of an Llcp socket" + message); + case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES: + return new LlcpException("Not enough ressources are available" + + message); + default: + return new LlcpException("Unkown error code " + errorCode + message); + } + } + + /** + * Internal constructor for the LlcpServiceSocket class. + * + * @param service + * The entry point to the Nfc Service for LlcpServiceSocket + * class. + * @param handle + * The handle returned by the NFC service and used to identify + * the socket in subsequent calls. + * @hide + */ + LlcpServiceSocket(ILlcpServiceSocket service, ILlcpSocket socketService, int handle) { + this.mService = service; + this.mHandle = handle; + this.mLlcpSocketService = socketService; + } + + /** + * Wait for incomming connection request from a LLCP client and accept this + * request + * + * @return socket object to be used to communicate with a LLCP client + * + * @throws IOException + * if the llcp link is lost or deactivated + * @throws LlcpException + * if not enough ressources are available + * + * @see LlcpSocket + * @since AA02.01 + */ + public LlcpSocket accept() throws IOException, LlcpException { + + try { + int handle = mService.accept(mHandle); + // Handle potential errors + if (ErrorCodes.isError(handle)) { + if (handle == ErrorCodes.ERROR_IO) { + throw new IOException(); + } else { + throw convertErrorToLlcpException(handle); + } + } + + // Build the public LlcpSocket object + return new LlcpSocket(mLlcpSocketService, handle); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in accept(): ", e); + return null; + } + + } + + /** + * Set the timeout for the accept request + * + * @param timeout + * value of the timeout for the accept request + * @since AA02.01 + */ + public void setAcceptTimeout(int timeout) { + try { + mService.setAcceptTimeout(mHandle, timeout); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in setAcceptTimeout(): ", e); + } + } + + /** + * Get the timeout value of the accept request + * + * @return mTimeout + * @since AA02.01 + */ + public int getAcceptTimeout() { + try { + return mService.getAcceptTimeout(mHandle); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getAcceptTimeout(): ", e); + return 0; + } + } + + /** + * Close the created Llcp Service socket + * + * @since AA02.01 + */ + public void close() { + try { + mService.close(mHandle); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in close(): ", e); + } + } + +} diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpSocket.java b/core/java/com/trustedlogic/trustednfc/android/LlcpSocket.java new file mode 100644 index 0000000..e47160c --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/LlcpSocket.java @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : LlcpClientSocket.java + * Original-Author : Trusted Logic S.A. (Daniel Tomas) + * Created : 18-02-2010 + */ + +package com.trustedlogic.trustednfc.android; + +import java.io.IOException; + +import com.trustedlogic.trustednfc.android.internal.ErrorCodes; + +import android.os.RemoteException; +import android.util.Log; + +/** + * LlcpClientSocket represents a LLCP Connection-Oriented client to be used in a + * connection-oriented communication + * + * @since AA02.01 + * @hide + */ +public class LlcpSocket { + + private static final String TAG = "LlcpSocket"; + + /** + * The handle returned by the NFC service and used to identify the LLCP + * socket in every call of this class. + * + * @hide + */ + protected int mHandle; + + /** + * The entry point for LLCP socket operations. + * + * @hide + */ + protected ILlcpSocket mService; + + static LlcpException convertErrorToLlcpException(int errorCode) { + return convertErrorToLlcpException(errorCode, null); + } + + static LlcpException convertErrorToLlcpException(int errorCode, + String message) { + if (message == null) { + message = ""; + } else { + message = " (" + message + ")"; + } + + switch (errorCode) { + case ErrorCodes.ERROR_SOCKET_CREATION: + return new LlcpException( + "Error during the creation of an Llcp socket" + message); + case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES: + return new LlcpException("Not enough ressources are available" + + message); + case ErrorCodes.ERROR_SOCKET_NOT_CONNECTED: + return new LlcpException("Socket not connected to an Llcp Service" + + message); + default: + return new LlcpException("Unkown error code " + errorCode + message); + } + } + + /** + * Internal constructor for the LlcpSocket class. + * + * @param service + * The entry point to the Nfc Service for LlcpServiceSocket + * class. + * @param handle + * The handle returned by the NFC service and used to identify + * the socket in subsequent calls. + * @hide + */ + LlcpSocket(ILlcpSocket service, int handle) { + this.mService = service; + this.mHandle = handle; + } + + /** + * Connect request to a specific LLCP Service by its SAP. + * + * @param sap + * Service Access Point number of the LLCP Service + * @throws IOException + * if the LLCP has been lost or deactivated. + * @throws LlcpException + * if the connection request is rejected by the remote LLCP + * Service + * @since AA02.01 + */ + public void connect(int sap) throws IOException, LlcpException { + try { + int result = mService.connect(mHandle, sap); + // Handle potential errors + if (ErrorCodes.isError(result)) { + if (result == ErrorCodes.ERROR_IO) { + throw new IOException(); + } else { + throw convertErrorToLlcpException(result); + } + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in accept(): ", e); + } + } + + /** + * Connect request to a specific LLCP Service by its Service Name. + * + * @param sn + * Service Name of the LLCP Service + * @throws IOException + * if the LLCP has been lost or deactivated. + * @throws LlcpException + * if the connection request is rejected by the remote LLCP + * Service + * @since AA02.01 + */ + public void connect(String sn) throws IOException, LlcpException { + try { + int result = mService.connectByName(mHandle, sn); + // Handle potential errors + if (ErrorCodes.isError(result)) { + if (result == ErrorCodes.ERROR_IO) { + throw new IOException(); + } else { + throw convertErrorToLlcpException(result); + } + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in accept(): ", e); + } + } + + /** + * Set the timeout for the connect request + * + * @param timeout + * timeout value for the connect request + * @since AA02.01 + */ + public void setConnectTimeout(int timeout) { + try { + mService.setConnectTimeout(mHandle, timeout); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in setConnectTimeout(): ", e); + } + } + + /** + * Get the timeout value of the connect request + * + * @return mTimeout + * @since AA02.01 + */ + public int getConnectTimeout() { + try { + return mService.getConnectTimeout(mHandle); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getConnectTimeout(): ", e); + return 0; + } + } + + /** + * Disconnect request to the connected LLCP socket and close the created + * socket. + * + * @throws IOException + * if the LLCP has been lost or deactivated. + * @since AA02.01 + */ + public void close() throws IOException { + try { + int result = mService.close(mHandle); + // Handle potential errors + if (ErrorCodes.isError(result)) { + throw new IOException(); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in close(): ", e); + } + } + + /** + * Send data to the connected LLCP Socket. + * + * @throws IOException + * if the LLCP has been lost or deactivated. + * @since AA02.01 + */ + public void send(byte[] data) throws IOException { + try { + int result = mService.send(mHandle, data); + // Handle potential errors + if (ErrorCodes.isError(result)) { + throw new IOException(); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in send(): ", e); + } + } + + /** + * Receive data from the connected LLCP socket + * + * @param receiveBuffer + * a buffer for the received data + * @return length length of the data received + * @throws IOException + * if the LLCP has been lost or deactivated. + * @since AA02.01 + */ + public int receive(byte[] receiveBuffer) throws IOException { + int receivedLength = 0; + try { + receivedLength = mService.receive(mHandle, receiveBuffer); + if(receivedLength == 0){ + throw new IOException(); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in send(): ", e); + } + + return receivedLength; + } + + /** + * Returns the local Service Access Point number of the socket + * + * @return localSap + * @since AA02.01 + */ + public int getLocalSap() { + try { + return mService.getLocalSap(mHandle); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getLocalSap(): ", e); + return 0; + } + } + + /** + * Returns the local Maximum Information Unit(MIU) of the socket + * + * @return miu + * @since AA02.01 + */ + public int getLocalSocketMiu() { + try { + return mService.getLocalSocketMiu(mHandle); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getLocalSocketMiu(): ", e); + return 0; + } + } + + /** + * Returns the local Receive Window(RW) of the socket + * + * @return rw + * @since AA02.01 + */ + public int getLocalSocketRw() { + try { + return mService.getLocalSocketRw(mHandle); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getLocalSocketRw(): ", e); + return 0; + } + } + + /** + * Returns the remote Maximum Information Unit(MIU) of the socket. + * <p> + * This method must be called when the socket is in CONNECTED_STATE + * + * @return remoteMiu + * @throws LlcpException + * if the LlcpClientSocket is not in a CONNECTED_STATE + * @since AA02.01 + */ + public int getRemoteSocketMiu() throws LlcpException { + try { + int result = mService.getRemoteSocketMiu(mHandle); + if(result != ErrorCodes.ERROR_SOCKET_NOT_CONNECTED){ + return result; + }else{ + throw convertErrorToLlcpException(result); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getRemoteSocketMiu(): ", e); + return 0; + } + } + + /** + * Returns the remote Receive Window(RW) of the connected remote socket. + * <p> + * This method must be called when the socket is in CONNECTED_STATE + * + * @return rw + * @throws LlcpException + * if the LlcpClientSocket is not in a CONNECTED_STATE + * @since AA02.01 + */ + public int getRemoteSocketRw() throws LlcpException { + try { + int result = mService.getRemoteSocketRw(mHandle); + if( result != ErrorCodes.ERROR_SOCKET_NOT_CONNECTED){ + return result; + }else{ + throw convertErrorToLlcpException(result); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getRemoteSocketRw(): ", e); + return 0; + } + } + + +} diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl b/core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl new file mode 100644 index 0000000..e60f4e8 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.trustedlogic.trustednfc.android; + +parcelable NdefMessage; diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefMessage.java b/core/java/com/trustedlogic/trustednfc/android/NdefMessage.java new file mode 100644 index 0000000..f03b604 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/NdefMessage.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : NDEFMessage.java + * Original-Author : Trusted Logic S.A. (Jeremie Corbier) + * Created : 05-10-2009 + */ + +package com.trustedlogic.trustednfc.android; + +import java.util.LinkedList; +import java.util.ListIterator; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Represents an NDEF message as specified by the <a + * href="http://www.nfc-forum.org/">NFC Forum</a>. + * + * @see NdefRecord + * + * @since AA01.04 + * @hide + */ +public class NdefMessage implements Parcelable { + /* Flag values */ + private static final int FLAG_MB = 0x80; + private static final int FLAG_ME = 0x40; + private static final int FLAG_CF = 0x20; + private static final int FLAG_SR = 0x10; + private static final int FLAG_IL = 0x08; + + /** + * Array of {@link NdefRecord} composing this message. + */ + private NdefRecord[] mRecords; + + /** + * Builds an NDEF message. + * + * @param data raw NDEF message data + * + * @throws NFCException + */ + public NdefMessage(byte[] data) throws NfcException { + if (parseNdefMessage(data) == -1) + throw new NfcException("Error while parsing NDEF message"); + } + + /** + * Builds an NDEF message. + * + * @param records + * an array of already created NDEF records + */ + public NdefMessage(NdefRecord[] records) { + mRecords = new NdefRecord[records.length]; + + System.arraycopy(records, 0, mRecords, 0, records.length); + } + + /** + * Returns the NDEF message as a byte array. + * + * @return the message as a byte array + */ + public byte[] toByteArray() { + if ((mRecords == null) || (mRecords.length == 0)) + return null; + + byte[] msg = {}; + + for (int i = 0; i < mRecords.length; i++) { + byte[] record = mRecords[i].toByteArray(); + byte[] tmp = new byte[msg.length + record.length]; + + /* Make sure the Message Begin flag is set only for the first record */ + if (i == 0) + record[0] |= FLAG_MB; + else + record[0] &= ~FLAG_MB; + + /* Make sure the Message End flag is set only for the last record */ + if (i == (mRecords.length - 1)) + record[0] |= FLAG_ME; + else + record[0] &= ~FLAG_ME; + + System.arraycopy(msg, 0, tmp, 0, msg.length); + System.arraycopy(record, 0, tmp, msg.length, record.length); + + msg = tmp; + } + + return msg; + } + + /** + * Returns an array of {@link NdefRecord} composing this message. + * + * @return mRecords + * + * @since AA02.01 + */ + public NdefRecord[] getRecords(){ + return mRecords; + } + + private native int parseNdefMessage(byte[] data); + + /** + * (Parcelable) Describe the parcel + * {@hide} + */ + public int describeContents() { + return 0; + } + + /** + * (Parcelable) Convert current object to a Parcel + * {@hide} + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mRecords.length); + dest.writeTypedArray(mRecords, 0); + } + + /** + * Creator class, needed when implementing from Parcelable + * {@hide} + */ + public static final Parcelable.Creator<NdefMessage> CREATOR = new Parcelable.Creator<NdefMessage>() { + public NdefMessage createFromParcel(Parcel in) { + int recordsLength = in.readInt(); + NdefRecord[] records = new NdefRecord[recordsLength]; + in.readTypedArray(records, NdefRecord.CREATOR); + return new NdefMessage(records); + } + + public NdefMessage[] newArray(int size) { + return new NdefMessage[size]; + } + }; + +} diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefRecord.aidl b/core/java/com/trustedlogic/trustednfc/android/NdefRecord.aidl new file mode 100644 index 0000000..9d95174 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/NdefRecord.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.trustedlogic.trustednfc.android; + +parcelable NdefRecord; diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefRecord.java b/core/java/com/trustedlogic/trustednfc/android/NdefRecord.java new file mode 100644 index 0000000..a0257fe --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/NdefRecord.java @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : NdefRecord.java + * Original-Author : Trusted Logic S.A. (Jeremie Corbier) + * Created : 05-10-2009 + */ + +package com.trustedlogic.trustednfc.android; + +import android.location.Location; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * An NDEF record as specified by the <a href="http://www.nfc-forum.org/">NFC + * Forum</a>. + * + * @see NdefMessage + * + * @since AA01.04 + * @hide + */ +public class NdefRecord implements Parcelable { + + /** + * Type Name Format - Empty record + */ + public static final short TNF_EMPTY = 0x0; + + /** + * Type Name Format - NFC Forum-defined type + */ + public static final short TNF_WELL_KNOWN_TYPE = 0x1; + + /** + * Type Name Format - RFC2045 MIME type + */ + public static final short TNF_MIME_MEDIA_TYPE = 0x2; + + /** + * Type Name Format - Absolute URI + */ + public static final short TNF_ABSOLUTE_URI = 0x3; + + /** + * Type Name Format - User-defined type + */ + public static final short TNF_EXTERNAL_TYPE = 0x4; + + /** + * Type Name Format - Unknown type + */ + public static final short TNF_UNKNOWN = 0x5; + + /** + * Type Name Format - Unchanged. This TNF is used for chunked records, so + * that middle records inherits from the first record's type. + */ + public static final short TNF_UNCHANGED = 0x6; + + /** + * NFC Forum-defined Type - Smart Poster + */ + public static final byte[] TYPE_SMART_POSTER = { 0x53, 0x70 }; + + /** + * NFC Forum-defined Type - Text + */ + public static final byte[] TYPE_TEXT = { 0x54 }; + + /** + * NFC Forum-defined Type - URI + */ + public static final byte[] TYPE_URI = { 0x55 }; + + /** + * NFC Forum-defined Global Type - Connection Handover Request + */ + public static final byte[] TYPE_HANDOVER_REQUEST = { 0x48, 0x72 }; + + /** + * NFC Forum-defined Global Type - Connection Handover Select + */ + public static final byte[] TYPE_HANDOVER_SELECT = { 0x48, 0x73 }; + + /** + * NFC Forum-defined Global Type - Connection Handover Carrier + */ + public static final byte[] TYPE_HANDOVER_CARRIER = { 0x48, 0x63 }; + + /** + * NFC Forum-defined Local Type - Alternative Carrier + */ + public static final byte[] TYPE_ALTERNATIVE_CARRIER = { 0x61, 0x63 }; + + /* Flag values */ + private static final int FLAG_MB = 0x80; + private static final int FLAG_ME = 0x40; + private static final int FLAG_CF = 0x20; + private static final int FLAG_SR = 0x10; + private static final int FLAG_IL = 0x08; + + /** + * Record Flags + */ + private short mFlags = 0; + + /** + * Record Type Name Format + */ + private short mTnf = 0; + + /** + * Record Type + */ + private byte[] mType = null; + + /** + * Record Identifier + */ + private byte[] mId = null; + + /** + * Record Payload + */ + private byte[] mPayload = null; + + /** + * Creates an NdefRecord given its Type Name Format, its type, its id and + * its. + * + * @param tnf + * Type Name Format + * @param type + * record type + * @param id + * record id (optional, can be null) + * @param data + * record payload + */ + public NdefRecord(short tnf, byte[] type, byte[] id, byte[] data) { + + /* generate flag */ + mFlags = FLAG_MB | FLAG_ME; + + /* Determine if it is a short record */ + if(data.length < 0xFF) + { + mFlags |= FLAG_SR; + } + + /* Determine if an id is present */ + if(id.length != 0) + { + mFlags |= FLAG_IL; + } + + mTnf = tnf; + mType = (byte[]) type.clone(); + mId = (byte[]) id.clone(); + mPayload = (byte[]) data.clone(); + } + + /** + * Appends data to the record's payload. + * + * @param data + * Data to be added to the record. + */ + public void appendPayload(byte[] data) { + byte[] newPayload = new byte[mPayload.length + data.length]; + + System.arraycopy(mPayload, 0, newPayload, 0, mPayload.length); + System.arraycopy(data, 0, newPayload, mPayload.length, data.length); + + mPayload = newPayload; + } + + /** + * Returns record as a byte array. + * + * @return record as a byte array. + */ + public byte[] toByteArray() { + return generate(mFlags, mTnf, mType, mId, mPayload); + } + + private native byte[] generate(short flags, short tnf, byte[] type, + byte[] id, byte[] data); + + /** + * (Parcelable) Describe the parcel + * {@hide} + */ + public int describeContents() { + return 0; + } + + /** + * (Parcelable) Convert current object to a Parcel + * {@hide} + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mTnf); + dest.writeInt(mType.length); + dest.writeByteArray(mType); + dest.writeInt(mId.length); + dest.writeByteArray(mId); + dest.writeInt(mPayload.length); + dest.writeByteArray(mPayload); + } + + /** + * Creator class, needed when implementing from Parcelable + * {@hide} + */ + public static final Parcelable.Creator<NdefRecord> CREATOR = new Parcelable.Creator<NdefRecord>() { + public NdefRecord createFromParcel(Parcel in) { + // TNF + short tnf = (short)in.readInt(); + // Type + int typeLength = in.readInt(); + byte[] type = new byte[typeLength]; + in.readByteArray(type); + // ID + int idLength = in.readInt(); + byte[] id = new byte[idLength]; + in.readByteArray(id); + // Payload + int payloadLength = in.readInt(); + byte[] payload = new byte[payloadLength]; + in.readByteArray(payload); + + return new NdefRecord(tnf, type, id, payload); + } + + public NdefRecord[] newArray(int size) { + return new NdefRecord[size]; + } + }; + + /** + * Returns record TNF + * + * @return mTnf + */ + public int getTnf(){ + return mTnf; + } + + /** + * Returns record TYPE + * + * @return mType + */ + public byte[] getType(){ + return mType; + } + + /** + * Returns record ID + * + * @return mId + */ + public byte[] getId(){ + return mId; + } + + /** + * Returns record Payload + * + * @return mPayload + */ + public byte[] getPayload(){ + return mPayload; + } + +} diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefTag.java b/core/java/com/trustedlogic/trustednfc/android/NdefTag.java new file mode 100644 index 0000000..1d99241 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/NdefTag.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : NDEFTag.java + * Original-Author : Trusted Logic S.A. (Jeremie Corbier) + * Created : 04-12-2009 + */ + +package com.trustedlogic.trustednfc.android; + +import java.io.IOException; + +import android.os.RemoteException; +import android.util.Log; + +/** + * NdefTag represents tags complying with the NFC Forum's NFC Data Exchange + * Format. + * + * @since AA01.04 + * @hide + */ +public class NdefTag extends NfcTag { + + private static final String TAG = "NdefTag"; + + + public NdefTag(NfcTag tag){ + super(tag.mService,tag.mHandle); + this.isConnected = tag.isConnected; + this.isClosed = tag.isClosed; + tag.isClosed = false; + } + + /** + * Internal constructor for the NfcNdefTag class. + * + * @param service The entry point to the Nfc Service for NfcNdefTag class. + * @param handle The handle returned by the NFC service and used to identify + * the tag in subsequent calls. + * @hide + */ + NdefTag(INfcTag service, int handle) { + super(service, handle); + } + + /** + * Read NDEF data from an NDEF tag. + * + * @return the NDEF message read from the tag. + * @throws NfcException if the tag is not NDEF-formatted. + * @throws IOException if the target has been lost or the connection has + * been closed. + * @see NdefMessage + */ + public NdefMessage read() throws NfcException, IOException { + // Check state + checkState(); + + //Check if the tag is Ndef compliant + if(isNdef != true){ + isNdef = isNdef(); + if(isNdef != true) { + throw new NfcException("Tag is not NDEF compliant"); + } + } + + // Perform transceive + try { + NdefMessage msg = mService.read(mHandle); + if (msg == null) { + throw new IOException("NDEF read failed"); + } + return msg; + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in read(): ", e); + return null; + } + } + + /** + * Write NDEF data to an NDEF-compliant tag. + * + * @param msg NDEF message to be written to the tag. + * @throws NfcException if the tag is not NDEF formatted. + * @throws IOException if the target has been lost or the connection has + * been closed. + * @see NdefMessage + */ + public void write(NdefMessage msg) throws NfcException, IOException { + // Check state + checkState(); + + //Check if the tag is Ndef compliant + if(isNdef != true){ + isNdef = isNdef(); + if(isNdef != true) { + throw new NfcException("Tag is not NDEF compliant"); + } + } + + // Perform transceive + try { + boolean isSuccess = mService.write(mHandle, msg); + if (!isSuccess) { + throw new IOException("NDEF write failed"); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in write(): ", e); + } + } +} diff --git a/core/java/com/trustedlogic/trustednfc/android/NfcException.java b/core/java/com/trustedlogic/trustednfc/android/NfcException.java new file mode 100644 index 0000000..2497c15 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/NfcException.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : NFCException.java + * Original-Author : Trusted Logic S.A. (Jeremie Corbier) + * Created : 26-08-2009 + */ + +package com.trustedlogic.trustednfc.android; + +/** + * Generic exception thrown in case something unexpected happened during the + * NFCManager operations. + * + * @since AA01.04 + * @hide + */ +public class NfcException extends Exception { + /** + * Constructs a new NfcException with the current stack trace and the + * specified detail message. + * + * @param s the detail message for this exception. + */ + public NfcException(String s) { + super(s); + } +} diff --git a/core/java/com/trustedlogic/trustednfc/android/NfcManager.java b/core/java/com/trustedlogic/trustednfc/android/NfcManager.java new file mode 100644 index 0000000..98ab5bf --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/NfcManager.java @@ -0,0 +1,656 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : NfcManager.java + * Original-Author : Trusted Logic S.A. (Jeremie Corbier) + * Created : 26-08-2009 + */ + +package com.trustedlogic.trustednfc.android; + +import java.io.IOException; + +import com.trustedlogic.trustednfc.android.internal.ErrorCodes; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.media.MiniThumbFile; +import android.os.Handler; +import android.os.RemoteException; +import android.util.Log; + +//import android.util.Log; + +/** + * This class provides the primary API for managing all aspects of NFC. Get an + * instance of this class by calling + * Context.getSystemService(Context.NFC_SERVICE). + * @hide + */ +public final class NfcManager { + /** + * Tag Reader Discovery mode + */ + private static final int DISCOVERY_MODE_TAG_READER = 0; + + /** + * NFC-IP1 Peer-to-Peer mode Enables the manager to act as a peer in an + * NFC-IP1 communication. Implementations should not assume that the + * controller will end up behaving as an NFC-IP1 target or initiator and + * should handle both cases, depending on the type of the remote peer type. + */ + private static final int DISCOVERY_MODE_NFCIP1 = 1; + + /** + * Card Emulation mode Enables the manager to act as an NFC tag. Provided + * that a Secure Element (an UICC for instance) is connected to the NFC + * controller through its SWP interface, it can be exposed to the outside + * NFC world and be addressed by external readers the same way they would + * with a tag. + * <p> + * Which Secure Element is exposed is implementation-dependent. + * + * @since AA01.04 + */ + private static final int DISCOVERY_MODE_CARD_EMULATION = 2; + + /** + * Used as Parcelable extra field in + * {@link com.trustedlogic.trustednfc.android.NfcManager#NDEF_TAG_DISCOVERED_ACTION} + * . It contains the NDEF message read from the NDEF tag discovered. + */ + public static final String NDEF_MESSAGE_EXTRA = "com.trustedlogic.trustednfc.android.extra.NDEF_MESSAGE"; + + /** + * Broadcast Action: a NDEF tag has been discovered. + * <p> + * Always contains the extra field + * {@link com.trustedlogic.trustednfc.android.NfcManager#NDEF_MESSAGE_EXTRA}. + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_NOTIFY permission. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String NDEF_TAG_DISCOVERED_ACTION = "com.trustedlogic.trustednfc.android.action.NDEF_TAG_DISCOVERED"; + + /** + * Used as byte array extra field in + * {@link com.trustedlogic.trustednfc.android.NfcManager#TRANSACTION_DETECTED_ACTION} + * . It contains the AID of the applet concerned by the transaction. + */ + public static final String AID_EXTRA = "com.trustedlogic.trustednfc.android.extra.AID"; + + /** + * Broadcast Action: a transaction with a secure element has been detected. + * <p> + * Always contains the extra field + * {@link com.trustedlogic.trustednfc.android.NfcManager#AID_EXTRA} + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_NOTIFY permission + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String TRANSACTION_DETECTED_ACTION = "com.trustedlogic.trustednfc.android.action.TRANSACTION_DETECTED"; + + /** + * LLCP link status: The LLCP link is activated. + * + * @since AA02.01 + */ + public static final int LLCP_LINK_STATE_ACTIVATED = 0; + + /** + * LLCP link status: The LLCP link is deactivated. + * + * @since AA02.01 + */ + public static final int LLCP_LINK_STATE_DEACTIVATED = 1; + + /** + * Used as int extra field in + * {@link com.trustedlogic.trustednfc.android.NfcManager#LLCP_LINK_STATE_CHANGED_ACTION} + * . It contains the new state of the LLCP link. + */ + public static final String LLCP_LINK_STATE_CHANGED_EXTRA = "com.trustedlogic.trustednfc.android.extra.LLCP_LINK_STATE"; + + /** + * Broadcast Action: the LLCP link state changed. + * <p> + * Always contains the extra field + * {@link com.trustedlogic.trustednfc.android.NfcManager#LLCP_LINK_STATE_CHANGED_EXTRA}. + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_LLCP permission. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String LLCP_LINK_STATE_CHANGED_ACTION = "com.trustedlogic.trustednfc.android.action.LLCP_LINK_STATE_CHANGED"; + + private static final String TAG = "NfcManager"; + + private Handler mHandler; + + private INfcManager mService; + + private INfcTag mNfcTagService; + + private IP2pTarget mP2pTargetService; + + private IP2pInitiator mP2pInitiatorService; + + private ILlcpSocket mLlcpSocketService; + + private ILlcpConnectionlessSocket mLlcpConnectionlessSocketService; + + private ILlcpServiceSocket mLlcpServiceSocketService; + + static NfcException convertErrorToNfcException(int errorCode) { + return convertErrorToNfcException(errorCode, null); + } + + static NfcException convertErrorToNfcException(int errorCode, String message) { + if (message == null) { + message = ""; + } else { + message = " (" + message + ")"; + } + + switch (errorCode) { + case ErrorCodes.ERROR_BUSY: + return new NfcException("Another operation is already pending" + message); + case ErrorCodes.ERROR_CANCELLED: + return new NfcException("Operation cancelled" + message); + case ErrorCodes.ERROR_TIMEOUT: + return new NfcException("Operation timed out" + message); + case ErrorCodes.ERROR_SOCKET_CREATION: + return new NfcException("Error during the creation of an Llcp socket:" + message); + case ErrorCodes.ERROR_SAP_USED: + return new NfcException("Error SAP already used:" + message); + case ErrorCodes.ERROR_SERVICE_NAME_USED: + return new NfcException("Error Service Name already used:" + message); + case ErrorCodes.ERROR_SOCKET_OPTIONS: + return new NfcException("Error Socket options:" + message); + case ErrorCodes.ERROR_INVALID_PARAM: + return new NfcException("Error Set Properties: invalid param" + message); + case ErrorCodes.ERROR_NFC_ON: + return new NfcException("Error Set Properties : NFC is ON" + message); + case ErrorCodes.ERROR_NOT_INITIALIZED: + return new NfcException("NFC is not running " + message); + case ErrorCodes.ERROR_SE_ALREADY_SELECTED: + return new NfcException("Secure Element already connected" + message); + case ErrorCodes.ERROR_NO_SE_CONNECTED: + return new NfcException("No Secure Element connected" + message); + case ErrorCodes.ERROR_SE_CONNECTED: + return new NfcException("A secure Element is already connected" + message); + default: + return new NfcException("Unkown error code " + errorCode + message); + } + } + + /** + * @hide + */ + public NfcManager(INfcManager service, Handler handler) { + mService = service; + mHandler = handler; + try { + mNfcTagService = mService.getNfcTagInterface(); + mP2pInitiatorService = mService.getP2pInitiatorInterface(); + mP2pTargetService = mService.getP2pTargetInterface(); + mLlcpServiceSocketService = mService.getLlcpServiceInterface(); + mLlcpConnectionlessSocketService = mService.getLlcpConnectionlessInterface(); + mLlcpSocketService = mService.getLlcpInterface(); + } catch (RemoteException e) { + mLlcpSocketService = null; + mNfcTagService = null; + mP2pInitiatorService = null; + mP2pTargetService = null; + mLlcpConnectionlessSocketService = null; + mLlcpServiceSocketService = null; + } + } + + /** + * Return the status of the NFC feature + * + * @return mIsNfcEnabled + * @since AA02.01 + */ + public boolean isEnabled() { + try { + return mService.isEnabled(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in isEnabled(): ", e); + return false; + } + } + + /** + * Enable the NFC Feature + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_ADMIN permission + * + * @throws NfcException if the enable failed + * @since AA02.01 + */ + public void enable() throws NfcException { + try { + boolean isSuccess = mService.enable(); + if (isSuccess == false) { + throw new NfcException("NFC Service failed to enable"); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in enable(): ", e); + } + } + + /** + * Disable the NFC feature + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_ADMIN permission + * + * @throws NfcException if the disable failed + * @since AA02.01 + */ + public void disable() throws NfcException { + try { + boolean isSuccess = mService.disable(); + if (isSuccess == false) { + throw new NfcException("NFC Service failed to disable"); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in disable(): ", e); + } + } + + /** + * Get the list of the identifiers of the Secure Elements detected + * by the NFC controller. + * + * @return list a list of Secure Element identifiers. + * @see #getSelectedSecureElement + * @see #selectSecureElement(int) + * @see #deselectSecureElement + * @since AA02.01 + */ + public int[] getSecureElementList() { + try { + return mService.getSecureElementList(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getSecureElementList(): ", e); + return null; + } + } + + /** + * Get the identifier of the currently selected secure element. + * + * @return id identifier of the currently selected Secure Element. 0 if none. + * @see #getSecureElementList + * @see #selectSecureElement(int) + * @see #deselectSecureElement + * @since AA02.01 + */ + public int getSelectedSecureElement() { + try { + return mService.getSelectedSecureElement(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getSelectedSecureElement(): ", e); + return -1; + } + } + + /** + * Select a specific Secure Element by its identifier. + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_ADMIN permission + * + * @throws NfcException if a or this secure element is already selected + * @see #getSecureElementList + * @see #getSelectedSecureElement + * @see #deselectSecureElement + * @since AA02.01 + */ + public void selectSecureElement(int seId) throws NfcException { + try { + int status = mService.selectSecureElement(seId); + if(status != ErrorCodes.SUCCESS){ + throw convertErrorToNfcException(status); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in selectSecureElement(): ", e); + } + } + + /** + * Deselect the currently selected Secure Element + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_ADMIN permission + * + * @throws NfcException if no secure Element is selected + * @see #getSecureElementList + * @see #getSelectedSecureElement + * @see #selectSecureElement(int) + * @since AA02.01 + */ + public void deselectSecureElement() throws NfcException { + try { + int status = mService.deselectSecureElement(); + if(status != ErrorCodes.SUCCESS){ + throw convertErrorToNfcException(status); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in deselectSecureElement(): ", e); + } + } + + /** + * Open a connection with a remote NFC peer + * + * This method does not return while no remote NFC peer enters the field. + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_RAW permission + * + * @return P2pDevice object to be used to communicate with the detected + * peer. + * @throws IOException if the target has been lost or the connection has + * been closed. + * @throws NfcException if an open is already started + * @see P2pDevice + * @see #getOpenTimeout + * @see #setOpenTimeout(int) + * @see #cancel + * @since AA02.01 + */ + public P2pDevice openP2pConnection() throws IOException, NfcException { + try { + int handle = mService.openP2pConnection(); + // Handle potential errors + if (ErrorCodes.isError(handle)) { + if (handle == ErrorCodes.ERROR_IO) { + throw new IOException(); + } else { + throw convertErrorToNfcException(handle); + } + } + // Build the public NfcTag object, depending on its type + if (mP2pTargetService.getMode(handle) == P2pDevice.MODE_P2P_TARGET) { + return new P2pTarget(mP2pTargetService, handle); + } else { + return new P2pInitiator(mP2pInitiatorService, handle); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in openTagConnection(): ", e); + return null; + } + } + + /** + * Open a connection with a tag + * + * This method does not return while no tag enters the field. + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_RAW permission + * + * @return tag object to be use to communicate with the detected NfcTag. + * @throws IOException if the target has been lost or the connection has + * been closed. + * @throws NfcException if an open is already started + * @see NfcTag + * @see #getOpenTimeout + * @see #setOpenTimeout(int) + * @see #cancel + * @since AA02.01 + */ + public NfcTag openTagConnection() throws IOException, NfcException { + try { + int handle = mService.openTagConnection(); + // Handle potential errors + if (ErrorCodes.isError(handle)) { + if (handle == ErrorCodes.ERROR_IO) { + throw new IOException(); + } else { + throw convertErrorToNfcException(handle); + } + } + // Build the public NfcTag object + return new NfcTag(mNfcTagService, handle); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in openTagConnection(): ", e); + return null; + } + } + + /** + * Set the timeout for open requests + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_RAW permission + * + * @param timeout value of the timeout for open request + * @see #openP2pConnection + * @see #openTagConnection + * @see #getOpenTimeout + * @since AA02.01 + */ + public void setOpenTimeout(int timeout) { + try { + mService.setOpenTimeout(timeout); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in setOpenTimeout(): ", e); + } + } + + /** + * Get the timeout value of open requests + * + * @return mTimeout + * @see #setOpenTimeout(int) + * @since AA02.01 + */ + public int getOpenTimeout() { + try { + return mService.getOpenTimeout(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getOpenTimeout(): ", e); + return 0; + } + } + + /** + * Cancel an openTagConnection or an openP2pConnection started + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_RAW permission + * + * @see #openP2pConnection + * @see #openTagConnection + * @since AA02.01 + */ + public void cancel() { + try { + mService.cancel(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in cancel(): ", e); + } + } + + /** + * Creates a connectionless socket for a LLCP link and set its Service + * Access Point number (SAP) + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_LLCP permission + * + * @param sap Service Access Point number related to the created + * Connectionless socket. + * @return LlcpConnectionlessSocket object to be used in a LLCP + * Connectionless communication. + * @throws IOException if the socket creation failed + * @throws NfcException if socket ressources are insufficicent + * @see LlcpConnectionlessSocket + * @since AA02.01 + */ + public LlcpConnectionlessSocket createLlcpConnectionlessSocket(int sap) throws IOException, + NfcException { + + try { + int handle = mService.createLlcpConnectionlessSocket(sap); + // Handle potential errors + if (ErrorCodes.isError(handle)) { + if (handle == ErrorCodes.ERROR_IO) { + throw new IOException(); + } else { + throw convertErrorToNfcException(handle); + } + } + + // Build the public LlcpConnectionLess object + return new LlcpConnectionlessSocket(mLlcpConnectionlessSocketService, handle); + + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in createLlcpConnectionlessSocket(): ", e); + return null; + } + } + + /** + * Creates a LlcpServiceSocket for a LLCP link, set its Service Access Point + * number (SAP). + * <p> + * During a LLCP communication, the LlcpServiceSocket will create LlcpSocket + * to communicate with incoming LLCP clients. For that, a server socket need + * to have some informations as a working buffer length in order to handle + * incoming data and some options to define the LLCP communication. + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_LLCP permission + * + * @param sap + * @param sn Service Name of the LlcpServiceSocket + * @param miu Maximum Information Unit (MIU) for a LlcpSocket created by the + * LlcpServiceSocket + * @param rw Receive Window (RW) for a LlcpSocket created by the + * LlcpServiceSocket + * @param linearBufferLength size of the memory space needed to handle + * incoming data for every LlcpSocket created. + * @return LlcpServiceSocket object to be used as a LLCP Service in a + * connection oriented communication. + * @throws IOException if the socket creation failed + * @throws NfcException if socket ressources are insufficicent + * @see LlcpServiceSocket + * @since AA02.01 + */ + public LlcpServiceSocket createLlcpServiceSocket(int sap, String sn, int miu, int rw, + int linearBufferLength) throws IOException, NfcException { + try { + int handle = mService.createLlcpServiceSocket(sap, sn, miu, rw, linearBufferLength); + // Handle potential errors + if (ErrorCodes.isError(handle)) { + if (handle == ErrorCodes.ERROR_IO) { + throw new IOException(); + } else { + throw convertErrorToNfcException(handle); + } + } + + // Build the public LlcpServiceSocket object + return new LlcpServiceSocket(mLlcpServiceSocketService, mLlcpSocketService, handle); + + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in createLlcpServiceSocket(): ", e); + return null; + } + } + + /** + * Creates a LlcpSocket for a LLCP link with a specific Service Access Point + * number (SAP) + * <p> + * A LlcpSocket need to have a linear buffer in order to handle incoming + * data. This linear buffer will be used to store incoming data as a stream. + * Data will be readable later. + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_LLCP permission + * + * @param sap Service Access Point number for the created socket + * @param miu Maximum Information Unit (MIU) of the communication socket + * @param rw Receive Window (RW) of the communication socket + * @param linearBufferLength size of the memory space needed to handle + * incoming data with this socket + * @throws IOException if the socket creation failed + * @throws NfcException if socket ressources are insufficicent + * @see LlcpSocket + * @since AA02.01 + */ + public LlcpSocket createLlcpSocket(int sap, int miu, int rw, int linearBufferLength) + throws IOException, NfcException { + try { + int handle = mService.createLlcpSocket(sap, miu, rw, linearBufferLength); + // Handle potential errors + if (ErrorCodes.isError(handle)) { + if (handle == ErrorCodes.ERROR_IO) { + throw new IOException(); + } else { + throw convertErrorToNfcException(handle); + } + } + // Build the public LlcpSocket object + return new LlcpSocket(mLlcpSocketService, handle); + + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in createLlcpSocket(): ", e); + return null; + } + } + + /** + * Set different parameters like the NCIP General bytes, the LLCP link + * parameters and all tag discovery parameters. + * <p class="note"> + * <strong>Note:</strong> Requires the NFC_ADMIN permission + * + * @param param parameter to be updated with a new value + * @param value new value of the parameter + * @throws NfcException if incorrect parameters of NFC is ON + * @since AA02.01 + */ + public void setProperties(String param, String value) throws NfcException { + try { + int result = mService.setProperties(param, value); + // Handle potential errors + if (ErrorCodes.isError(result)) { + throw convertErrorToNfcException(result); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in setProperties(): ", e); + } + } + + /** + * Get the value of different parameters like the NCFIP General bytes, the + * LLCP link parameters and all tag discovery parameters. + * + * @param param parameter to be updated + * @return String value of the requested parameter + * @throws RemoteException + * @since AA02.01 + */ + public String getProperties(String param) { + String value; + try { + value = mService.getProperties(param); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getProperties(): ", e); + return null; + } + return value; + } + +} diff --git a/core/java/com/trustedlogic/trustednfc/android/NfcTag.java b/core/java/com/trustedlogic/trustednfc/android/NfcTag.java new file mode 100644 index 0000000..798c7e4 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/NfcTag.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : NFCTag.java + * Original-Author : Trusted Logic S.A. (Daniel Tomas) + * Created : 26-02-2010 + */ + +package com.trustedlogic.trustednfc.android; + +import java.io.IOException; + +import android.os.RemoteException; +import android.util.Log; + +import com.trustedlogic.trustednfc.android.internal.ErrorCodes; + +/** + * This class represents tags with no known formatting. One can use the method + * {@link #isNdef()} to determine if the tag can store NDEF-formatted messages. + * <p> + * + * <pre class="prettyprint"> + * if (tag.isNdef()) { + * NdefTag ndefTag = (NdefTag) tag; + * NdefMessage msg = ndefTag.read(); + * } + * </pre> + * + * @since AA01.04 + * @see NdefMessage + * @hide + */ +public class NfcTag { + + private static final String TAG = "NfcTag"; + + /** + * The handle returned by the NFC service and used to identify the tag in + * every call of this class. + * + * @hide + */ + protected int mHandle; + + /** + * The entry point for tag operations. + * + * @hide + */ + protected INfcTag mService; + + /** + * Flag set when the object is closed and thus not usable any more. + * + * @hide + */ + protected boolean isClosed = false; + + /** + * Flag set when the tag is connected. + * + * @hide + */ + protected boolean isConnected = false; + + /** + * Flag set when a check NDEF is performed. + * + * @hide + */ + protected boolean isNdef = false; + + /** + * Check if tag is still opened. + * + * @return data sent by the P2pInitiator. + * @throws NfcException if accessing a closed target. + * + * @hide + */ + public void checkState() throws NfcException { + if (isClosed) { + throw new NfcException("Tag has been closed."); + } + if (!isConnected) { + throw new NfcException("Tag is not connected."); + } + } + + /** + * Internal constructor for the NfcTag class. + * + * @param service The entry point to the Nfc Service for NfcTag class. + * @param handle The handle returned by the NFC service and used to identify + * the tag in subsequent calls. + * @hide + */ + NfcTag(INfcTag service, int handle) { + this.mService = service; + this.mHandle = handle; + } + + /** + * Connects to the tag. This shall be called prior to any other operation on + * the tag. + * + * @throws IOException if the tag has been lost or the connection has been + * closed. + * @throws nfcException if the tag is already in connected state. + */ + public void connect() throws NfcException, IOException { + // Check state + if (isClosed) { + throw new NfcException("Tag has been closed."); + } + if (isConnected) { + throw new NfcException("Already connected"); + } + + // Perform connect + try { + int result = mService.connect(mHandle); + if (ErrorCodes.isError(result)) { + if (result == ErrorCodes.ERROR_IO) { + throw new IOException("Failed to connect"); + } + else { + throw NfcManager.convertErrorToNfcException(result); + } + } + isConnected = true; + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in connect(): ", e); + } + } + + /** + * Disconnects from the tag. This must be called so that other targets can + * be discovered. It restarts the NFC discovery loop. + * + * @throws NfcException if the tag is already in disconnected state or not connected + */ + public void close() throws NfcException { + // Check state + checkState(); + + try { + mService.close(mHandle); + isClosed = true; + isConnected = false; + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in close(): ", e); + } + } + + /** + * Exchanges raw data with the tag, whatever the tag type. + * + * To exchange APDUs with a ISO14443-4-compliant tag, the data parameter + * must be filled with the C-APDU (CLA, INS, P1, P2 [, ...]). The returned + * data consists of the R-APDU ([...,] SW1, SW2). + * + * @param data data to be sent to the tag + * @return data sent in response by the tag + * @throws IOException if the tag has been lost or the connection has been + * closed. + * @throws NfcException in case of failure within the stack + */ + public byte[] transceive(byte[] data) throws IOException, NfcException { + // Check state + checkState(); + + // Perform transceive + try { + byte[] response = mService.transceive(mHandle, data); + if (response == null) { + throw new IOException("Transceive failed"); + } + return response; + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in transceive(): ", e); + return null; + } + } + + /** + * Checks whether tag is NDEF-compliant or not. + * + * @return true if the tag is NDEF-compliant, false otherwise + * @throws NfcException in case an error occurred when trying to determine + * whether the tag is NDEF-compliant + */ + public boolean isNdef() throws NfcException { + // Check state + checkState(); + + // Perform Check Ndef + try { + isNdef = mService.isNdef(mHandle); + return isNdef; + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in isNdef(): ", e); + return false; + } + } + + /** + * Returns target type. constants. + * + * @return tag type. + */ + public String getType() { + try { + return mService.getType(mHandle); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getType(): ", e); + return null; + } + } + + /** + * Returns target UID. + * + * @return tag UID. + */ + public byte[] getUid() { + try { + return mService.getUid(mHandle); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getType(): ", e); + return null; + } + } + +} diff --git a/core/java/com/trustedlogic/trustednfc/android/P2pDevice.java b/core/java/com/trustedlogic/trustednfc/android/P2pDevice.java new file mode 100644 index 0000000..65800f2 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/P2pDevice.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : P2PDevice.java + * Original-Author : Trusted Logic S.A. (Daniel Tomas) + * Created : 26-02-2010 + */ + +package com.trustedlogic.trustednfc.android; + +import java.io.IOException; + +/** + * P2pDevice is the abstract base class for all supported P2P targets the + * NfcManager can handle. + * @hide + */ +public abstract class P2pDevice { + + /** + * Peer-to-Peer Target. + */ + public static final short MODE_P2P_TARGET = 0x00; + + /** + * Peer-to-Peer Initiator. + */ + public static final short MODE_P2P_INITIATOR = 0x01; + + /** + * Invalid target type. + */ + public static final short MODE_INVALID = 0xff; + + /** + * Target handle, used by native calls. + * @hide + */ + protected int mHandle; + + /** + * Flag set when the object is closed and thus not usable any more. + * @hide + */ + protected boolean isClosed = false; + + /** + * Prevent default constructor to be public. + * @hide + */ + protected P2pDevice() { + } + + /** + * Returns the remote NFC-IP1 General Bytes. + * + * @return remote general bytes + * @throws IOException + */ + public byte[] getGeneralBytes() throws IOException { + // Should not be called directly (use subclasses overridden method instead) + return null; + } + + /** + * Returns target type. The value returned can be one of the TYPE_* + * constants. + * + * @return target type. + */ + public int getMode() { + // Should not be called directly (use subclasses overridden method instead) + return MODE_INVALID; + } +} diff --git a/core/java/com/trustedlogic/trustednfc/android/P2pInitiator.java b/core/java/com/trustedlogic/trustednfc/android/P2pInitiator.java new file mode 100644 index 0000000..0f28ae0 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/P2pInitiator.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : P2PInitiator.java + * Original-Author : Trusted Logic S.A. (Daniel Tomas) + */ + +package com.trustedlogic.trustednfc.android; + +import java.io.IOException; + +import com.trustedlogic.trustednfc.android.internal.ErrorCodes; + +import android.os.RemoteException; +import android.util.Log; + +/** + * P2pInitiator represents the initiator in an NFC-IP1 peer-to-peer + * communication. + * + * @see P2pTarget + * @since AA02.01 + * @hide + */ +public class P2pInitiator extends P2pDevice { + + private static final String TAG = "P2pInitiator"; + + /** + * The entry point for P2P tag operations. + * @hide + */ + private IP2pInitiator mService; + + /** + * Internal constructor for the P2pInitiator class. + * + * @param handle The handle returned by the NFC service and used to identify + * the tag in subsequent calls. + * + * @hide + */ + P2pInitiator(IP2pInitiator service, int handle) { + this.mService = service; + this.mHandle = handle; + } + + /** + * Receives data from a P2pInitiator. + * + * @return data sent by the P2pInitiator. + * @throws IOException if the target has been lost or if the connection has + * been closed. + */ + public byte[] receive() throws IOException { + try { + byte[] result = mService.receive(mHandle); + if (result == null) { + throw new IOException("Tag has been lost"); + } + return result; + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in receive(): ", e); + return null; + } + } + + /** + * Sends data to a P2pInitiator. + * + * @param data data to be sent to the P2pInitiator. + * @throws IOException if the target has been lost or if the connection has + * been closed. + */ + public void send(byte[] data) throws IOException { + try { + boolean isSuccess = mService.send(mHandle, data); + if (!isSuccess) { + throw new IOException("Tag has been lost"); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in send(): ", e); + } + } + + @Override + public byte[] getGeneralBytes() { + try { + return mService.getGeneralBytes(mHandle); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getGeneralBytes(): ", e); + return null; + } + } + + @Override + public int getMode() { + return P2pDevice.MODE_P2P_INITIATOR; + } + +} diff --git a/core/java/com/trustedlogic/trustednfc/android/P2pTarget.java b/core/java/com/trustedlogic/trustednfc/android/P2pTarget.java new file mode 100644 index 0000000..b5e00db --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/P2pTarget.java @@ -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. + */ + +/** + * File : P2PTarget.java + * Original-Author : Trusted Logic S.A. (Daniel Tomas) + */ + +package com.trustedlogic.trustednfc.android; + +import java.io.IOException; + +import com.trustedlogic.trustednfc.android.internal.ErrorCodes; + +import android.os.RemoteException; +import android.util.Log; + +/** + * P2pTarget represents the target in an NFC-IP1 peer-to-peer communication. + * + * @see P2pInitiator + * @since AA02.01 + * @hide + */ +public class P2pTarget extends P2pDevice { + + private static final String TAG = "P2pTarget"; + + /** + * The entry point for P2P tag operations. + * @hide + */ + private IP2pTarget mService; + + /** + * Flag set when the object is closed and thus not usable any more. + * @hide + */ + private boolean isClosed = false; + + /** + * Flag set when the tag is connected. + * @hide + */ + private boolean isConnected = false; + + /** + * Check if tag is still opened. + * + * @return data sent by the P2pInitiator. + * @throws NfcException if accessing a closed target. + * + * @hide + */ + public void checkState() throws NfcException { + if(isClosed) { + throw new NfcException("Tag has been closed."); + } + } + + /** + * Internal constructor for the P2pTarget class. + * + * @param handle The handle returned by the NFC service and used to identify + * the tag in subsequent calls. + * + * @hide + */ + P2pTarget(IP2pTarget service, int handle) { + this.mService = service; + this.mHandle = handle; + } + + /** + * Connects to the P2pTarget. This shall be called prior to any other + * operation on the P2pTarget. + * + * @throws NfcException + */ + public void connect() throws NfcException { + // Check state + checkState(); + if (isConnected) { + throw new NfcException("Already connected"); + } + + // Perform connect + try { + int result = mService.connect(mHandle); + if (ErrorCodes.isError(result)) { + if (result == ErrorCodes.ERROR_IO) { + throw new NfcException("Failed to connect"); + } + else { + throw NfcManager.convertErrorToNfcException(result); + } + } + isConnected = true; + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in connect(): ", e); + } + } + + /** + * Disconnects from the P2p Target. This must be called so that other + * targets can be discovered. It restarts the NFC discovery loop. + * + * @throws NFCException + */ + public void disconnect() throws NfcException { + checkState(); + try { + mService.disconnect(mHandle); + isConnected = true; + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in disconnect(): ", e); + } + } + + /** + * Exchanges raw data with the P2pTarget. + * + * @param data data to be sent to the P2pTarget + * @return data sent in response by the P2pTarget + * @throws IOException if the target has been lost or the connection has + * been closed. + * @throws NfcException in case of failure within the stack + */ + public byte[] transceive(byte[] data) throws IOException, NfcException { + // Check state + checkState(); + + // Perform transceive + try { + byte[] response = mService.transceive(mHandle, data); + if (response == null) { + throw new IOException("Transceive failed"); + } + return response; + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in transceive(): ", e); + return null; + } + } + + /** + * Get the General bytes of the connected P2P Target + * + * @return general bytes of the connected P2P Target + * @throws IOException if the target in not in connected state + */ + public byte[] getGeneralBytes() throws IOException { + try { + if(isConnected){ + return mService.getGeneralBytes(mHandle); + }else{ + throw new IOException("Target not in connected state"); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getGeneralBytes(): ", e); + return null; + } + } + + @Override + public int getMode() { + return P2pDevice.MODE_P2P_TARGET; + } + +} diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/ErrorCodes.java b/core/java/com/trustedlogic/trustednfc/android/internal/ErrorCodes.java new file mode 100644 index 0000000..ca3b7e0 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/internal/ErrorCodes.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : ErrorCodes.java + * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau) + * Created : 26-02-2010 + */ + +package com.trustedlogic.trustednfc.android.internal; + +/** + * This class defines all the error codes that can be returned by the service + * and producing an exception on the application level. These are needed since + * binders does not support exceptions. + * + * @hide + */ +public class ErrorCodes { + + public static boolean isError(int code) { + if (code < 0) { + return true; + } else { + return false; + } + } + + public static final int SUCCESS = 0; + + public static final int ERROR_IO = -1; + + public static final int ERROR_CANCELLED = -2; + + public static final int ERROR_TIMEOUT = -3; + + public static final int ERROR_BUSY = -4; + + public static final int ERROR_CONNECT = -5; + + public static final int ERROR_DISCONNECT = -5; + + public static final int ERROR_READ = -6; + + public static final int ERROR_WRITE = -7; + + public static final int ERROR_INVALID_PARAM = -8; + + public static final int ERROR_INSUFFICIENT_RESOURCES = -9; + + public static final int ERROR_SOCKET_CREATION = -10; + + public static final int ERROR_SOCKET_NOT_CONNECTED = -11; + + public static final int ERROR_BUFFER_TO_SMALL = -12; + + public static final int ERROR_SAP_USED = -13; + + public static final int ERROR_SERVICE_NAME_USED = -14; + + public static final int ERROR_SOCKET_OPTIONS = -15; + + public static final int ERROR_NFC_ON = -16; + + public static final int ERROR_NOT_INITIALIZED = -17; + + public static final int ERROR_SE_ALREADY_SELECTED = -18; + + public static final int ERROR_SE_CONNECTED = -19; + + public static final int ERROR_NO_SE_CONNECTED = -20; + + + + + + +} diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpConnectionlessSocket.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpConnectionlessSocket.java new file mode 100644 index 0000000..ccfbeb4 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpConnectionlessSocket.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : NativeLlcpConnectionLessSocket.java + * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau) + * Created : 18-02-2010 + */ + +package com.trustedlogic.trustednfc.android.internal; + +import com.trustedlogic.trustednfc.android.LlcpPacket; + +/** + * LlcpConnectionlessSocket represents a LLCP Connectionless object to be used + * in a connectionless communication + * + * @since AA02.01 + * {@hide} + */ + +public class NativeLlcpConnectionlessSocket { + + private int mHandle; + + private int mSap; + + private int mLinkMiu; + + public NativeLlcpConnectionlessSocket(){; + } + + public NativeLlcpConnectionlessSocket(int sap){ + mSap = sap; + } + + public native boolean doSendTo(int sap, byte[] data); + + public native LlcpPacket doReceiveFrom(int linkMiu); + + public native boolean doClose(); + + public int getLinkMiu(){ + return mLinkMiu; + } + + public int getSap(){ + return mSap; + } + + public int getHandle(){ + return mHandle; + } + +} diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpServiceSocket.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpServiceSocket.java new file mode 100644 index 0000000..a01f135 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpServiceSocket.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : NativeLlcpServerSocket.java + * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau) + * Created : 18-02-2010 + */ + +package com.trustedlogic.trustednfc.android.internal; + +/** + * LlcpServiceSocket represents a LLCP Service to be used in a + * Connection-oriented communication + * {@hide} + */ + +public class NativeLlcpServiceSocket { + + private int mHandle; + + private int mLocalMiu; + + private int mLocalRw; + + private int mLocalLinearBufferLength; + + private int mSap; + + private int mTimeout; + + private String mServiceName; + + public NativeLlcpServiceSocket(){ + + } + + public NativeLlcpServiceSocket(String serviceName){ + mServiceName = serviceName; + } + + public native NativeLlcpSocket doAccept(int timeout, int miu, int rw, int linearBufferLength); + + public native boolean doClose(); + + public int getHandle(){ + return mHandle; + } + + public void setAcceptTimeout(int timeout){ + mTimeout = timeout; + } + + public int getAcceptTimeout(){ + return mTimeout; + } + + public int getRw(){ + return mLocalRw; + } + + public int getMiu(){ + return mLocalMiu; + } + + public int getLinearBufferLength(){ + return mLocalLinearBufferLength; + } +} diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpSocket.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpSocket.java new file mode 100644 index 0000000..077c5e0 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpSocket.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. + */ + +/** + * File : NativeLlcpClientSocket.java + * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau) + * Created : 18-02-2010 + */ + +package com.trustedlogic.trustednfc.android.internal; + +/** + * LlcpClientSocket represents a LLCP Connection-Oriented client to be used in a + * connection-oriented communication + * {@hide} + */ + +public class NativeLlcpSocket { + + private int mHandle; + + private int mSap; + + private int mLocalMiu; + + private int mLocalRw; + + private int mTimeout; + + public NativeLlcpSocket(){ + + } + + public NativeLlcpSocket(int sap, int miu, int rw){ + mSap = sap; + mLocalMiu = miu; + mLocalRw = rw; + } + + public native boolean doConnect(int nSap, int timeout); + + public native boolean doConnectBy(String sn, int timeout); + + public native boolean doClose(); + + public native boolean doSend(byte[] data); + + public native int doReceive(byte[] recvBuff); + + public native int doGetRemoteSocketMiu(); + + public native int doGetRemoteSocketRw(); + + + + public void setConnectTimeout(int timeout){ + mTimeout = timeout; + } + + public int getConnectTimeout(){ + return mTimeout; + } + + public int getSap(){ + return mSap; + } + + public int getMiu(){ + return mLocalMiu; + } + + public int getRw(){ + return mLocalRw; + } + + public int getHandle(){ + return mHandle; + } + +} diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNdefTag.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNdefTag.java new file mode 100644 index 0000000..d1e64a6 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNdefTag.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : NativeNdefTag.java + * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau) + * Created : 18-02-2010 + */ + +package com.trustedlogic.trustednfc.android.internal; + +/** + * Native interface to the NDEF tag functions + * + * {@hide} + */ +public class NativeNdefTag { + private int mHandle; + + public native byte[] doRead(); + + public native boolean doWrite(byte[] buf); +} diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java new file mode 100644 index 0000000..2f5a0f0 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : NativeNfcManager.java + * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau) + * Created : 18-02-2010 + */ + +package com.trustedlogic.trustednfc.android.internal; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +import com.trustedlogic.trustednfc.android.NfcManager; +import com.trustedlogic.trustednfc.android.NdefMessage; +import com.trustedlogic.trustednfc.android.NfcTag; + +/** + * Native interface to the NFC Manager functions {@hide} + */ +public class NativeNfcManager { + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String INTERNAL_LLCP_LINK_STATE_CHANGED_EXTRA = "com.trustedlogic.trustednfc.android.extra.INTERNAL_LLCP_LINK_STATE"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION = "com.trustedlogic.trustednfc.android.action.INTERNAL_LLCP_LINK_STATE_CHANGED"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String INTERNAL_TARGET_DESELECTED_ACTION = "com.trustedlogic.trustednfc.android.action.INTERNAL_TARGET_DESELECTED"; + + /* Native structure */ + private int mNative; + + private Context mContext; + + private Handler mNfcHandler; + + private static final String TAG = "NativeNfcManager"; + + private static final int MSG_NDEF_TAG = 0; + + private static final int MSG_CARD_EMULATION = 1; + + private static final int MSG_LLCP_LINK_ACTIVATION = 2; + + private static final int MSG_LLCP_LINK_DEACTIVATED = 3; + + private static final int MSG_TARGET_DESELECTED = 4; + + public NativeNfcManager(Context context) { + mNfcHandler = new NfcHandler(); + mContext = context; + } + + /** + * Initializes Native structure + */ + public native boolean initializeNativeStructure(); + + /** + * Initializes NFC stack. + */ + public native boolean initialize(); + + /** + * Deinitializes NFC stack. + */ + public native boolean deinitialize(); + + /** + * Enable discory for the NdefMessage and Transaction notification + */ + public native void enableDiscovery(int mode); + + /** + * Disables an NFCManager mode of operation. Allows to disable tag reader, + * peer to peer initiator or target modes. + * + * @param mode discovery mode to enable. Must be one of the provided + * NFCManager.DISCOVERY_MODE_* constants. + */ + public native void disableDiscoveryMode(int mode); + + public native int[] doGetSecureElementList(); + + public native void doSelectSecureElement(int seID); + + public native void doDeselectSecureElement(int seID); + + public native NativeP2pDevice doOpenP2pConnection(int timeout); + + public native NativeNfcTag doOpenTagConnection(int timeout); + + public native int doGetLastError(); + + public native void doSetProperties(int param, int value); + + public native void doCancel(); + + public native NativeLlcpConnectionlessSocket doCreateLlcpConnectionlessSocket(int nSap); + + public native NativeLlcpServiceSocket doCreateLlcpServiceSocket(int nSap, String sn, int miu, + int rw, int linearBufferLength); + + public native NativeLlcpSocket doCreateLlcpSocket(int sap, int miu, int rw, + int linearBufferLength); + + public native boolean doCheckLlcp(); + + public native boolean doActivateLlcp(); + + private class NfcHandler extends Handler { + @Override + public void handleMessage(Message msg) { + + try { + switch (msg.what) { + case MSG_NDEF_TAG: + Log.d(TAG, "Checking for NDEF tag message"); + NativeNfcTag tag = (NativeNfcTag) msg.obj; + if (tag.doConnect()) { + if (tag.checkNDEF()) { + byte[] buff = tag.doRead(); + if (buff != null) { + NdefMessage msgNdef = new NdefMessage(buff); + if (msgNdef != null) { + /* Send broadcast ordered */ + Intent NdefMessageIntent = new Intent(); + NdefMessageIntent + .setAction(NfcManager.NDEF_TAG_DISCOVERED_ACTION); + NdefMessageIntent.putExtra(NfcManager.NDEF_MESSAGE_EXTRA, + msgNdef); + Log.d(TAG, "NDEF message found, broadcasting to applications"); + mContext.sendOrderedBroadcast(NdefMessageIntent, + android.Manifest.permission.NFC_NOTIFY); + /* Disconnect tag */ + tag.doAsyncDisconnect(); + } + } else { + Log.w(TAG, "Unable to read NDEF message (tag empty or not well formated)"); + /* Disconnect tag */ + tag.doAsyncDisconnect(); + } + } else { + Log.d(TAG, "Tag is *not* NDEF compliant"); + /* Disconnect tag */ + tag.doAsyncDisconnect(); + } + } else { + /* Disconnect tag */ + tag.doAsyncDisconnect(); + } + break; + case MSG_CARD_EMULATION: + Log.d(TAG, "Card Emulation message"); + byte[] aid = (byte[]) msg.obj; + /* Send broadcast ordered */ + Intent TransactionIntent = new Intent(); + TransactionIntent.setAction(NfcManager.TRANSACTION_DETECTED_ACTION); + TransactionIntent.putExtra(NfcManager.AID_EXTRA, aid); + Log.d(TAG, "Broadcasting Card Emulation event"); + mContext.sendOrderedBroadcast(TransactionIntent, + android.Manifest.permission.NFC_NOTIFY); + break; + + case MSG_LLCP_LINK_ACTIVATION: + NativeP2pDevice device = (NativeP2pDevice) msg.obj; + + Log.d(TAG, "LLCP Activation message"); + + if (device.getMode() == NativeP2pDevice.MODE_P2P_TARGET) { + if (device.doConnect()) { + /* Check Llcp compliancy */ + if (doCheckLlcp()) { + /* Activate Llcp Link */ + if (doActivateLlcp()) { + Log.d(TAG, "Initiator Activate LLCP OK"); + /* Broadcast Intent Link LLCP activated */ + Intent LlcpLinkIntent = new Intent(); + LlcpLinkIntent + .setAction(INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION); + LlcpLinkIntent.putExtra( + INTERNAL_LLCP_LINK_STATE_CHANGED_EXTRA, + NfcManager.LLCP_LINK_STATE_ACTIVATED); + Log.d(TAG, "Broadcasting internal LLCP activation"); + mContext.sendBroadcast(LlcpLinkIntent); + } + + } else { + device.doDisconnect(); + } + + } + + } else if (device.getMode() == NativeP2pDevice.MODE_P2P_INITIATOR) { + /* Check Llcp compliancy */ + if (doCheckLlcp()) { + /* Activate Llcp Link */ + if (doActivateLlcp()) { + Log.d(TAG, "Target Activate LLCP OK"); + /* Broadcast Intent Link LLCP activated */ + Intent LlcpLinkIntent = new Intent(); + LlcpLinkIntent + .setAction(INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION); + LlcpLinkIntent.putExtra(INTERNAL_LLCP_LINK_STATE_CHANGED_EXTRA, + NfcManager.LLCP_LINK_STATE_ACTIVATED); + Log.d(TAG, "Broadcasting internal LLCP activation"); + mContext.sendBroadcast(LlcpLinkIntent); + } + } + } + break; + + case MSG_LLCP_LINK_DEACTIVATED: + /* Broadcast Intent Link LLCP activated */ + Log.d(TAG, "LLCP Link Deactivated message"); + Intent LlcpLinkIntent = new Intent(); + LlcpLinkIntent.setAction(NfcManager.LLCP_LINK_STATE_CHANGED_ACTION); + LlcpLinkIntent.putExtra(NfcManager.LLCP_LINK_STATE_CHANGED_EXTRA, + NfcManager.LLCP_LINK_STATE_DEACTIVATED); + Log.d(TAG, "Broadcasting LLCP deactivation"); + mContext.sendOrderedBroadcast(LlcpLinkIntent, + android.Manifest.permission.NFC_LLCP); + break; + + case MSG_TARGET_DESELECTED: + /* Broadcast Intent Target Deselected */ + Log.d(TAG, "Target Deselected"); + Intent TargetDeselectedIntent = new Intent(); + TargetDeselectedIntent.setAction(INTERNAL_TARGET_DESELECTED_ACTION); + Log.d(TAG, "Broadcasting Intent"); + mContext.sendOrderedBroadcast(TargetDeselectedIntent, + android.Manifest.permission.NFC_LLCP); + break; + + default: + Log.e(TAG, "Unknown message received"); + break; + } + } catch (Exception e) { + // Log, don't crash! + Log.e(TAG, "Exception in NfcHandler.handleMessage:", e); + } + } + }; + + /** + * Notifies Ndef Message + */ + private void notifyNdefMessageListeners(NativeNfcTag tag) { + Message msg = mNfcHandler.obtainMessage(); + + msg.what = MSG_NDEF_TAG; + msg.obj = tag; + + mNfcHandler.sendMessage(msg); + } + + /** + * Notifies transaction + */ + private void notifyTargetDeselected() { + Message msg = mNfcHandler.obtainMessage(); + + msg.what = MSG_TARGET_DESELECTED; + + mNfcHandler.sendMessage(msg); + } + + /** + * Notifies transaction + */ + private void notifyTransactionListeners(byte[] aid) { + Message msg = mNfcHandler.obtainMessage(); + + msg.what = MSG_CARD_EMULATION; + msg.obj = aid; + + mNfcHandler.sendMessage(msg); + } + + /** + * Notifies P2P Device detected, to activate LLCP link + */ + private void notifyLlcpLinkActivation(NativeP2pDevice device) { + Message msg = mNfcHandler.obtainMessage(); + + msg.what = MSG_LLCP_LINK_ACTIVATION; + msg.obj = device; + + mNfcHandler.sendMessage(msg); + } + + /** + * Notifies P2P Device detected, to activate LLCP link + */ + private void notifyLlcpLinkDeactivated() { + Message msg = mNfcHandler.obtainMessage(); + + msg.what = MSG_LLCP_LINK_DEACTIVATED; + + mNfcHandler.sendMessage(msg); + } + +} diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcTag.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcTag.java new file mode 100644 index 0000000..b92783d --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcTag.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. + */ + +/** + * File : NativeNfcTag.java + * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau) + * Created : 18-02-2010 + */ + +package com.trustedlogic.trustednfc.android.internal; + +/** + * Native interface to the NFC tag functions + * + * {@hide} + */ +public class NativeNfcTag { + private int mHandle; + + private String mType; + + private byte[] mUid; + + public native boolean doConnect(); + + public native boolean doDisconnect(); + + public native void doAsyncDisconnect(); + + public native byte[] doTransceive(byte[] data); + + public native boolean checkNDEF(); + + public native byte[] doRead(); + + public native boolean doWrite(byte[] buf); + + public int getHandle() { + return mHandle; + } + + public String getType() { + return mType; + } + + public byte[] getUid() { + return mUid; + } +} diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeP2pDevice.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeP2pDevice.java new file mode 100644 index 0000000..75d25ba --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeP2pDevice.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * File : NativeP2pDevice.java + * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau) + * Created : 18-02-2010 + */ + +package com.trustedlogic.trustednfc.android.internal; + +/** + * Native interface to the P2P Initiator functions + * + * {@hide} + */ +public class NativeP2pDevice { + + /** + * Peer-to-Peer Target. + */ + public static final short MODE_P2P_TARGET = 0x00; + + /** + * Peer-to-Peer Initiator. + */ + public static final short MODE_P2P_INITIATOR = 0x01; + + /** + * Invalid target type. + */ + public static final short MODE_INVALID = 0xff; + + private int mHandle; + + private int mMode; + + private byte[] mGeneralBytes; + + public native byte[] doReceive(); + + public native boolean doSend(byte[] data); + + public native boolean doConnect(); + + public native boolean doDisconnect(); + + public native byte[] doTransceive(byte[] data); + + public int getHandle() { + return mHandle; + } + + public int getMode() { + return mMode; + } + + public byte[] getGeneralBytes() { + return mGeneralBytes; + } + +} diff --git a/core/java/com/trustedlogic/trustednfc/android/package.html b/core/java/com/trustedlogic/trustednfc/android/package.html new file mode 100644 index 0000000..0c0b605 --- /dev/null +++ b/core/java/com/trustedlogic/trustednfc/android/package.html @@ -0,0 +1,473 @@ +<html> +<body> + +<p>Provides classes that manage the NFC functionality.</p> + +<p>The NFC functionality is related to Near Field Communication.</p> + +<p>The NFC APIs let applications:</p> +<ul> + <li>Scan for remote NFC targets (NFC Tag or NFC Peer)</li> + <li>Transfer raw data to and from remote NFC targets (NFC Tags or NFC Peer)</li> + <li>Read/Write NDEF data from/to remote NFC targets (NFC Tags)</li> + <li>Establish LLCP connection with a remote NFC target (NFC Peer with LLCP support)</li> + <li>Exchange data with a remote NFC target through LLCP services (NFC Peer with LLCP support)</li> + <li>Be notified of transactions on the local Secure Element by an external NFC reader</li> +</ul> + + +<h1>Setting Up NFC</h1> + +<p> +Before an application can use the NFC feature, it needs to check if NFC is +supported on the device by getting an instance of the +{@link com.trustedlogic.trustednfc.android.NfcManager} class. +</p> + +<pre> + NfcManager mNfcManager = (NfcManager) getSystemService(Context.NFC_SERVICE); + if (mNfcManager == null) { + // Device does not support NFC + } +</pre> + +<p> +An application can ensure that NFC is enabled. +If not, an application with the needed permission can request that NFC be +enabled. +</p> + +<pre> + if (!mNfcManager.isEnabled) { + // NFC is currently disabled. + // Enable NFC. + mNfcManager.enable(); + } +</pre> + +<p> +Before using the card emulation mode, an application can ensure that a secure +element is selected ({@link com.trustedlogic.trustednfc.android.NfcManager#getSelectedSecureElement}). +If not, an application with the needed permission can recover the list of +available secure elements on the device +({@link com.trustedlogic.trustednfc.android.NfcManager#getSecureElementList}) and select one +({@link com.trustedlogic.trustednfc.android.NfcManager#selectSecureElement}). +</p> + +<p> +Before using the NFC feature, an application can configure the NFC device by +calling {@link com.trustedlogic.trustednfc.android.NfcManager#setProperties}. This function allows: +</p> +<ul> + <li>Enabling/disabling the NFC device capabilities (RF types, baudrates, + NFCIP-1 mode and role...)</li> + <li>Settings the NFCIP-1 general bytes and the LLCP link parameters</li> +</ul> +<p> +The setting properties can be customized according to the Device capabilities. +The next table give the minimal set of properties supported by the Device. +Depending on the implementation, the table may be completed. +</p> +<table> + <TR><TH> Property Name </TH><TH> Property Values </TH></TR> + <TR><TD> discovery.felica </TD><TD> <b>true</b>|false </TD></TR> + <TR><TD> discovery.iso14443A </TD><TD> <b>true</b>|false </TD></TR> + <TR><TD> discovery.iso14443B </TD><TD> <b>true</b>|false </TD></TR> + <TR><TD> discovery.iso15693 </TD><TD> <b>true</b>|false </TD></TR> + <TR><TD> discovery.nfcip </TD><TD> <b>true</b>|false </TD></TR> + <TR><TD> nfcip.baudrate </TD><TD> 106|212|424 </TD></TR> + <TR><TD> nfcip.generalbytes </TD><TD> </TD></TR> + <TR><TD> nfcip.mode </TD><TD> active|passive|<b>all</b> </TD></TR> + <TR><TD> nfcip.role </TD><TD> initiator|target|<b>both</b> </TD></TR> + <TR><TD> llcp.lto </TD><TD> <b>150</b> (0 to 255) </TD></TR> + <TR><TD> llcp.opt </TD><TD> <b>0</b> (0 to 3) </TD></TR> + <TR><TD> llcp.miu </TD><TD> <b>128</b> (128 to 2176) </TD></TR> + <TR><TD> llcp.wks </TD><TD> <b>1</b> (0 to 15) </TD></TR> +</table> +<p>(default values in bold)</p> + + +<h1>NFC Permissions</h1> + +<p> +To change the NFC service settings such as enabling the NFC targets +discovery or activating the secure element, an application must declare the +NFC_ADMIN permission. +</p> +<p> +To perform NFC raw communication with a remote NFC target in +Reader/Write Mode or Peer-to-Peer Mode, an application must declare the NFC_RAW +permission. +</p> +<p> +To receive NDEF message or Secure Element intents, an application must declare +the NFC_NOTIFY permission. +</p> +<p> +To receive the LLCP link intent and perform an LLCP communication with a remote NFC target, an application must +declare the NFC_LLCP permission. +</p> + + +<h1>NFC Usage</h1> + +<p> +The following code samples illustrate the APIs usage regarding the NFC service +use cases. +</p> + +<h2>Reader/Writer Mode NDEF message notification</h2> + +<p> +This code sample illustrates the NDEF message notification through an Intent declared in the manifest and a receiver implemented in the application. +</p> +<p>Main involved classes/methods:</p> + +<p>Manifest Example:</p> +<pre> + <receiver android:name=".NfcReaderDemoReceiver"> + <intent-filter> + <action android:name= "com.trustedlogic.trustednfc.android.action.NDEF_TAG_DISCOVERED"/> + </intent-filter> + </receiver> +</pre> + +<p>Receiver Example:</p> +<ul> + <li>{@link com.trustedlogic.trustednfc.android.NdefMessage}</li> + <li>{@link com.trustedlogic.trustednfc.android.NfcManager#NDEF_TAG_DISCOVERED_ACTION}</li> + <li>{@link com.trustedlogic.trustednfc.android.NfcManager#NDEF_MESSAGE_EXTRA}</li> +</ul> +<pre> +public class NdefMessageReceiverSample extends BroadcastReceiver { + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(NfcManager.NDEF_TAG_DISCOVERERD_ACTION)) { + NdefMessage msg = intent.getParcelableExtra(NfcManager.NDEF_MESSAGE_EXTRA); + + /* Manage the NdefMessage received */ + } +</pre> + +<h2>Reader/Writer Mode raw exchange</h2> + +<p> +This code sample illustrates raw exchanges with a NFC target in Reader/Writer +mode. +</p> +<p>Main involved classes/methods:</p> +<ul> + <li>{@link com.trustedlogic.trustednfc.android.NfcManager#openTagConnection}</li> + <li>{@link com.trustedlogic.trustednfc.android.NfcTag}</li> +</ul> + +<pre> +public class TagReaderSample { + + /** The NFC manager to access NFC features */ + private NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE); + + private void runTagReader() { + NfcTag tag = null; + String type; + byte[] cmd = { 0x01, 0x02, 0x03 }; + byte[] res; + + while (true) { + try { + Log.i("NFC example", "Please wave in front of the tag"); + // Open a connection on next available tag + try { + tag = manager.openTagConnection(); + } catch (NfcException e) { + // TODO: Handle open failure + } + + // Look for a mifare 4k + type = tag.getType(); + if (type.equals("Mifare4K")) { + Log.i("NFC example", "Tag detected"); + tag.connect(); + // Ready to communicate, we can send transceive ! + res = tag.transceive(cmd); + } else { + Log.i("NFC example", "Unknown tag"); + } + } catch (IOException e) { + // TODO: Handle broken connection + } finally { + if (tag != null) { + tag.close(); + } + } + } + } +} +</pre> + +<h2>Peer-to-Peer Mode raw exchange</h2> + +<p> +This code sample illustrates raw exchanges with a NFC target in Peer-to-Peer +mode. +</p> +<p>Main involved classes/methods:</p> +<ul> + <li>{@link com.trustedlogic.trustednfc.android.NfcManager#openP2pConnection}</li> + <li>{@link com.trustedlogic.trustednfc.android.P2pDevice}</li> + <li>{@link com.trustedlogic.trustednfc.android.P2pInitiator}</li> + <li>{@link com.trustedlogic.trustednfc.android.P2pTarget}</li> +</ul> + +<pre> +public class P2pSample { + + /** The NFC manager to access NFC features */ + private NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE); + + private void runP2p() { + P2pDevice deviceP2p; + P2pInitiator initiator; + P2pTarget target; + byte[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + byte[] echo = new byte[data.length * 10]; + + try { + deviceP2p = manager.openP2pConnection(); + + if (deviceP2p.getMode() == P2pDevice.MODE_P2P_INITIATOR) { + target = new P2pTarget(deviceP2p); + // Connect to the detected P2P target + target.connect(); + // send data to the target + target.transceive(data); + // disconnect the connected target + target.disconnect(); + } else if (deviceP2p.getMode() == P2pDevice.MODE_P2P_TARGET) { + initiator = new P2pInitiator(deviceP2p); + //target in receive state + echo = initiator.receive(); + // send back the data received + initiator.send(echo); + } + } catch (IOException e0) { + + } catch (NfcException e1) { + + } + } +} +</pre> + +<h2>Peer-to-Peer Mode LLCP exchange</h2> + +<p> +This code sample illustrates how to get LLCP link state notification with the declaration of a Receiver in the manifest of the application and the implementation +of the receiver in the application. +</p> +<p>Manifest Example:</p> +<pre> + <receiver android:name=".LlcpModeReceiverSample"> + <intent-filter> + <action android:name= "com.trustedlogic.trustednfc.android.action.LLCP_LINK_STATE_CHANGED"/> + </intent-filter> + </receiver> +</pre> + +<p>Receiver Example:</p> +<ul> + <li>{@link com.trustedlogic.trustednfc.android.NfcManager#LLCP_LINK_STATE_CHANGED_ACTION}</li> + <li>{@link com.trustedlogic.trustednfc.android.NfcManager#LLCP_LINK_STATE_CHANGED_EXTRA}</li> +</ul> +<pre> +public class LlcpModeReceiverSample extends BroadcastReceiver { + public void onReceive(Context context, Intent intent) { + + if (intent.getAction().equals(NfcManager.LLCP_LINK_STATE_CHANGED_ACTION)){ + byte[] aid = intent.getByteArrayExtra(NfcManager.LLCP_LINK_STATE_CHANGED_EXTRA); + /* Create an LLCP service or client and start an LLCP communication */ + } + } +</pre> + + +<p> +This code samples illustrate LLCP exchanges with a NFC Peer. +</p> +<p>Main involved classes/methods:</p> +<ul> + <li>{@link com.trustedlogic.trustednfc.android.NfcManager#createLlcpSocket}</li> + <li>{@link com.trustedlogic.trustednfc.android.NfcManager#createLlcpConnectionlessSocket}</li> + <li>{@link com.trustedlogic.trustednfc.android.NfcManager#createLlcpServiceSocket}</li> + <li>{@link com.trustedlogic.trustednfc.android.LlcpSocket}</li> + <li>{@link com.trustedlogic.trustednfc.android.LlcpConnectionlessSocket}</li> + <li>{@link com.trustedlogic.trustednfc.android.LlcpPacket}</li> + <li>{@link com.trustedlogic.trustednfc.android.LlcpServiceSocket}</li> +</ul> + +<pre> +public class LlcpServerSample { + + /** The NFC manager to access NFC features */ + private NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE); + + private void runLlcpClient() { + LlcpSocket sock; + byte[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + byte[] echo = new byte[data.length * 10]; + int length = 0; + + sock = manager.createLlcpSocket((short) 128, (byte) 2, 1024); + + // set a timeout in ms for connect request + sock.setConnectTimeout(10); + + try { + // Connect to remote service + // NOTE: could be sock.connect("com.trusted-logic.tnfc.testapp"); + sock.connect((byte) 0x10); + + // Send data + for (int i = 0; i < 10; i++) { + sock.send(data); + } + + // Receive echo + while (length < 10 * data.length) { + length += sock.receive(echo); + } + + } catch (IOException e) { + // TODO: Handle broken connection broken (link down, remote closure + // or connect rejected) or Timeout expired + } + } +} +</pre> + +<pre> +public class LlcpClientSample { + + /** The NFC manager to access NFC features */ + private NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE); + + private void runLlcpClient() { + LlcpSocket sock; + byte[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + byte[] echo = new byte[data.length * 10]; + int length = 0; + + sock = manager.createLlcpSocket((short) 128, (byte) 2, 1024); + try { + // Connect to remote service + // NOTE: could be sock.connect("com.trusted-logic.tnfc.testapp"); + sock.connect((byte) 0x10); + + // Send data + for (int i = 0; i < 10; i++) { + sock.send(data); + } + + // Receive echo + while (length < 10 * data.length) { + length += sock.receive(echo); + } + + } catch (IOException e) { + // TODO: Handle broken connection broken (link down, remote closure + // or connect rejected) + } + } +} +</pre> + +<h2>Card Emulation Mode transaction notification</h2> + +<p> +This code sample illustrates how to get the card emulation notification with the declaration of a Receiver in the manifest of the application and the implementation +of the receiver in the application. +</p> +<p>Manifest Example:</p> +<pre> + <receiver android:name=".NfcReaderDemoReceiver"> + <intent-filter> + <action android:name= "com.trustedlogic.trustednfc.android.action.TRANSACTION_DETECTED"/> + </intent-filter> + </receiver> +</pre> + +<p>Receiver Example:</p> +<ul> + <li>{@link com.trustedlogic.trustednfc.android.NfcManager#TRANSACTION_DETECTED_ACTION}</li> + <li>{@link com.trustedlogic.trustednfc.android.NfcManager#AID_EXTRA}</li> +</ul> +<pre> +public class CardEmulationReceiverSample extends BroadcastReceiver { + public void onReceive(Context context, Intent intent) { + + if (intent.getAction().equals(NfcManager.TRANSACTION_DETECTED_ACTION)){ + byte[] aid = intent.getByteArrayExtra(NfcManager.AID_EXTRA); + /* Manage the AID: */ + /* For example start an activity related to this AID value or display a popup with the AID */ + } + } +</pre> + + + +<h1>Multiple Applications rules</h1> + +<p> +Several LLCP sockets can be created by a single application or by multiple +applications by calling {@link com.trustedlogic.trustednfc.android.NfcManager#createLlcpSocket}, +{@link com.trustedlogic.trustednfc.android.NfcManager#createLlcpConnectionlessSocket} or +{@link com.trustedlogic.trustednfc.android.NfcManager#createLlcpServiceSocket}, provided the local SAP +numbers are differents. +</p> + +<p> +Only one application can open a raw connection by calling +{@link com.trustedlogic.trustednfc.android.NfcManager#openTagConnection} or +{@link com.trustedlogic.trustednfc.android.NfcManager#openP2pConnection}. +While this application has not closed or cancelled its connection, any other +application that attempts to open another raw connection will raise an +exception. +During an open connnection, the card emulation mode is always enabled and +applications are able to receive card emulation intents. +</p> + +<p> +When an application opens a tag connection by calling +{@link com.trustedlogic.trustednfc.android.NfcManager#openTagConnection}, this operation is exclusive, no NDEF message intent are +broadcast while the connection is not closed or canceled. +</p> + +<p> +When an application opens a peer-to-peer connection by calling +{@link com.trustedlogic.trustednfc.android.NfcManager#openP2pConnection}, this operation is exclusive, no LLCP intent are broadcast and LLCP sockets are +disabled while the connection is not closed or canceled. +</p> + + +<h1>NFC Tag types</h1> + +<p> +The {@link com.trustedlogic.trustednfc.android.NfcTag} type returned by +{@link com.trustedlogic.trustednfc.android.NfcTag#getType} indicates the set of +commands supported by the tag. These commands can be used in +{@link com.trustedlogic.trustednfc.android.NfcTag#transceive}. +</p> + +<TABLE BORDER="1"> + <TR><TH> Tag Type </TH><TH> Returned string </TH></TR> + <TR><TD> Jewel/Topaz </TD><TD> Jewel </TD></TR> + <TR><TD> Mifare UltraLight </TD><TD> MifareUL </TD></TR> + <TR><TD> Mifare Standard 1K </TD><TD> Mifare1K </TD></TR> + <TR><TD> Mifare Standard 4K </TD><TD> Mifare4K </TD></TR> + <TR><TD> Mifare DESFIRE </TD><TD> MifareDESFIRE </TD></TR> + <TR><TD> Felica </TD><TD> Felica </TD></TR> + <TR><TD> ISO14443-4 A or B </TD><TD> Iso14443 </TD></TR> + <TR><TD> ISO15693 </TD><TD> Iso15693 </TD></TR> +</TABLE> + +</body> +</html> diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 8c280b4..f4d1b6e 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -193,6 +193,14 @@ LOCAL_SHARED_LIBRARIES := \ libwpa_client \ libjpeg +ifeq ($(BOARD_HAVE_NFC),true) +LOCAL_SHARED_LIBRARIES += \ + libnfc_jni \ + libnfc + +LOCAL_CFLAGS += -DHAVE_NFC +endif + ifeq ($(BOARD_HAVE_BLUETOOTH),true) LOCAL_C_INCLUDES += \ external/dbus \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index beb49c8..e5d5d8a 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -170,6 +170,18 @@ extern int register_android_view_MotionEvent(JNIEnv* env); extern int register_android_content_res_ObbScanner(JNIEnv* env); extern int register_android_content_res_Configuration(JNIEnv* env); +#ifdef HAVE_NFC +extern int register_com_trustedlogic_trustednfc_android_internal_NativeNfcManager(JNIEnv *env); +extern int register_com_trustedlogic_trustednfc_android_internal_NativeNfcTag(JNIEnv *env); +extern int register_com_trustedlogic_trustednfc_android_internal_NativeNdefTag(JNIEnv *env); +extern int register_com_trustedlogic_trustednfc_android_NdefMessage(JNIEnv *env); +extern int register_com_trustedlogic_trustednfc_android_NdefRecord(JNIEnv *env); +extern int register_com_trustedlogic_trustednfc_android_internal_NativeP2pDevice(JNIEnv *env); +extern int register_com_trustedlogic_trustednfc_android_internal_NativeLlcpSocket(JNIEnv *env); +extern int register_com_trustedlogic_trustednfc_android_internal_NativeLlcpConnectionlessSocket(JNIEnv *env); +extern int register_com_trustedlogic_trustednfc_android_internal_NativeLlcpServiceSocket(JNIEnv *env); +#endif + static AndroidRuntime* gCurRuntime = NULL; static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) @@ -1287,6 +1299,18 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_res_ObbScanner), REG_JNI(register_android_content_res_Configuration), + +#ifdef HAVE_NFC + REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeNfcManager), + REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeNfcTag), + REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeNdefTag), + REG_JNI(register_com_trustedlogic_trustednfc_android_NdefMessage), + REG_JNI(register_com_trustedlogic_trustednfc_android_NdefRecord), + REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeP2pDevice), + REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeLlcpSocket), + REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeLlcpConnectionlessSocket), + REG_JNI(register_com_trustedlogic_trustednfc_android_internal_NativeLlcpServiceSocket), +#endif }; /* diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp index 62c89fc..2a9eacf 100644 --- a/core/jni/android_content_res_ObbScanner.cpp +++ b/core/jni/android_content_res_ObbScanner.cpp @@ -34,7 +34,17 @@ static struct { jfieldID flags; } gObbInfoClassInfo; -static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file, +static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) +{ + jclass npeClazz; + + npeClazz = env->FindClass(exc); + LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc); + + env->ThrowNew(npeClazz, msg); +} + +static void android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file, jobject obbInfo) { const char* filePath = env->GetStringUTFChars(file, JNI_FALSE); @@ -42,7 +52,8 @@ static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject c sp<ObbFile> obb = new ObbFile(); if (!obb->readFrom(filePath)) { env->ReleaseStringUTFChars(file, filePath); - return JNI_FALSE; + doThrow(env, "java/io/IOException", "Could not read OBB file"); + return; } env->ReleaseStringUTFChars(file, filePath); @@ -51,13 +62,13 @@ static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject c jstring packageName = env->NewStringUTF(packageNameStr); if (packageName == NULL) { - return JNI_FALSE; + doThrow(env, "java/io/IOException", "Could not read OBB file"); + return; } env->SetObjectField(obbInfo, gObbInfoClassInfo.packageName, packageName); env->SetIntField(obbInfo, gObbInfoClassInfo.version, obb->getVersion()); - - return JNI_TRUE; + env->SetIntField(obbInfo, gObbInfoClassInfo.flags, obb->getFlags()); } /* @@ -65,7 +76,7 @@ static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject c */ static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ - { "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)Z", + { "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)V", (void*) android_content_res_ObbScanner_getObbInfo }, }; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 68a5a14..b9eb5d6 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -86,6 +86,10 @@ <protected-broadcast android:name="android.hardware.action.USB_DISCONNECTED" /> <protected-broadcast android:name="android.hardware.action.USB_STATE" /> + <protected-broadcast android:name="com.trustedlogic.trustednfc.android.action.NDEF_TAG_DISCOVERED" /> + <protected-broadcast android:name="com.trustedlogic.trustednfc.android.action.TRANSACTION_DETECTED" /> + <protected-broadcast android:name="com.trustedlogic.trustednfc.android.action.LLCP_LINK_STATE_CHANGED" /> + <!-- ====================================== --> <!-- Permissions for things that cost money --> <!-- ====================================== --> @@ -336,6 +340,30 @@ android:description="@string/permdesc_bluetooth" android:label="@string/permlab_bluetooth" /> + <!-- Allows applications to access remote NFC devices + @hide --> + <permission android:name="com.trustedlogic.trustednfc.permission.NFC_RAW" + android:permissionGroup="android.permission-group.NETWORK" + android:protectionLevel="dangerous" + android:description="@string/permdesc_nfcRaw" + android:label="@string/permlab_nfcRaw" /> + + <!-- Allows applications to be notified of remote NFC devices + @hide --> + <permission android:name="com.trustedlogic.trustednfc.permission.NFC_NOTIFY" + android:permissionGroup="android.permission-group.NETWORK" + android:protectionLevel="dangerous" + android:description="@string/permdesc_nfcNotify" + android:label="@string/permlab_nfcNotify" /> + + <!-- Allows applications to be notified of remote NFC LLCP devices + @hide --> + <permission android:name="com.trustedlogic.trustednfc.permission.NFC_LLCP" + android:permissionGroup="android.permission-group.NETWORK" + android:protectionLevel="dangerous" + android:description="@string/permdesc_nfcLlcp" + android:label="@string/permlab_nfcLlcp" /> + <!-- Allows applications to call into AccountAuthenticators. Only the system can get this permission. --> <permission android:name="android.permission.ACCOUNT_MANAGER" @@ -839,6 +867,14 @@ android:description="@string/permdesc_bluetoothAdmin" android:label="@string/permlab_bluetoothAdmin" /> + <!-- Allows applications to change NFC connectivity settings + @hide --> + <permission android:name="com.trustedlogic.trustednfc.permission.NFC_ADMIN" + android:permissionGroup="android.permission-group.SYSTEM_TOOLS" + android:protectionLevel="dangerous" + android:description="@string/permdesc_nfcAdmin" + android:label="@string/permlab_nfcAdmin" /> + <!-- Allows an application to clear the caches of all installed applications on the device. --> <permission android:name="android.permission.CLEAR_APP_CACHE" diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png Binary files differindex 9fb43b8..c21b24e 100644 --- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png +++ b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png Binary files differindex e26a083..87007a3 100644 --- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png +++ b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png diff --git a/core/res/res/drawable-hdpi/stat_notify_car_mode.png b/core/res/res/drawable-hdpi/stat_notify_car_mode.png Binary files differindex 60c3778..e700d79 100755..100644 --- a/core/res/res/drawable-hdpi/stat_notify_car_mode.png +++ b/core/res/res/drawable-hdpi/stat_notify_car_mode.png diff --git a/core/res/res/drawable-hdpi/stat_notify_email_generic.png b/core/res/res/drawable-hdpi/stat_notify_email_generic.png Binary files differindex 87f0cd4..bc5fcab 100644 --- a/core/res/res/drawable-hdpi/stat_notify_email_generic.png +++ b/core/res/res/drawable-hdpi/stat_notify_email_generic.png diff --git a/core/res/res/drawable-hdpi/stat_notify_error.png b/core/res/res/drawable-hdpi/stat_notify_error.png Binary files differindex 37c8853..b3a18b3 100644..100755 --- a/core/res/res/drawable-hdpi/stat_notify_error.png +++ b/core/res/res/drawable-hdpi/stat_notify_error.png diff --git a/core/res/res/drawable-hdpi/stat_notify_gmail.png b/core/res/res/drawable-hdpi/stat_notify_gmail.png Binary files differindex 8a9140c..ea8beae 100644 --- a/core/res/res/drawable-hdpi/stat_notify_gmail.png +++ b/core/res/res/drawable-hdpi/stat_notify_gmail.png diff --git a/core/res/res/drawable-hdpi/stat_notify_missed_call.png b/core/res/res/drawable-hdpi/stat_notify_missed_call.png Binary files differindex d1173b4..3c19c93 100755..100644 --- a/core/res/res/drawable-hdpi/stat_notify_missed_call.png +++ b/core/res/res/drawable-hdpi/stat_notify_missed_call.png diff --git a/core/res/res/drawable-hdpi/stat_notify_more.png b/core/res/res/drawable-hdpi/stat_notify_more.png Binary files differindex 1c7f9db..f54b3d4 100755 --- a/core/res/res/drawable-hdpi/stat_notify_more.png +++ b/core/res/res/drawable-hdpi/stat_notify_more.png diff --git a/core/res/res/drawable-hdpi/stat_notify_sdcard_usb.png b/core/res/res/drawable-hdpi/stat_notify_sdcard_usb.png Binary files differindex a5e369e..fb2b26a 100644..100755 --- a/core/res/res/drawable-hdpi/stat_notify_sdcard_usb.png +++ b/core/res/res/drawable-hdpi/stat_notify_sdcard_usb.png diff --git a/core/res/res/drawable-hdpi/stat_notify_sync_error.png b/core/res/res/drawable-hdpi/stat_notify_sync_error.png Binary files differindex 6e3b545..26b2446 100755 --- a/core/res/res/drawable-hdpi/stat_notify_sync_error.png +++ b/core/res/res/drawable-hdpi/stat_notify_sync_error.png diff --git a/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png b/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png Binary files differindex e9405dd..0458124 100755..100644 --- a/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png +++ b/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index ebccfb6..d9177e7 100644..100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -381,8 +381,10 @@ <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_storage">Storage</string> + <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] --> + <string name="permgroupdesc_storage" product="nosdcard">Access the shared storage.</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permgroupdesc_storage">Access the SD card.</string> + <string name="permgroupdesc_storage" product="default">Access the SD card.</string> <!-- Permissions --> @@ -1151,6 +1153,30 @@ connections with paired devices.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_nfcAdmin">NFC administration</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_nfcAdmin">Allows an application to configure + the local NFC phone.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_nfcRaw">NFC full access to remote device</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_nfcRaw">Allows an application to access + remote NFC devices.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_nfcNotify">NFC notification from remote device</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_nfcNotify">Allows an application to be notified + of operations related to remote NFC devices.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_nfcLlcp">NFC notification from remote LLCP device</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_nfcLlcp">Allows an application to be notified + of LLCP operations related to remote NFC devices.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_disableKeyguard">disable keylock</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_disableKeyguard">Allows an application to disable @@ -1200,10 +1226,14 @@ <string name="permdesc_writeDictionary">Allows an application to write new words into the user dictionary.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] --> + <string name="permlab_sdcardWrite" product="nosdcard">modify/delete shared storage contents</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permlab_sdcardWrite">modify/delete SD card contents</string> + <string name="permlab_sdcardWrite" product="default">modify/delete SD card contents</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] --> + <string name="permdesc_sdcardWrite" product="nosdcard">Allows an application to write to the shared storage.</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_sdcardWrite">Allows an application to write to the SD card.</string> + <string name="permdesc_sdcardWrite" product="default">Allows an application to write to the SD card.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_cache_filesystem">access the cache filesystem</string> @@ -1213,31 +1243,28 @@ <!-- Policy administration --> <!-- Title of policy access to limiting the user's password choices --> - <string name="policylab_limitPassword">Limit password</string> + <string name="policylab_limitPassword">Set password rules</string> <!-- Description of policy access to limiting the user's password choices --> - <string name="policydesc_limitPassword">Restrict the types of passwords you - are allowed to use.</string> + <string name="policydesc_limitPassword">Control the length and the characters allowed in + screen-unlock passwords</string> <!-- Title of policy access to watch user login attempts --> - <string name="policylab_watchLogin">Watch login attempts</string> + <string name="policylab_watchLogin">Monitor screen-unlock attempts</string> <!-- Description of policy access to watch user login attempts --> - <string name="policydesc_watchLogin">Monitor failed attempts to login to - the device, to perform some action.</string> + <string name="policydesc_watchLogin">Monitor the number of incorrect passwords entered when unlocking + the screen, and lock the phone or erase all the phone\'s data if too many incorrect passwords are entered + </string> <!-- Title of policy access to reset user's password --> - <string name="policylab_resetPassword">Reset password</string> + <string name="policylab_resetPassword">Change the screen-unlock password</string> <!-- Description of policy access to reset user's password --> - <string name="policydesc_resetPassword">Force your password - to a new value, requiring the administrator give it to you - before you can log in.</string> + <string name="policydesc_resetPassword">Change the screen-unlock password</string> <!-- Title of policy access to force lock the device --> - <string name="policylab_forceLock">Force lock</string> + <string name="policylab_forceLock">Lock the screen</string> <!-- Description of policy access to limiting the user's password choices --> - <string name="policydesc_forceLock">Control when device locks, - requiring you re-enter its password.</string> + <string name="policydesc_forceLock">Control how and when the screen locks</string> <!-- Title of policy access to wipe the user's data --> <string name="policylab_wipeData">Erase all data</string> <!-- Description of policy access to wipe the user's data --> - <string name="policydesc_wipeData">Perform a factory reset, deleting - all of your data without any confirmation.</string> + <string name="policydesc_wipeData">Erase the phone\'s data without warning, by performing a factory data reset</string> <!-- The order of these is important, don't reorder without changing Contacts.java --> <skip /> <!-- Phone number types from android.provider.Contacts. This could be used when adding a new phone number for a contact, for example. --> @@ -2058,12 +2085,16 @@ <!-- See USB_STORAGE. USB_STORAGE_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to mount. This is the title. --> <string name="usb_storage_title">USB connected</string> + <!-- See USB_STORAGE. This is the message. [CHAR LIMIT=NONE] --> + <string name="usb_storage_message" product="nosdcard">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s shared storage.</string> <!-- See USB_STORAGE. This is the message. --> - <string name="usb_storage_message">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s SD card.</string> + <string name="usb_storage_message" product="default">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s SD card.</string> <!-- See USB_STORAGE. This is the button text to mount the phone on the computer. --> <string name="usb_storage_button_mount">Turn on USB storage</string> + <!-- See USB_STORAGE_DIALOG. If there was an error mounting, this is the text. [CHAR LIMIT=NONE] --> + <string name="usb_storage_error_message" product="nosdcard">There is a problem using your shared storage for USB storage.</string> <!-- See USB_STORAGE_DIALOG. If there was an error mounting, this is the text. --> - <string name="usb_storage_error_message">There is a problem using your SD card for USB storage.</string> + <string name="usb_storage_error_message" product="default">There is a problem using your SD card for USB storage.</string> <!-- USB_STORAGE: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across. This is the title --> <string name="usb_storage_notification_title">USB connected</string> <!-- See USB_STORAGE. This is the message. --> @@ -2078,8 +2109,10 @@ <!-- This is the label for the activity, and should never be visible to the user. --> <!-- See USB_STORAGE_STOP. USB_STORAGE_STOP_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to stop usb storage. This is the title. --> <string name="usb_storage_stop_title">USB storage in use</string> + <!-- See USB_STORAGE_STOP. This is the message. [CHAR LIMIT=NONE] --> + <string name="usb_storage_stop_message" product="nosdcard">Before turning off USB storage, make sure you have unmounted (\u201cejected\u201d) your Android\u2018s shared storage from your computer.</string> <!-- See USB_STORAGE_STOP. This is the message. --> - <string name="usb_storage_stop_message">Before turning off USB storage, make sure you have unmounted (\u201cejected\u201d) your Android\u2018s SD card from your computer.</string> + <string name="usb_storage_stop_message" product="default">Before turning off USB storage, make sure you have unmounted (\u201cejected\u201d) your Android\u2018s SD card from your computer.</string> <!-- See USB_STORAGE_STOP. This is the button text to stop usb storage. --> <string name="usb_storage_stop_button_mount">Turn off USB storage</string> <!-- See USB_STORAGE_STOP_DIALOG. If there was an error stopping, this is the text. --> @@ -2096,10 +2129,14 @@ <!-- External media format dialog strings --> <!-- This is the label for the activity, and should never be visible to the user. --> + <!-- See EXTMEDIA_FORMAT. EXTMEDIA_FORMAT_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to format the SD card. This is the title. [CHAR LIMIT=20] --> + <string name="extmedia_format_title" product="nosdcard">Format shared storage</string> <!-- See EXTMEDIA_FORMAT. EXTMEDIA_FORMAT_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to format the SD card. This is the title. --> - <string name="extmedia_format_title">Format SD card</string> + <string name="extmedia_format_title" product="default">Format SD card</string> + <!-- See EXTMEDIA_FORMAT. This is the message. [CHAR LIMIT=NONE] --> + <string name="extmedia_format_message" product="nosdcard">Format shared storage, erasing all files stored there? Action cannot be reversed!</string> <!-- See EXTMEDIA_FORMAT. This is the message. --> - <string name="extmedia_format_message">Are you sure you want to format the SD card? All data on your card will be lost.</string> + <string name="extmedia_format_message" product="default">Are you sure you want to format the SD card? All data on your card will be lost.</string> <!-- See EXTMEDIA_FORMAT. This is the button text to format the sd card. --> <string name="extmedia_format_button_format">Format</string> @@ -2124,29 +2161,51 @@ <string name="candidates_style"><u>candidates</u></string> <!-- External media notification strings --> + <!-- Shown when external media is being checked [CHAR LIMIT=30] --> + <string name="ext_media_checking_notification_title" product="nosdcard">Preparing shared storage</string> <!-- Shown when external media is being checked --> - <string name="ext_media_checking_notification_title">Preparing SD card</string> + <string name="ext_media_checking_notification_title" product="default">Preparing SD card</string> <string name="ext_media_checking_notification_message">Checking for errors.</string> + <!-- Shown when external media is blank (or unsupported filesystem) [CHAR LIMIT=30] --> + <string name="ext_media_nofs_notification_title" product="nosdcard">Blank shared storage</string> <!-- Shown when external media is blank (or unsupported filesystem) --> - <string name="ext_media_nofs_notification_title">Blank SD card</string> - <string name="ext_media_nofs_notification_message">SD card blank or has unsupported filesystem.</string> + <string name="ext_media_nofs_notification_title" product="default">Blank SD card</string> + <!-- Shown when shared storage cannot be read. [CHAR LIMIT=NONE] --> + <string name="ext_media_nofs_notification_message" product="nosdcard">Shared storage blank or has unsupported filesystem.</string> + <string name="ext_media_nofs_notification_message" product="default">SD card blank or has unsupported filesystem.</string> + <!-- Shown when external media is unmountable (corrupt)) [CHAR LIMIT=30] --> + <string name="ext_media_unmountable_notification_title" product="nosdcard">Damaged shared storage</string> <!-- Shown when external media is unmountable (corrupt)) --> - <string name="ext_media_unmountable_notification_title">Damaged SD card</string> - <string name="ext_media_unmountable_notification_message">SD card damaged. You may have to reformat it.</string> + <string name="ext_media_unmountable_notification_title" product="default">Damaged SD card</string> + <!-- Shown when shared storage cannot be read. [CHAR LIMIT=NONE] --> + <string name="ext_media_unmountable_notification_message" product="nosdcard">Shared storage damaged. You may have to reformat it.</string> + <string name="ext_media_unmountable_notification_message" product="default">SD card damaged. You may have to reformat it.</string> + <!-- Shown when external media is unsafely removed [CHAR LIMIT=30] --> + <string name="ext_media_badremoval_notification_title" product="nosdcard">Shared storage unexpectedly removed</string> <!-- Shown when external media is unsafely removed --> - <string name="ext_media_badremoval_notification_title">SD card unexpectedly removed</string> - <string name="ext_media_badremoval_notification_message">Unmount SD card before removing to avoid data loss.</string> + <string name="ext_media_badremoval_notification_title" product="default">SD card unexpectedly removed</string> + <!-- Shown when external media is unsafely removed. [CHAR LIMIT=NONE] --> + <string name="ext_media_badremoval_notification_message" product="nosdcard">Unmount shared storage before removing to avoid data loss.</string> + <string name="ext_media_badremoval_notification_message" product="default">Unmount SD card before removing to avoid data loss.</string> + <!-- Shown when external media has been safely removed [CHAR LIMIT=30] --> + <string name="ext_media_safe_unmount_notification_title" product="nosdcard">Shared storage safe to remove</string> <!-- Shown when external media has been safely removed --> - <string name="ext_media_safe_unmount_notification_title">SD card safe to remove</string> - <string name="ext_media_safe_unmount_notification_message">You can safely remove SD card.</string> + <string name="ext_media_safe_unmount_notification_title" product="default">SD card safe to remove</string> + <!-- Shown when external media has been safely removed. [CHAR LIMIT=NONE] --> + <string name="ext_media_safe_unmount_notification_message" product="nosdcard">You can safely remove shared storage.</string> + <string name="ext_media_safe_unmount_notification_message" product="default">You can safely remove SD card.</string> + <!-- Shown when external media is missing [CHAR LIMIT=30] --> + <string name="ext_media_nomedia_notification_title" product="nosdcard">Removed shared storage</string> <!-- Shown when external media is missing --> - <string name="ext_media_nomedia_notification_title">Removed SD card</string> - <string name="ext_media_nomedia_notification_message">SD card removed. Insert a new one.</string> + <string name="ext_media_nomedia_notification_title" product="default">Removed SD card</string> + <!-- Shown when external media is missing. [CHAR LIMIT=NONE] --> + <string name="ext_media_nomedia_notification_message" product="nosdcard">Shared storage removed. Insert new media.</string> + <string name="ext_media_nomedia_notification_message" product="default">SD card removed. Insert a new one.</string> <!-- Shown in LauncherActivity when the requested target Intent didn't return any matching Activities, leaving the list empty. --> <string name="activity_list_empty">No matching activities found</string> diff --git a/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java index a7ec7d5..ed42e64 100644 --- a/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java +++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java @@ -14,18 +14,18 @@ * limitations under the License. */ -package android.net; +package android.app; +import android.app.DownloadManager.Query; +import android.app.DownloadManager.Request; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; import android.net.ConnectivityManager; -import android.net.DownloadManager; import android.net.NetworkInfo; -import android.net.DownloadManager.Query; -import android.net.DownloadManager.Request; +import android.net.Uri; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Environment; diff --git a/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java b/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java index a61f02d..38f336e 100644 --- a/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java +++ b/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java @@ -14,18 +14,18 @@ * limitations under the License. */ -package android.net; +package android.app; +import android.app.DownloadManager.Query; +import android.app.DownloadManager.Request; +import android.app.DownloadManagerBaseTest.DataType; +import android.app.DownloadManagerBaseTest.MultipleDownloadsCompletedReceiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; -import android.net.DownloadManager; -import android.net.DownloadManager.Query; -import android.net.DownloadManager.Request; -import android.net.DownloadManagerBaseTest.DataType; -import android.net.DownloadManagerBaseTest.MultipleDownloadsCompletedReceiver; +import android.net.Uri; import android.net.wifi.WifiManager; import android.os.Environment; import android.os.ParcelFileDescriptor; diff --git a/core/tests/coretests/src/android/net/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java index 9fa8620..4ff0295 100644 --- a/core/tests/coretests/src/android/net/DownloadManagerStressTest.java +++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java @@ -14,14 +14,15 @@ * limitations under the License. */ -package android.net; +package android.app; import java.io.File; import java.util.Random; +import android.app.DownloadManager.Query; +import android.app.DownloadManager.Request; import android.database.Cursor; -import android.net.DownloadManager.Query; -import android.net.DownloadManager.Request; +import android.net.Uri; import android.os.ParcelFileDescriptor; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk index 576765c..7206f4a 100644 --- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk +++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk @@ -19,7 +19,7 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) \ - ../../../coretests/src/android/net/DownloadManagerBaseTest.java + ../../../coretests/src/android/app/DownloadManagerBaseTest.java LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib LOCAL_SDK_VERSION := current diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java index f21e7ac..c0f670b 100644 --- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java +++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java @@ -15,13 +15,13 @@ */ package com.android.frameworks.downloadmanagertests; +import android.app.DownloadManager; +import android.app.DownloadManager.Query; +import android.app.DownloadManager.Request; +import android.app.DownloadManagerBaseTest; import android.content.Context; import android.content.Intent; import android.database.Cursor; -import android.net.DownloadManager; -import android.net.DownloadManager.Query; -import android.net.DownloadManager.Request; -import android.net.DownloadManagerBaseTest; import android.net.Uri; import android.os.Environment; import android.os.ParcelFileDescriptor; diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h index 9a09586..ed2f7d7 100644 --- a/include/media/stagefright/AudioPlayer.h +++ b/include/media/stagefright/AudioPlayer.h @@ -27,6 +27,7 @@ namespace android { class MediaSource; class AudioTrack; +class AwesomePlayer; class AudioPlayer : public TimeSource { public: @@ -35,7 +36,9 @@ public: SEEK_COMPLETE }; - AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink); + AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink, + AwesomePlayer *audioObserver = NULL); + virtual ~AudioPlayer(); // Caller retains ownership of "source". @@ -91,6 +94,7 @@ private: MediaBuffer *mFirstBuffer; sp<MediaPlayerBase::AudioSink> mAudioSink; + AwesomePlayer *mObserver; static void AudioCallback(int event, void *user, void *info); void AudioCallback(int event, void *info); diff --git a/include/media/stagefright/MPEG2TSWriter.h b/include/media/stagefright/MPEG2TSWriter.h new file mode 100644 index 0000000..551ca01 --- /dev/null +++ b/include/media/stagefright/MPEG2TSWriter.h @@ -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. + */ + +#ifndef MPEG2TS_WRITER_H_ + +#define MPEG2TS_WRITER_H_ + +#include <media/stagefright/foundation/ABase.h> +#include <media/stagefright/foundation/AHandlerReflector.h> +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/MediaWriter.h> + +namespace android { + +struct MPEG2TSWriter : public MediaWriter { + MPEG2TSWriter(const char *filename); + + virtual status_t addSource(const sp<MediaSource> &source); + virtual status_t start(MetaData *param = NULL); + virtual status_t stop(); + virtual status_t pause(); + virtual bool reachedEOS(); + virtual status_t dump(int fd, const Vector<String16>& args); + + void onMessageReceived(const sp<AMessage> &msg); + +protected: + virtual ~MPEG2TSWriter(); + +private: + enum { + kWhatSourceNotify = 'noti' + }; + + struct SourceInfo; + + FILE *mFile; + sp<ALooper> mLooper; + sp<AHandlerReflector<MPEG2TSWriter> > mReflector; + + bool mStarted; + + Vector<sp<SourceInfo> > mSources; + size_t mNumSourcesDone; + + int64_t mNumTSPacketsWritten; + int64_t mNumTSPacketsBeforeMeta; + + void writeTS(); + void writeProgramAssociationTable(); + void writeProgramMap(); + void writeAccessUnit(int32_t sourceIndex, const sp<ABuffer> &buffer); + + DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSWriter); +}; + +} // namespace android + +#endif // MPEG2TS_WRITER_H_ diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index 1594e31..ab2f11d 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -95,6 +95,8 @@ enum { // Ogg files can be tagged to be automatically looping... kKeyAutoLoop = 'autL', // bool (int32_t) + + kKeyValidSamples = 'valD', // int32_t }; enum { diff --git a/include/storage/IMountService.h b/include/storage/IMountService.h index a2735a4..436fc38 100644 --- a/include/storage/IMountService.h +++ b/include/storage/IMountService.h @@ -62,7 +62,8 @@ public: virtual void finishMediaUpdate() = 0; virtual void mountObb(const String16& filename, const String16& key, const sp<IObbActionListener>& token) = 0; - virtual void unmountObb(const String16& filename, const bool force) = 0; + virtual void unmountObb(const String16& filename, const bool force, + const sp<IObbActionListener>& token) = 0; virtual bool isObbMounted(const String16& filename) = 0; virtual bool getMountedObbPath(const String16& filename, String16& path) = 0; }; diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp index 902bb27..3ad9319 100644 --- a/libs/storage/IMountService.cpp +++ b/libs/storage/IMountService.cpp @@ -429,8 +429,8 @@ public: reply.readExceptionCode(); } - void mountObb(const String16& filename, const String16& key, const sp< - IObbActionListener>& token) + void mountObb(const String16& filename, const String16& key, + const sp<IObbActionListener>& token) { Parcel data, reply; data.writeInterfaceToken(IMountService::getInterfaceDescriptor()); @@ -448,7 +448,7 @@ public: } } - void unmountObb(const String16& filename, const bool force) + void unmountObb(const String16& filename, const bool force, const sp<IObbActionListener>& token) { Parcel data, reply; data.writeInterfaceToken(IMountService::getInterfaceDescriptor()); diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index 5da1676..9544a95 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -1070,7 +1070,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } // Figure out whether splitting will be allowed for this window. - if (newTouchedWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH) { + if (newTouchedWindow + && (newTouchedWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH)) { // New window supports splitting. isSplit = true; } else if (isSplit) { @@ -1916,6 +1917,14 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet // The first/last pointer went down/up. action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; + } else { + // A secondary pointer went down/up. + uint32_t splitPointerIndex = 0; + while (pointerId != splitPointerIds[splitPointerIndex]) { + splitPointerIndex += 1; + } + action = maskedAction | (splitPointerIndex + << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); } } else { // An unrelated pointer changed. diff --git a/location/Android.mk b/location/Android.mk new file mode 100644 index 0000000..12db2f7 --- /dev/null +++ b/location/Android.mk @@ -0,0 +1,19 @@ +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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) + +ifeq ($(TARGET_BUILD_APPS),) +include $(call all-makefiles-under, $(LOCAL_PATH)) +endif diff --git a/location/lib/Android.mk b/location/lib/Android.mk new file mode 100644 index 0000000..a06478a --- /dev/null +++ b/location/lib/Android.mk @@ -0,0 +1,45 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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) + +# the library +# ============================================================ +include $(CLEAR_VARS) + +LOCAL_MODULE:= com.android.location.provider +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := \ + $(call all-subdir-java-files) + +include $(BUILD_JAVA_LIBRARY) + + +# ==== com.google.location.xml lib def ======================== +include $(CLEAR_VARS) + +LOCAL_MODULE := com.android.location.provider.xml +LOCAL_MODULE_TAGS := optional + +LOCAL_MODULE_CLASS := ETC + +# This will install the file in /system/etc/permissions +# +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions + +LOCAL_SRC_FILES := $(LOCAL_MODULE) + +include $(BUILD_PREBUILT) diff --git a/location/lib/com.android.location.provider.xml b/location/lib/com.android.location.provider.xml new file mode 100644 index 0000000..000e68f --- /dev/null +++ b/location/lib/com.android.location.provider.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<permissions> + <library name="com.android.location.provider" + file="/system/framework/com.android.location.provider.jar" /> +</permissions> diff --git a/location/java/android/location/provider/GeocodeProvider.java b/location/lib/java/com/android/location/provider/GeocodeProvider.java index 493c631..666bb02 100644 --- a/location/java/android/location/provider/GeocodeProvider.java +++ b/location/lib/java/com/android/location/provider/GeocodeProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.location.provider; +package com.android.location.provider; import android.os.IBinder; @@ -80,4 +80,3 @@ public abstract class GeocodeProvider { return mProvider; } } - diff --git a/location/java/android/location/provider/LocationProvider.java b/location/lib/java/com/android/location/provider/LocationProvider.java index 14dea14..3714f40 100644 --- a/location/java/android/location/provider/LocationProvider.java +++ b/location/lib/java/com/android/location/provider/LocationProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.location.provider; +package com.android.location.provider; import android.content.Context; import android.net.NetworkInfo; @@ -356,4 +356,3 @@ public abstract class LocationProvider { */ public abstract void onRemoveListener(int uid, WorkSource ws); } - diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index 8f40130..ebe3302 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -63,7 +63,6 @@ namespace { // Flag to allow a one time init of global memory, only happens on first call ever int LvmInitFlag = LVM_FALSE; -int LvmSessionsActive = 0; SessionContext GlobalSessionMemory[LVM_MAX_SESSIONS]; int SessionIndex[LVM_MAX_SESSIONS]; @@ -189,16 +188,19 @@ extern "C" int EffectCreate(effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_interface_t *pInterface){ - int ret; + int ret = 0; int sessionNo; int i; - EffectContext *pContext = new EffectContext; + EffectContext *pContext = NULL; + bool newBundle = false; + SessionContext *pSessionContext; LOGV("\n\tEffectCreate start session %d", sessionId); if (pInterface == NULL || uuid == NULL){ LOGV("\tLVM_ERROR : EffectCreate() called with NULL pointer"); - return -EINVAL; + ret = -EINVAL; + goto exit; } if(LvmInitFlag == LVM_FALSE){ @@ -207,8 +209,6 @@ extern "C" int EffectCreate(effect_uuid_t *uuid, LvmGlobalBundle_init(); } - LOGV("\tEffectCreate: There are %d LVM sessions acive\n", LvmSessionsActive); - // Find next available sessionNo for(i=0; i<LVM_MAX_SESSIONS; i++){ if((SessionIndex[i] == LVM_UNUSED_SESSION)||(SessionIndex[i] == sessionId)){ @@ -221,23 +221,20 @@ extern "C" int EffectCreate(effect_uuid_t *uuid, if(i==LVM_MAX_SESSIONS){ LOGV("\tLVM_ERROR : Cannot find memory to allocate for current session"); - return -EINVAL; + ret = -EINVAL; + goto exit; } + + pContext = new EffectContext; + // If this is the first create in this session if(GlobalSessionMemory[sessionNo].bBundledEffectsEnabled == LVM_FALSE){ LOGV("\tEffectCreate - This is the first effect in current sessionId %d sessionNo %d", sessionId, sessionNo); - LvmSessionsActive++; - - if(LvmSessionsActive >= LVM_MAX_SESSIONS){ - LOGV("\tLVM_ERROR : Number of active session is greater than LVM_MAX_SESSIONS (%d)", - LVM_MAX_SESSIONS); - return -EINVAL; - } - GlobalSessionMemory[sessionNo].bBundledEffectsEnabled = LVM_TRUE; GlobalSessionMemory[sessionNo].pBundledContext = new BundledEffectContext; + newBundle = true; pContext->pBundledContext = GlobalSessionMemory[sessionNo].pBundledContext; pContext->pBundledContext->SessionNo = sessionNo; @@ -251,17 +248,16 @@ extern "C" int EffectCreate(effect_uuid_t *uuid, pContext->pBundledContext->bVirtualizerTempDisabled = LVM_FALSE; pContext->pBundledContext->NumberEffectsEnabled = 0; pContext->pBundledContext->NumberEffectsCalled = 0; - pContext->pBundledContext->frameCount = 0; pContext->pBundledContext->firstVolume = LVM_TRUE; #ifdef LVM_PCM - char fileName[256]; snprintf(fileName, 256, "/data/tmp/bundle_%p_pcm_in.pcm", pContext->pBundledContext); pContext->pBundledContext->PcmInPtr = fopen(fileName, "w"); if (pContext->pBundledContext->PcmInPtr == NULL) { LOGV("cannot open %s", fileName); - return -EINVAL; + ret = -EINVAL; + goto exit; } snprintf(fileName, 256, "/data/tmp/bundle_%p_pcm_out.pcm", pContext->pBundledContext); @@ -270,7 +266,8 @@ extern "C" int EffectCreate(effect_uuid_t *uuid, LOGV("cannot open %s", fileName); fclose(pContext->pBundledContext->PcmInPtr); pContext->pBundledContext->PcmInPtr = NULL; - return -EINVAL; + ret = -EINVAL; + goto exit; } #endif @@ -285,15 +282,18 @@ extern "C" int EffectCreate(effect_uuid_t *uuid, pContext->pBundledContext->bMuteEnabled = LVM_FALSE; pContext->pBundledContext->bStereoPositionEnabled = LVM_FALSE; pContext->pBundledContext->positionSaved = 0; + pContext->pBundledContext->workBuffer = NULL; + pContext->pBundledContext->frameCount = -1; + pContext->pBundledContext->SamplesToExitCountVirt = 0; + pContext->pBundledContext->SamplesToExitCountBb = 0; + pContext->pBundledContext->SamplesToExitCountEq = 0; LOGV("\tEffectCreate - Calling LvmBundle_init"); ret = LvmBundle_init(pContext); if (ret < 0){ LOGV("\tLVM_ERROR : EffectCreate() Bundle init failed"); - delete pContext->pBundledContext; - delete pContext; - return ret; + goto exit; } } else{ @@ -304,13 +304,14 @@ extern "C" int EffectCreate(effect_uuid_t *uuid, } LOGV("\tEffectCreate - pBundledContext is %p", pContext->pBundledContext); - SessionContext *pSessionContext = &GlobalSessionMemory[pContext->pBundledContext->SessionNo]; + pSessionContext = &GlobalSessionMemory[pContext->pBundledContext->SessionNo]; // Create each Effect if (memcmp(uuid, &gBassBoostDescriptor.uuid, sizeof(effect_uuid_t)) == 0){ // Create Bass Boost LOGV("\tEffectCreate - Effect to be created is LVM_BASS_BOOST"); pSessionContext->bBassInstantiated = LVM_TRUE; + pContext->pBundledContext->SamplesToExitCountBb = 0; pContext->itfe = &gLvmEffectInterface; pContext->EffectType = LVM_BASS_BOOST; @@ -318,6 +319,7 @@ extern "C" int EffectCreate(effect_uuid_t *uuid, // Create Virtualizer LOGV("\tEffectCreate - Effect to be created is LVM_VIRTUALIZER"); pSessionContext->bVirtualizerInstantiated=LVM_TRUE; + pContext->pBundledContext->SamplesToExitCountVirt = 0; pContext->itfe = &gLvmEffectInterface; pContext->EffectType = LVM_VIRTUALIZER; @@ -325,6 +327,7 @@ extern "C" int EffectCreate(effect_uuid_t *uuid, // Create Equalizer LOGV("\tEffectCreate - Effect to be created is LVM_EQUALIZER"); pSessionContext->bEqualizerInstantiated = LVM_TRUE; + pContext->pBundledContext->SamplesToExitCountEq = 0; pContext->itfe = &gLvmEffectInterface; pContext->EffectType = LVM_EQUALIZER; @@ -338,46 +341,77 @@ extern "C" int EffectCreate(effect_uuid_t *uuid, } else{ LOGV("\tLVM_ERROR : EffectCreate() invalid UUID"); - return -EINVAL; + ret = -EINVAL; + goto exit; } - *pInterface = (effect_interface_t)pContext; +exit: + if (ret != 0) { + if (pContext != NULL) { + if (newBundle) { + GlobalSessionMemory[sessionNo].bBundledEffectsEnabled = LVM_FALSE; + SessionIndex[sessionNo] = LVM_UNUSED_SESSION; + delete pContext->pBundledContext; + } + delete pContext; + } + *pInterface = (effect_interface_t)NULL; + } else { + *pInterface = (effect_interface_t)pContext; + } LOGV("\tEffectCreate end..\n\n"); - return 0; + return ret; } /* end EffectCreate */ extern "C" int EffectRelease(effect_interface_t interface){ LOGV("\n\tEffectRelease start %p", interface); EffectContext * pContext = (EffectContext *)interface; - LOGV("\n\tEffectRelease start interface: %p, context %p", interface, pContext->pBundledContext); + LOGV("\tEffectRelease start interface: %p, context %p", interface, pContext->pBundledContext); if (pContext == NULL){ LOGV("\tLVM_ERROR : EffectRelease called with NULL pointer"); return -EINVAL; } - - Effect_setEnabled(pContext, LVM_FALSE); - SessionContext *pSessionContext = &GlobalSessionMemory[pContext->pBundledContext->SessionNo]; // Clear the instantiated flag for the effect + // protect agains the case where an effect is un-instantiated without being disabled if(pContext->EffectType == LVM_BASS_BOOST) { LOGV("\tEffectRelease LVM_BASS_BOOST Clearing global intstantiated flag"); pSessionContext->bBassInstantiated = LVM_FALSE; + if(pContext->pBundledContext->SamplesToExitCountBb > 0){ + pContext->pBundledContext->NumberEffectsEnabled--; + } + pContext->pBundledContext->SamplesToExitCountBb = 0; } else if(pContext->EffectType == LVM_VIRTUALIZER) { LOGV("\tEffectRelease LVM_VIRTUALIZER Clearing global intstantiated flag"); pSessionContext->bVirtualizerInstantiated = LVM_FALSE; + if(pContext->pBundledContext->SamplesToExitCountVirt > 0){ + pContext->pBundledContext->NumberEffectsEnabled--; + } + pContext->pBundledContext->SamplesToExitCountVirt = 0; } else if(pContext->EffectType == LVM_EQUALIZER) { LOGV("\tEffectRelease LVM_EQUALIZER Clearing global intstantiated flag"); pSessionContext->bEqualizerInstantiated =LVM_FALSE; + if(pContext->pBundledContext->SamplesToExitCountEq > 0){ + pContext->pBundledContext->NumberEffectsEnabled--; + } + pContext->pBundledContext->SamplesToExitCountEq = 0; } else if(pContext->EffectType == LVM_VOLUME) { LOGV("\tEffectRelease LVM_VOLUME Clearing global intstantiated flag"); pSessionContext->bVolumeInstantiated = LVM_FALSE; + if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE){ + pContext->pBundledContext->NumberEffectsEnabled--; + } } else { LOGV("\tLVM_ERROR : EffectRelease : Unsupported effect\n\n\n\n\n\n\n"); } + // Disable effect, in this case ignore errors (return codes) + // if an effect has already been disabled + Effect_setEnabled(pContext, LVM_FALSE); + // if all effects are no longer instantiaed free the lvm memory and delete BundledEffectContext if ((pSessionContext->bBassInstantiated == LVM_FALSE) && (pSessionContext->bVolumeInstantiated == LVM_FALSE) && @@ -395,8 +429,6 @@ extern "C" int EffectRelease(effect_interface_t interface){ } #endif - LvmSessionsActive--; - LOGV("\tEffectRelease: There are %d LVM sessions remaining\n", LvmSessionsActive); // Clear the SessionIndex for(int i=0; i<LVM_MAX_SESSIONS; i++){ @@ -409,11 +441,14 @@ extern "C" int EffectRelease(effect_interface_t interface){ } LOGV("\tEffectRelease: All effects are no longer instantiated\n"); - pSessionContext->bBundledEffectsEnabled =LVM_FALSE; + pSessionContext->bBundledEffectsEnabled = LVM_FALSE; pSessionContext->pBundledContext = LVM_NULL; LOGV("\tEffectRelease: Freeing LVM Bundle memory\n"); LvmEffect_free(pContext); LOGV("\tEffectRelease: Deleting LVM Bundle context %p\n", pContext->pBundledContext); + if (pContext->pBundledContext->workBuffer != NULL) { + free(pContext->pBundledContext->workBuffer); + } delete pContext->pBundledContext; pContext->pBundledContext = LVM_NULL; } @@ -643,6 +678,14 @@ int LvmBundle_init(EffectContext *pContext){ return 0; } /* end LvmBundle_init */ + +static inline int16_t clamp16(int32_t sample) +{ + if ((sample>>15) ^ (sample>>31)) + sample = 0x7FFF ^ (sample>>31); + return sample; +} + //---------------------------------------------------------------------------- // LvmBundle_process() //---------------------------------------------------------------------------- @@ -668,39 +711,25 @@ int LvmBundle_process(LVM_INT16 *pIn, LVM_ControlParams_t ActiveParams; /* Current control Parameters */ LVM_ReturnStatus_en LvmStatus = LVM_SUCCESS; /* Function call status */ - LVM_INT16 *pOutTmp; + if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE){ pOutTmp = pOut; }else if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){ - pOutTmp = (LVM_INT16 *)malloc(frameCount * sizeof(LVM_INT16) * 2); - if(pOutTmp == NULL){ - LOGV("\tLVM_ERROR : LvmBundle_process failed to allocate memory for " - "EFFECT_BUFFER_ACCESS_ACCUMULATE mode"); - return -EINVAL; + if (pContext->pBundledContext->frameCount != frameCount) { + if (pContext->pBundledContext->workBuffer != NULL) { + free(pContext->pBundledContext->workBuffer); + } + pContext->pBundledContext->workBuffer = + (LVM_INT16 *)malloc(frameCount * sizeof(LVM_INT16) * 2); + pContext->pBundledContext->frameCount = frameCount; } + pOutTmp = pContext->pBundledContext->workBuffer; }else{ LOGV("LVM_ERROR : LvmBundle_process invalid access mode"); return -EINVAL; } - /* Get the current settings */ - LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance, - &ActiveParams); - - LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "LvmBundle_process") - if(LvmStatus != LVM_SUCCESS) return -EINVAL; - - pContext->pBundledContext->frameCount++; - if(pContext->pBundledContext->frameCount == 100) - { - //LOGV("\tBB: %d VIRT: %d EQ: %d, session (%d), context is %p\n", - //ActiveParams.BE_OperatingMode, - //ActiveParams.VirtualizerOperatingMode, ActiveParams.EQNB_OperatingMode, - //pContext->pBundledContext->SessionNo, pContext->pBundledContext); - pContext->pBundledContext->frameCount = 0; - } - #ifdef LVM_PCM fwrite(pIn, frameCount*sizeof(LVM_INT16)*2, 1, pContext->pBundledContext->PcmInPtr); fflush(pContext->pBundledContext->PcmInPtr); @@ -725,9 +754,8 @@ int LvmBundle_process(LVM_INT16 *pIn, if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){ for (int i=0; i<frameCount*2; i++){ - pOut[i] += pOutTmp[i]; + pOut[i] = clamp16((LVM_INT32)pOut[i] + (LVM_INT32)pOutTmp[i]); } - free(pOutTmp); } return 0; } /* end LvmBundle_process */ @@ -813,15 +841,15 @@ int LvmEffect_disable(EffectContext *pContext){ ActiveParams.BE_OperatingMode = LVM_BE_OFF; } if(pContext->EffectType == LVM_VIRTUALIZER) { - LOGV("\tLvmEffect_disable : Enabling LVM_VIRTUALIZER"); + LOGV("\tLvmEffect_disable : Disabling LVM_VIRTUALIZER"); ActiveParams.VirtualizerOperatingMode = LVM_MODE_OFF; } if(pContext->EffectType == LVM_EQUALIZER) { - LOGV("\tLvmEffect_disable : Enabling LVM_EQUALIZER"); + LOGV("\tLvmEffect_disable : Disabling LVM_EQUALIZER"); ActiveParams.EQNB_OperatingMode = LVM_EQNB_OFF; } if(pContext->EffectType == LVM_VOLUME) { - LOGV("\tLvmEffect_disable : Enabling LVM_VOLUME"); + LOGV("\tLvmEffect_disable : Disabling LVM_VOLUME"); } LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams); @@ -2406,85 +2434,114 @@ int Effect_setEnabled(EffectContext *pContext, bool enabled) switch (pContext->EffectType) { case LVM_BASS_BOOST: if (pContext->pBundledContext->bBassEnabled == LVM_TRUE) { - LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_BASS_BOOST is already enabled"); + LOGV("\tEffect_setEnabled() LVM_BASS_BOOST is already enabled"); return -EINVAL; } + if(pContext->pBundledContext->SamplesToExitCountBb <= 0){ + pContext->pBundledContext->NumberEffectsEnabled++; + } pContext->pBundledContext->SamplesToExitCountBb = (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1); pContext->pBundledContext->bBassEnabled = LVM_TRUE; break; case LVM_EQUALIZER: if (pContext->pBundledContext->bEqualizerEnabled == LVM_TRUE) { - LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_EQUALIZER is already enabled"); + LOGV("\tEffect_setEnabled() LVM_EQUALIZER is already enabled"); return -EINVAL; } + if(pContext->pBundledContext->SamplesToExitCountEq <= 0){ + pContext->pBundledContext->NumberEffectsEnabled++; + } pContext->pBundledContext->SamplesToExitCountEq = (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1); pContext->pBundledContext->bEqualizerEnabled = LVM_TRUE; break; case LVM_VIRTUALIZER: if (pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE) { - LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VIRTUALIZER is already enabled"); + LOGV("\tEffect_setEnabled() LVM_VIRTUALIZER is already enabled"); return -EINVAL; } + if(pContext->pBundledContext->SamplesToExitCountVirt <= 0){ + pContext->pBundledContext->NumberEffectsEnabled++; + } pContext->pBundledContext->SamplesToExitCountVirt = (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1); pContext->pBundledContext->bVirtualizerEnabled = LVM_TRUE; break; case LVM_VOLUME: if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE) { - LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VOLUME is already enabled"); + LOGV("\tEffect_setEnabled() LVM_VOLUME is already enabled"); return -EINVAL; } + pContext->pBundledContext->NumberEffectsEnabled++; pContext->pBundledContext->bVolumeEnabled = LVM_TRUE; break; default: - LOGV("\tLVM_ERROR : Effect_setEnabled() invalid effect type"); + LOGV("\tEffect_setEnabled() invalid effect type"); return -EINVAL; } - pContext->pBundledContext->NumberEffectsEnabled++; LvmEffect_enable(pContext); } else { switch (pContext->EffectType) { case LVM_BASS_BOOST: if (pContext->pBundledContext->bBassEnabled == LVM_FALSE) { - LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_BASS_BOOST is already disabled"); + LOGV("\tEffect_setEnabled() LVM_BASS_BOOST is already disabled"); return -EINVAL; } pContext->pBundledContext->bBassEnabled = LVM_FALSE; break; case LVM_EQUALIZER: if (pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE) { - LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_EQUALIZER is already disabled"); + LOGV("\tEffect_setEnabled() LVM_EQUALIZER is already disabled"); return -EINVAL; } pContext->pBundledContext->bEqualizerEnabled = LVM_FALSE; break; case LVM_VIRTUALIZER: if (pContext->pBundledContext->bVirtualizerEnabled == LVM_FALSE) { - LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VIRTUALIZER is already disabled"); + LOGV("\tEffect_setEnabled() LVM_VIRTUALIZER is already disabled"); return -EINVAL; } pContext->pBundledContext->bVirtualizerEnabled = LVM_FALSE; break; case LVM_VOLUME: if (pContext->pBundledContext->bVolumeEnabled == LVM_FALSE) { - LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VOLUME is already disabled"); + LOGV("\tEffect_setEnabled() LVM_VOLUME is already disabled"); return -EINVAL; } pContext->pBundledContext->bVolumeEnabled = LVM_FALSE; break; default: - LOGV("\tLVM_ERROR : Effect_setEnabled() invalid effect type"); + LOGV("\tEffect_setEnabled() invalid effect type"); return -EINVAL; } - pContext->pBundledContext->NumberEffectsEnabled--; LvmEffect_disable(pContext); } return 0; } +//---------------------------------------------------------------------------- +// LVC_Convert_VolToDb() +//---------------------------------------------------------------------------- +// Purpose: +// Convery volume in Q24 to dB +// +// Inputs: +// vol: Q.24 volume dB +// +//----------------------------------------------------------------------- + +int16_t LVC_Convert_VolToDb(uint32_t vol){ + int16_t dB; + + dB = LVC_ToDB_s32Tos16(vol <<7); + dB = (dB +8)>>4; + dB = (dB <-96) ? -96 : dB ; + + return dB; +} + } // namespace } // namespace @@ -2493,32 +2550,31 @@ extern "C" int Effect_process(effect_interface_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer){ EffectContext * pContext = (EffectContext *) self; - LVM_ControlParams_t ActiveParams; /* Current control Parameters */ LVM_ReturnStatus_en LvmStatus = LVM_SUCCESS; /* Function call status */ int status = 0; - int status2Sec = 0; int lvmStatus = 0; LVM_INT16 *in = (LVM_INT16 *)inBuffer->raw; LVM_INT16 *out = (LVM_INT16 *)outBuffer->raw; -//LOGV("\tEffect_process Start : Enabled = %d Called = %d", -//pContext->pBundledContext->NumberEffectsEnabled,pContext->pBundledContext->NumberEffectsCalled); -// LOGV("\tEffect_process Start : Samples left %d %d %d", +//LOGV("\tEffect_process Start : Enabled = %d Called = %d (%8d %8d %8d)", +//pContext->pBundledContext->NumberEffectsEnabled,pContext->pBundledContext->NumberEffectsCalled, // pContext->pBundledContext->SamplesToExitCountBb, // pContext->pBundledContext->SamplesToExitCountVirt, // pContext->pBundledContext->SamplesToExitCountEq); -// LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams); -// LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "VolumeGetStereoPosition") -// if(LvmStatus != LVM_SUCCESS) return -EINVAL; -// LOGV("\tEffect_process Internal Operating Modes: BB %d VIRT %d EQ %d", -// ActiveParams.BE_OperatingMode, ActiveParams.VirtualizerOperatingMode, -// ActiveParams.EQNB_OperatingMode); - if (pContext == NULL){ LOGV("\tLVM_ERROR : Effect_process() ERROR pContext == NULL"); return -EINVAL; } + + //if(pContext->EffectType == LVM_BASS_BOOST){ + // LOGV("\tEffect_process: Effect type is BASS_BOOST"); + //}else if(pContext->EffectType == LVM_EQUALIZER){ + // LOGV("\tEffect_process: Effect type is LVM_EQUALIZER"); + //}else if(pContext->EffectType == LVM_VIRTUALIZER){ + // LOGV("\tEffect_process: Effect type is LVM_VIRTUALIZER"); + //} + if (inBuffer == NULL || inBuffer->raw == NULL || outBuffer == NULL || outBuffer->raw == NULL || inBuffer->frameCount != outBuffer->frameCount){ @@ -2529,70 +2585,57 @@ extern "C" int Effect_process(effect_interface_t self, (pContext->EffectType == LVM_BASS_BOOST)){ //LOGV("\tEffect_process() LVM_BASS_BOOST Effect is not enabled"); if(pContext->pBundledContext->SamplesToExitCountBb > 0){ - status2Sec = -ENODATA; pContext->pBundledContext->SamplesToExitCountBb -= outBuffer->frameCount * 2; // STEREO //LOGV("\tEffect_process: Waiting to turn off BASS_BOOST, %d samples left", // pContext->pBundledContext->SamplesToExitCountBb); } else { status = -ENODATA; + pContext->pBundledContext->NumberEffectsEnabled--; } } if ((pContext->pBundledContext->bVolumeEnabled == LVM_FALSE)&& (pContext->EffectType == LVM_VOLUME)){ //LOGV("\tEffect_process() LVM_VOLUME Effect is not enabled"); status = -ENODATA; + pContext->pBundledContext->NumberEffectsEnabled--; } if ((pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE)&& (pContext->EffectType == LVM_EQUALIZER)){ //LOGV("\tEffect_process() LVM_EQUALIZER Effect is not enabled"); if(pContext->pBundledContext->SamplesToExitCountEq > 0){ - status2Sec = -ENODATA; pContext->pBundledContext->SamplesToExitCountEq -= outBuffer->frameCount * 2; // STEREO - //LOGV("\tEffect_process: Waiting for 2 secs to turn off EQUALIZER, %d samples left", + //LOGV("\tEffect_process: Waiting to turn off EQUALIZER, %d samples left", // pContext->pBundledContext->SamplesToExitCountEq); } else { status = -ENODATA; + pContext->pBundledContext->NumberEffectsEnabled--; } } if ((pContext->pBundledContext->bVirtualizerEnabled == LVM_FALSE)&& (pContext->EffectType == LVM_VIRTUALIZER)){ //LOGV("\tEffect_process() LVM_VIRTUALIZER Effect is not enabled"); if(pContext->pBundledContext->SamplesToExitCountVirt > 0){ - status2Sec = -ENODATA; pContext->pBundledContext->SamplesToExitCountVirt -= outBuffer->frameCount * 2;// STEREO - //LOGV("\tEffect_process: Waiting for 2 secs to turn off VIRTUALIZER, %d samples left", + //LOGV("\tEffect_process: Waiting for to turn off VIRTUALIZER, %d samples left", // pContext->pBundledContext->SamplesToExitCountVirt); } else { status = -ENODATA; + pContext->pBundledContext->NumberEffectsEnabled--; } } - // If this is the last frame of an effect process its output with no effect - if(status == -ENODATA){ - if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){ - //LOGV("\tLVM_ERROR : Effect_process() accumulating last frame into output buffer"); - //LOGV("\tLVM_ERROR : Effect_process() trying copying last frame into output buffer"); - //LOGV("\tLVM_ERROR : Enabled = %d Called = %d", - //pContext->pBundledContext->NumberEffectsEnabled, - //pContext->pBundledContext->NumberEffectsCalled); - - }else{ - //LOGV("\tLVM_ERROR : Effect_process() copying last frame into output buffer"); - } - } - - if((status2Sec != -ENODATA)&&(status != -ENODATA)){ + if(status != -ENODATA){ pContext->pBundledContext->NumberEffectsCalled++; } if(pContext->pBundledContext->NumberEffectsCalled == pContext->pBundledContext->NumberEffectsEnabled){ - //LOGV("\tEffect_process Calling process with %d effects enabled, %d called: Effect %d", + //LOGV("\tEffect_process Calling process with %d effects enabled, %d called: Effect %d", //pContext->pBundledContext->NumberEffectsEnabled, //pContext->pBundledContext->NumberEffectsCalled, pContext->EffectType); if(status == -ENODATA){ - //LOGV("\tLVM_ERROR : Effect_process() actually processing last frame"); + LOGV("\tEffect_process() processing last frame"); } pContext->pBundledContext->NumberEffectsCalled = 0; /* Process all the available frames, block processing is @@ -2836,10 +2879,10 @@ extern "C" int Effect_command(effect_interface_t self, case EFFECT_CMD_SET_PARAM:{ //LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_SET_PARAM start"); if(pContext->EffectType == LVM_BASS_BOOST){ - //LOGV("\tBassBoost_command EFFECT_CMD_SET_PARAM param %d, *replySize %d, value %d ", - // *(int32_t *)((char *)pCmdData + sizeof(effect_param_t)), - // *replySize, - // *(int16_t *)((char *)pCmdData + sizeof(effect_param_t) + sizeof(int32_t))); + //LOGV("\tBassBoost_command EFFECT_CMD_SET_PARAM param %d, *replySize %d, value %d", + // *(int32_t *)((char *)pCmdData + sizeof(effect_param_t)), + // *replySize, + // *(int16_t *)((char *)pCmdData + sizeof(effect_param_t) + sizeof(int32_t))); if (pCmdData == NULL|| cmdSize != (int)(sizeof(effect_param_t) + sizeof(int32_t) +sizeof(int16_t))|| @@ -3038,30 +3081,71 @@ extern "C" int Effect_command(effect_interface_t self, } case EFFECT_CMD_SET_VOLUME: { - int32_t vol = *(int32_t *)pCmdData; - int16_t dB; - int32_t vol_ret[2] = {1<<24,1<<24}; // Apply no volume + uint32_t leftVolume, rightVolume; + int16_t leftdB, rightdB; + int16_t maxdB, pandB; + int32_t vol_ret[2] = {1<<24,1<<24}; // Apply no volume + int status = 0; + LVM_ControlParams_t ActiveParams; /* Current control Parameters */ + LVM_ReturnStatus_en LvmStatus=LVM_SUCCESS; /* Function call status */ // if pReplyData is NULL, VOL_CTRL is delegated to another effect if(pReplyData == LVM_NULL){ break; } - if(vol==0x1000000){ - vol -= 1; + if (pCmdData == NULL || + cmdSize != 2 * sizeof(uint32_t)) { + LOGV("\tLVM_ERROR : Effect_command cmdCode Case: " + "EFFECT_CMD_SET_VOLUME: ERROR"); + return -EINVAL; } - // Convert volume linear (Q8.24) to volume dB (0->-96) - dB = android::LVC_ToDB_s32Tos16(vol <<7); - dB = (dB +8)>>4; - dB = (dB <-96) ? -96 : dB ; - LOGV("\tEFFECT_CMD_SET_VOLUME Session: %d, SessionID: %d VOLUME is %d dB (%d), " - "effect is %d", - pContext->pBundledContext->SessionNo, pContext->pBundledContext->SessionId, - (int32_t)dB, vol<<7, pContext->EffectType); + leftVolume = ((*(uint32_t *)pCmdData)); + rightVolume = ((*((uint32_t *)pCmdData + 1))); + + if(leftVolume == 0x1000000){ + leftVolume -= 1; + } + if(rightVolume == 0x1000000){ + rightVolume -= 1; + } + + // Convert volume to dB + leftdB = android::LVC_Convert_VolToDb(leftVolume); + rightdB = android::LVC_Convert_VolToDb(rightVolume); + + pandB = rightdB - leftdB; + + // Calculate max volume in dB + maxdB = leftdB; + if(rightdB > maxdB){ + maxdB = rightdB; + } + //LOGV("\tEFFECT_CMD_SET_VOLUME Session: %d, SessionID: %d VOLUME is %d dB (%d), " + // "effect is %d", + //pContext->pBundledContext->SessionNo, pContext->pBundledContext->SessionId, + //(int32_t)maxdB, maxVol<<7, pContext->EffectType); + //LOGV("\tEFFECT_CMD_SET_VOLUME: Left is %d, Right is %d", leftVolume, rightVolume); + //LOGV("\tEFFECT_CMD_SET_VOLUME: Left %ddB, Right %ddB, Position %ddB", + // leftdB, rightdB, pandB); memcpy(pReplyData, vol_ret, sizeof(int32_t)*2); - android::VolumeSetVolumeLevel(pContext, (int16_t)(dB*100)); + android::VolumeSetVolumeLevel(pContext, (int16_t)(maxdB*100)); + + /* Get the current settings */ + LvmStatus =LVM_GetControlParameters(pContext->pBundledContext->hInstance,&ActiveParams); + LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "VolumeSetStereoPosition") + if(LvmStatus != LVM_SUCCESS) return -EINVAL; + + /* Volume parameters */ + ActiveParams.VC_Balance = pandB; + LOGV("\t\tVolumeSetStereoPosition() (-96dB -> +96dB)-> %d\n", ActiveParams.VC_Balance ); + + /* Activate the initial settings */ + LvmStatus =LVM_SetControlParameters(pContext->pBundledContext->hInstance,&ActiveParams); + LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "VolumeSetStereoPosition") + if(LvmStatus != LVM_SUCCESS) return -EINVAL; break; } case EFFECT_CMD_SET_AUDIO_MODE: diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h index 91963af..2b51029 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h @@ -88,12 +88,13 @@ struct BundledEffectContext{ int positionSaved; bool bMuteEnabled; /* Must store as mute = -96dB level */ bool bStereoPositionEnabled; - int frameCount; LVM_Fs_en SampleRate; int SamplesPerSecond; int SamplesToExitCountEq; int SamplesToExitCountBb; int SamplesToExitCountVirt; + LVM_INT16 *workBuffer; + int frameCount; #ifdef LVM_PCM FILE *PcmInPtr; FILE *PcmOutPtr; diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index aba5f52..d975cb9 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -534,8 +534,9 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) if (f) { while (!feof(f)) { fgets(buffer, SIZE, f); - if (strstr(buffer, " /sdcard/") || + if (strstr(buffer, " /mnt/sdcard/") || strstr(buffer, " /system/sounds/") || + strstr(buffer, " /data/") || strstr(buffer, " /system/media/")) { result.append(" "); result.append(buffer); @@ -569,8 +570,9 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) } else { linkto[len] = 0; } - if (strstr(linkto, "/sdcard/") == linkto || + if (strstr(linkto, "/mnt/sdcard/") == linkto || strstr(linkto, "/system/sounds/") == linkto || + strstr(linkto, "/data/") == linkto || strstr(linkto, "/system/media/") == linkto) { result.append(" "); result.append(buffer); diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index e0321a5..3e17a7e 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -16,6 +16,7 @@ LOCAL_SRC_FILES:= \ HTTPStream.cpp \ JPEGSource.cpp \ MP3Extractor.cpp \ + MPEG2TSWriter.cpp \ MPEG4Extractor.cpp \ MPEG4Writer.cpp \ MediaBuffer.cpp \ diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index c27cfc8..b314114 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -27,9 +27,13 @@ #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> +#include "include/AwesomePlayer.h" + namespace android { -AudioPlayer::AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink) +AudioPlayer::AudioPlayer( + const sp<MediaPlayerBase::AudioSink> &audioSink, + AwesomePlayer *observer) : mAudioTrack(NULL), mInputBuffer(NULL), mSampleRate(0), @@ -45,7 +49,8 @@ AudioPlayer::AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink) mIsFirstBuffer(false), mFirstBufferResult(OK), mFirstBuffer(NULL), - mAudioSink(audioSink) { + mAudioSink(audioSink), + mObserver(observer) { } AudioPlayer::~AudioPlayer() { @@ -301,6 +306,9 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { } mSeeking = false; + if (mObserver) { + mObserver->postAudioSeekComplete(); + } } } @@ -323,6 +331,10 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { Mutex::Autolock autoLock(mLock); if (err != OK) { + if (mObserver && !mReachedEOS) { + mObserver->postAudioEOS(); + } + mReachedEOS = true; mFinalStatus = err; break; @@ -411,6 +423,12 @@ status_t AudioPlayer::seekTo(int64_t time_us) { mReachedEOS = false; mSeekTimeUs = time_us; + if (mAudioSink != NULL) { + mAudioSink->flush(); + } else { + mAudioTrack->flush(); + } + return OK; } diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 8507afc..12022bd 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -639,7 +639,7 @@ status_t AwesomePlayer::play_l() { if (mAudioSource != NULL) { if (mAudioPlayer == NULL) { if (mAudioSink != NULL) { - mAudioPlayer = new AudioPlayer(mAudioSink); + mAudioPlayer = new AudioPlayer(mAudioSink, this); mAudioPlayer->setSource(mAudioSource); // We've already started the MediaSource in order to enable @@ -666,8 +666,6 @@ status_t AwesomePlayer::play_l() { } else { mAudioPlayer->resume(); } - - postCheckAudioStatusEvent_l(); } if (mTimeSource == NULL && mAudioPlayer == NULL) { @@ -1169,7 +1167,7 @@ void AwesomePlayer::postCheckAudioStatusEvent_l() { return; } mAudioStatusEventPending = true; - mQueue.postEventWithDelay(mCheckAudioStatusEvent, 100000ll); + mQueue.postEvent(mCheckAudioStatusEvent); } void AwesomePlayer::onCheckAudioStatus() { @@ -1200,8 +1198,6 @@ void AwesomePlayer::onCheckAudioStatus() { mFlags |= FIRST_FRAME; postStreamDoneEvent_l(finalStatus); } - - postCheckAudioStatusEvent_l(); } status_t AwesomePlayer::prepare() { @@ -1662,5 +1658,13 @@ uint32_t AwesomePlayer::flags() const { return mExtractorFlags; } +void AwesomePlayer::postAudioEOS() { + postCheckAudioStatusEvent_l(); +} + +void AwesomePlayer::postAudioSeekComplete() { + postCheckAudioStatusEvent_l(); +} + } // namespace android diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp new file mode 100644 index 0000000..ee74b88 --- /dev/null +++ b/media/libstagefright/MPEG2TSWriter.cpp @@ -0,0 +1,758 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "MPEG2TSWriter" +#include <media/stagefright/foundation/ADebug.h> + +#include <media/stagefright/foundation/hexdump.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MPEG2TSWriter.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/Utils.h> + +#include "include/ESDS.h" + +namespace android { + +struct MPEG2TSWriter::SourceInfo : public AHandler { + SourceInfo(const sp<MediaSource> &source); + + void start(const sp<AMessage> ¬ify); + void stop(); + + unsigned streamType() const; + unsigned incrementContinuityCounter(); + + enum { + kNotifyStartFailed, + kNotifyBuffer, + kNotifyReachedEOS, + }; + +protected: + virtual void onMessageReceived(const sp<AMessage> &msg); + + virtual ~SourceInfo(); + +private: + enum { + kWhatStart = 'strt', + kWhatRead = 'read', + }; + + sp<MediaSource> mSource; + sp<ALooper> mLooper; + sp<AMessage> mNotify; + + sp<ABuffer> mAACBuffer; + + unsigned mStreamType; + unsigned mContinuityCounter; + + void extractCodecSpecificData(); + + void appendAACFrames(MediaBuffer *buffer); + void flushAACFrames(); + + void postAVCFrame(MediaBuffer *buffer); + + DISALLOW_EVIL_CONSTRUCTORS(SourceInfo); +}; + +MPEG2TSWriter::SourceInfo::SourceInfo(const sp<MediaSource> &source) + : mSource(source), + mLooper(new ALooper), + mStreamType(0), + mContinuityCounter(0) { + mLooper->setName("MPEG2TSWriter source"); + + sp<MetaData> meta = mSource->getFormat(); + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { + mStreamType = 0x0f; + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + mStreamType = 0x1b; + } else { + TRESPASS(); + } +} + +MPEG2TSWriter::SourceInfo::~SourceInfo() { +} + +unsigned MPEG2TSWriter::SourceInfo::streamType() const { + return mStreamType; +} + +unsigned MPEG2TSWriter::SourceInfo::incrementContinuityCounter() { + if (++mContinuityCounter == 16) { + mContinuityCounter = 0; + } + + return mContinuityCounter; +} + +void MPEG2TSWriter::SourceInfo::start(const sp<AMessage> ¬ify) { + mLooper->registerHandler(this); + mLooper->start(); + + mNotify = notify; + + (new AMessage(kWhatStart, id()))->post(); +} + +void MPEG2TSWriter::SourceInfo::stop() { + mLooper->unregisterHandler(id()); + mLooper->stop(); +} + +void MPEG2TSWriter::SourceInfo::extractCodecSpecificData() { + sp<MetaData> meta = mSource->getFormat(); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + return; + } + + sp<ABuffer> out = new ABuffer(1024); + out->setRange(0, 0); + + uint32_t type; + const void *data; + size_t size; + CHECK(meta->findData(kKeyAVCC, &type, &data, &size)); + + const uint8_t *ptr = (const uint8_t *)data; + + size_t numSeqParameterSets = ptr[5] & 31; + + ptr += 6; + size -= 6; + + for (size_t i = 0; i < numSeqParameterSets; ++i) { + CHECK(size >= 2); + size_t length = U16_AT(ptr); + + ptr += 2; + size -= 2; + + CHECK(size >= length); + + CHECK_LE(out->size() + 4 + length, out->capacity()); + memcpy(out->data() + out->size(), "\x00\x00\x00\x01", 4); + memcpy(out->data() + out->size() + 4, ptr, length); + out->setRange(0, out->size() + length + 4); + + ptr += length; + size -= length; + } + + CHECK(size >= 1); + size_t numPictureParameterSets = *ptr; + ++ptr; + --size; + + for (size_t i = 0; i < numPictureParameterSets; ++i) { + CHECK(size >= 2); + size_t length = U16_AT(ptr); + + ptr += 2; + size -= 2; + + CHECK(size >= length); + + CHECK_LE(out->size() + 4 + length, out->capacity()); + memcpy(out->data() + out->size(), "\x00\x00\x00\x01", 4); + memcpy(out->data() + out->size() + 4, ptr, length); + out->setRange(0, out->size() + length + 4); + + ptr += length; + size -= length; + } + + out->meta()->setInt64("timeUs", 0ll); + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kNotifyBuffer); + notify->setObject("buffer", out); + notify->post(); +} + +void MPEG2TSWriter::SourceInfo::postAVCFrame(MediaBuffer *buffer) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kNotifyBuffer); + + sp<ABuffer> copy = + new ABuffer(buffer->range_length()); + memcpy(copy->data(), + (const uint8_t *)buffer->data() + + buffer->range_offset(), + buffer->range_length()); + + int64_t timeUs; + CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); + copy->meta()->setInt64("timeUs", timeUs); + + int32_t isSync; + if (buffer->meta_data()->findInt32(kKeyIsSyncFrame, &isSync) + && isSync != 0) { + copy->meta()->setInt32("isSync", true); + } + + notify->setObject("buffer", copy); + notify->post(); +} + +void MPEG2TSWriter::SourceInfo::appendAACFrames(MediaBuffer *buffer) { + if (mAACBuffer != NULL + && mAACBuffer->size() + 7 + buffer->range_length() + > mAACBuffer->capacity()) { + flushAACFrames(); + } + + if (mAACBuffer == NULL) { + size_t alloc = 4096; + if (buffer->range_length() + 7 > alloc) { + alloc = 7 + buffer->range_length(); + } + + mAACBuffer = new ABuffer(alloc); + + int64_t timeUs; + CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); + + mAACBuffer->meta()->setInt64("timeUs", timeUs); + mAACBuffer->meta()->setInt32("isSync", true); + + mAACBuffer->setRange(0, 0); + } + + sp<MetaData> meta = mSource->getFormat(); + uint32_t type; + const void *data; + size_t size; + CHECK(meta->findData(kKeyESDS, &type, &data, &size)); + + ESDS esds((const char *)data, size); + CHECK_EQ(esds.InitCheck(), (status_t)OK); + + const uint8_t *codec_specific_data; + size_t codec_specific_data_size; + esds.getCodecSpecificInfo( + (const void **)&codec_specific_data, &codec_specific_data_size); + + CHECK_GE(codec_specific_data_size, 2u); + + unsigned profile = (codec_specific_data[0] >> 3) - 1; + + unsigned sampling_freq_index = + ((codec_specific_data[0] & 7) << 1) + | (codec_specific_data[1] >> 7); + + unsigned channel_configuration = + (codec_specific_data[1] >> 3) & 0x0f; + + uint8_t *ptr = mAACBuffer->data() + mAACBuffer->size(); + + const uint32_t aac_frame_length = buffer->range_length() + 7; + + *ptr++ = 0xff; + *ptr++ = 0xf1; // b11110001, ID=0, layer=0, protection_absent=1 + + *ptr++ = + profile << 6 + | sampling_freq_index << 2 + | ((channel_configuration >> 2) & 1); // private_bit=0 + + // original_copy=0, home=0, copyright_id_bit=0, copyright_id_start=0 + *ptr++ = + (channel_configuration & 3) << 6 + | aac_frame_length >> 11; + *ptr++ = (aac_frame_length >> 3) & 0xff; + *ptr++ = (aac_frame_length & 7) << 5; + + // adts_buffer_fullness=0, number_of_raw_data_blocks_in_frame=0 + *ptr++ = 0; + + memcpy(ptr, + (const uint8_t *)buffer->data() + buffer->range_offset(), + buffer->range_length()); + + ptr += buffer->range_length(); + + mAACBuffer->setRange(0, ptr - mAACBuffer->data()); +} + +void MPEG2TSWriter::SourceInfo::flushAACFrames() { + if (mAACBuffer == NULL) { + return; + } + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kNotifyBuffer); + notify->setObject("buffer", mAACBuffer); + notify->post(); + + mAACBuffer.clear(); +} + +void MPEG2TSWriter::SourceInfo::onMessageReceived(const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatStart: + { + status_t err = mSource->start(); + if (err != OK) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kNotifyStartFailed); + notify->post(); + break; + } + + extractCodecSpecificData(); + + (new AMessage(kWhatRead, id()))->post(); + break; + } + + case kWhatRead: + { + MediaBuffer *buffer; + status_t err = mSource->read(&buffer); + + if (err != OK && err != INFO_FORMAT_CHANGED) { + if (mStreamType == 0x0f) { + flushAACFrames(); + } + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kNotifyReachedEOS); + notify->setInt32("status", err); + notify->post(); + break; + } + + if (err == OK) { + if (buffer->range_length() > 0) { + if (mStreamType == 0x0f) { + appendAACFrames(buffer); + } else { + postAVCFrame(buffer); + } + } + + buffer->release(); + buffer = NULL; + } + + msg->post(); + break; + } + + default: + TRESPASS(); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +MPEG2TSWriter::MPEG2TSWriter(const char *filename) + : mFile(fopen(filename, "wb")), + mStarted(false), + mNumSourcesDone(0), + mNumTSPacketsWritten(0), + mNumTSPacketsBeforeMeta(0) { + CHECK(mFile != NULL); + + mLooper = new ALooper; + mLooper->setName("MPEG2TSWriter"); + + mReflector = new AHandlerReflector<MPEG2TSWriter>(this); + + mLooper->registerHandler(mReflector); + mLooper->start(); +} + +MPEG2TSWriter::~MPEG2TSWriter() { + mLooper->unregisterHandler(mReflector->id()); + mLooper->stop(); + + fclose(mFile); + mFile = NULL; +} + +status_t MPEG2TSWriter::addSource(const sp<MediaSource> &source) { + CHECK(!mStarted); + + sp<MetaData> meta = source->getFormat(); + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) + && strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { + return ERROR_UNSUPPORTED; + } + + sp<SourceInfo> info = new SourceInfo(source); + + mSources.push(info); + + return OK; +} + +status_t MPEG2TSWriter::start(MetaData *param) { + CHECK(!mStarted); + + mStarted = true; + mNumSourcesDone = 0; + mNumTSPacketsWritten = 0; + mNumTSPacketsBeforeMeta = 0; + + for (size_t i = 0; i < mSources.size(); ++i) { + sp<AMessage> notify = + new AMessage(kWhatSourceNotify, mReflector->id()); + + notify->setInt32("source-index", i); + + mSources.editItemAt(i)->start(notify); + } + + return OK; +} + +status_t MPEG2TSWriter::stop() { + CHECK(mStarted); + + for (size_t i = 0; i < mSources.size(); ++i) { + mSources.editItemAt(i)->stop(); + } + mStarted = false; + + return OK; +} + +status_t MPEG2TSWriter::pause() { + CHECK(mStarted); + + return OK; +} + +bool MPEG2TSWriter::reachedEOS() { + return !mStarted || (mNumSourcesDone == mSources.size() ? true : false); +} + +status_t MPEG2TSWriter::dump(int fd, const Vector<String16> &args) { + return OK; +} + +void MPEG2TSWriter::onMessageReceived(const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatSourceNotify: + { + int32_t sourceIndex; + CHECK(msg->findInt32("source-index", &sourceIndex)); + + int32_t what; + CHECK(msg->findInt32("what", &what)); + + if (what == SourceInfo::kNotifyReachedEOS + || what == SourceInfo::kNotifyStartFailed) { + ++mNumSourcesDone; + } else if (what == SourceInfo::kNotifyBuffer) { + sp<RefBase> obj; + CHECK(msg->findObject("buffer", &obj)); + + writeTS(); + + sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get()); + writeAccessUnit(sourceIndex, buffer); + } + break; + } + + default: + TRESPASS(); + } +} + +void MPEG2TSWriter::writeProgramAssociationTable() { + // 0x47 + // transport_error_indicator = b0 + // payload_unit_start_indicator = b1 + // transport_priority = b0 + // PID = b0000000000000 (13 bits) + // transport_scrambling_control = b00 + // adaptation_field_control = b01 (no adaptation field, payload only) + // continuity_counter = b???? + // skip = 0x00 + // --- payload follows + // table_id = 0x00 + // section_syntax_indicator = b1 + // must_be_zero = b0 + // reserved = b11 + // section_length = 0x00d + // transport_stream_id = 0x0000 + // reserved = b11 + // version_number = b00001 + // current_next_indicator = b1 + // section_number = 0x00 + // last_section_number = 0x00 + // one program follows: + // program_number = 0x0001 + // reserved = b111 + // program_map_PID = 0x01e0 (13 bits!) + // CRC = 0x???????? + + static const uint8_t kData[] = { + 0x47, + 0x40, 0x00, 0x10, 0x00, // b0100 0000 0000 0000 0001 ???? 0000 0000 + 0x00, 0xb0, 0x0d, 0x00, // b0000 0000 1011 0000 0000 1101 0000 0000 + 0x00, 0xc3, 0x00, 0x00, // b0000 0000 1100 0011 0000 0000 0000 0000 + 0x00, 0x01, 0xe1, 0xe0, // b0000 0000 0000 0001 1110 0001 1110 0000 + 0x00, 0x00, 0x00, 0x00 // b???? ???? ???? ???? ???? ???? ???? ???? + }; + + sp<ABuffer> buffer = new ABuffer(188); + memset(buffer->data(), 0, buffer->size()); + memcpy(buffer->data(), kData, sizeof(kData)); + + static const unsigned kContinuityCounter = 5; + buffer->data()[3] |= kContinuityCounter; + + CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size()); +} + +void MPEG2TSWriter::writeProgramMap() { + // 0x47 + // transport_error_indicator = b0 + // payload_unit_start_indicator = b1 + // transport_priority = b0 + // PID = b0 0001 1110 0000 (13 bits) [0x1e0] + // transport_scrambling_control = b00 + // adaptation_field_control = b01 (no adaptation field, payload only) + // continuity_counter = b???? + // skip = 0x00 + // -- payload follows + // table_id = 0x02 + // section_syntax_indicator = b1 + // must_be_zero = b0 + // reserved = b11 + // section_length = 0x??? + // program_number = 0x0001 + // reserved = b11 + // version_number = b00001 + // current_next_indicator = b1 + // section_number = 0x00 + // last_section_number = 0x00 + // reserved = b111 + // PCR_PID = b? ???? ???? ???? (13 bits) + // reserved = b1111 + // program_info_length = 0x000 + // one or more elementary stream descriptions follow: + // stream_type = 0x?? + // reserved = b111 + // elementary_PID = b? ???? ???? ???? (13 bits) + // reserved = b1111 + // ES_info_length = 0x000 + // CRC = 0x???????? + + static const uint8_t kData[] = { + 0x47, + 0x41, 0xe0, 0x10, 0x00, // b0100 0001 1110 0000 0001 ???? 0000 0000 + 0x02, 0xb0, 0x00, 0x00, // b0000 0010 1011 ???? ???? ???? 0000 0000 + 0x01, 0xc3, 0x00, 0x00, // b0000 0001 1100 0011 0000 0000 0000 0000 + 0xe0, 0x00, 0xf0, 0x00 // b111? ???? ???? ???? 1111 0000 0000 0000 + }; + + sp<ABuffer> buffer = new ABuffer(188); + memset(buffer->data(), 0, buffer->size()); + memcpy(buffer->data(), kData, sizeof(kData)); + + static const unsigned kContinuityCounter = 5; + buffer->data()[3] |= kContinuityCounter; + + size_t section_length = 5 * mSources.size() + 4 + 9; + buffer->data()[6] |= section_length >> 8; + buffer->data()[7] = section_length & 0xff; + + static const unsigned kPCR_PID = 0x1e1; + buffer->data()[13] |= (kPCR_PID >> 8) & 0x1f; + buffer->data()[14] = kPCR_PID & 0xff; + + uint8_t *ptr = &buffer->data()[sizeof(kData)]; + for (size_t i = 0; i < mSources.size(); ++i) { + *ptr++ = mSources.editItemAt(i)->streamType(); + + const unsigned ES_PID = 0x1e0 + i + 1; + *ptr++ = 0xe0 | (ES_PID >> 8); + *ptr++ = ES_PID & 0xff; + *ptr++ = 0xf0; + *ptr++ = 0x00; + } + + *ptr++ = 0x00; + *ptr++ = 0x00; + *ptr++ = 0x00; + *ptr++ = 0x00; + + CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size()); +} + +void MPEG2TSWriter::writeAccessUnit( + int32_t sourceIndex, const sp<ABuffer> &accessUnit) { + // 0x47 + // transport_error_indicator = b0 + // payload_unit_start_indicator = b1 + // transport_priority = b0 + // PID = b0 0001 1110 ???? (13 bits) [0x1e0 + 1 + sourceIndex] + // transport_scrambling_control = b00 + // adaptation_field_control = b01 (no adaptation field, payload only) + // continuity_counter = b???? + // -- payload follows + // packet_startcode_prefix = 0x000001 + // stream_id = 0x?? (0xe0 for avc video, 0xc0 for aac audio) + // PES_packet_length = 0x???? + // reserved = b10 + // PES_scrambling_control = b00 + // PES_priority = b0 + // data_alignment_indicator = b1 + // copyright = b0 + // original_or_copy = b0 + // PTS_DTS_flags = b10 (PTS only) + // ESCR_flag = b0 + // ES_rate_flag = b0 + // DSM_trick_mode_flag = b0 + // additional_copy_info_flag = b0 + // PES_CRC_flag = b0 + // PES_extension_flag = b0 + // PES_header_data_length = 0x05 + // reserved = b0010 (PTS) + // PTS[32..30] = b??? + // reserved = b1 + // PTS[29..15] = b??? ???? ???? ???? (15 bits) + // reserved = b1 + // PTS[14..0] = b??? ???? ???? ???? (15 bits) + // reserved = b1 + // the first fragment of "buffer" follows + + sp<ABuffer> buffer = new ABuffer(188); + memset(buffer->data(), 0, buffer->size()); + + const unsigned PID = 0x1e0 + sourceIndex + 1; + + const unsigned continuity_counter = + mSources.editItemAt(sourceIndex)->incrementContinuityCounter(); + + // XXX if there are multiple streams of a kind (more than 1 audio or + // more than 1 video) they need distinct stream_ids. + const unsigned stream_id = + mSources.editItemAt(sourceIndex)->streamType() == 0x0f ? 0xc0 : 0xe0; + + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + uint32_t PTS = (timeUs * 9ll) / 100ll; + + size_t PES_packet_length = accessUnit->size() + 8; + + uint8_t *ptr = buffer->data(); + *ptr++ = 0x47; + *ptr++ = 0x40 | (PID >> 8); + *ptr++ = PID & 0xff; + *ptr++ = 0x10 | continuity_counter; + *ptr++ = 0x00; + *ptr++ = 0x00; + *ptr++ = 0x01; + *ptr++ = stream_id; + *ptr++ = PES_packet_length >> 8; + *ptr++ = PES_packet_length & 0xff; + *ptr++ = 0x84; + *ptr++ = 0x80; + *ptr++ = 0x05; + *ptr++ = 0x20 | (((PTS >> 30) & 7) << 1) | 1; + *ptr++ = (PTS >> 22) & 0xff; + *ptr++ = (((PTS >> 15) & 0x7f) << 1) | 1; + *ptr++ = (PTS >> 7) & 0xff; + *ptr++ = ((PTS & 0x7f) << 1) | 1; + + size_t sizeLeft = buffer->data() + buffer->size() - ptr; + size_t copy = accessUnit->size(); + if (copy > sizeLeft) { + copy = sizeLeft; + } + + memcpy(ptr, accessUnit->data(), copy); + + CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size()); + + size_t offset = copy; + while (offset < accessUnit->size()) { + // for subsequent fragments of "buffer": + // 0x47 + // transport_error_indicator = b0 + // payload_unit_start_indicator = b0 + // transport_priority = b0 + // PID = b0 0001 1110 ???? (13 bits) [0x1e0 + 1 + sourceIndex] + // transport_scrambling_control = b00 + // adaptation_field_control = b01 (no adaptation field, payload only) + // continuity_counter = b???? + // the fragment of "buffer" follows. + + memset(buffer->data(), 0, buffer->size()); + + const unsigned continuity_counter = + mSources.editItemAt(sourceIndex)->incrementContinuityCounter(); + + ptr = buffer->data(); + *ptr++ = 0x47; + *ptr++ = 0x00 | (PID >> 8); + *ptr++ = PID & 0xff; + *ptr++ = 0x10 | continuity_counter; + + size_t sizeLeft = buffer->data() + buffer->size() - ptr; + size_t copy = accessUnit->size() - offset; + if (copy > sizeLeft) { + copy = sizeLeft; + } + + memcpy(ptr, accessUnit->data() + offset, copy); + CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), + buffer->size()); + + offset += copy; + } +} + +void MPEG2TSWriter::writeTS() { + if (mNumTSPacketsWritten >= mNumTSPacketsBeforeMeta) { + writeProgramAssociationTable(); + writeProgramMap(); + + mNumTSPacketsBeforeMeta = mNumTSPacketsWritten + 2500; + } +} + +} // namespace android + diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp index 7a8cf32..43938b2 100644 --- a/media/libstagefright/OggExtractor.cpp +++ b/media/libstagefright/OggExtractor.cpp @@ -93,7 +93,10 @@ private: sp<DataSource> mSource; off_t mOffset; Page mCurrentPage; + uint64_t mPrevGranulePosition; size_t mCurrentPageSize; + bool mFirstPacketInPage; + uint64_t mCurrentPageSamples; size_t mNextLaceIndex; off_t mFirstDataOffset; @@ -113,6 +116,8 @@ private: void parseFileMetaData(); void extractAlbumArt(const void *data, size_t size); + uint64_t findPrevGranulePosition(off_t pageOffset); + MyVorbisExtractor(const MyVorbisExtractor &); MyVorbisExtractor &operator=(const MyVorbisExtractor &); }; @@ -193,7 +198,10 @@ status_t OggSource::read( MyVorbisExtractor::MyVorbisExtractor(const sp<DataSource> &source) : mSource(source), mOffset(0), + mPrevGranulePosition(0), mCurrentPageSize(0), + mFirstPacketInPage(true), + mCurrentPageSamples(0), mNextLaceIndex(0), mFirstDataOffset(-1) { mCurrentPage.mNumSegments = 0; @@ -238,6 +246,52 @@ status_t MyVorbisExtractor::findNextPage( } } +// Given the offset of the "current" page, find the page immediately preceding +// it (if any) and return its granule position. +// To do this we back up from the "current" page's offset until we find any +// page preceding it and then scan forward to just before the current page. +uint64_t MyVorbisExtractor::findPrevGranulePosition(off_t pageOffset) { + off_t prevPageOffset = 0; + off_t prevGuess = pageOffset; + for (;;) { + if (prevGuess >= 5000) { + prevGuess -= 5000; + } else { + prevGuess = 0; + } + + LOGV("backing up %ld bytes", pageOffset - prevGuess); + + CHECK_EQ(findNextPage(prevGuess, &prevPageOffset), (status_t)OK); + + if (prevPageOffset < pageOffset || prevGuess == 0) { + break; + } + } + + if (prevPageOffset == pageOffset) { + // We did not find a page preceding this one. + return 0; + } + + LOGV("prevPageOffset at %ld, pageOffset at %ld", prevPageOffset, pageOffset); + + for (;;) { + Page prevPage; + ssize_t n = readPage(prevPageOffset, &prevPage); + + if (n <= 0) { + return 0; + } + + prevPageOffset += n; + + if (prevPageOffset == pageOffset) { + return prevPage.mGranulePosition; + } + } +} + status_t MyVorbisExtractor::seekToOffset(off_t offset) { if (mFirstDataOffset >= 0 && offset < mFirstDataOffset) { // Once we know where the actual audio data starts (past the headers) @@ -252,9 +306,16 @@ status_t MyVorbisExtractor::seekToOffset(off_t offset) { return err; } + // We found the page we wanted to seek to, but we'll also need + // the page preceding it to determine how many valid samples are on + // this page. + mPrevGranulePosition = findPrevGranulePosition(pageOffset); + mOffset = pageOffset; mCurrentPageSize = 0; + mFirstPacketInPage = true; + mCurrentPageSamples = 0; mCurrentPage.mNumSegments = 0; mNextLaceIndex = 0; @@ -399,6 +460,12 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { buffer->meta_data()->setInt64(kKeyTime, timeUs); } + if (mFirstPacketInPage) { + buffer->meta_data()->setInt32( + kKeyValidSamples, mCurrentPageSamples); + mFirstPacketInPage = false; + } + *out = buffer; return OK; @@ -423,6 +490,12 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { return n < 0 ? n : (status_t)ERROR_END_OF_STREAM; } + mCurrentPageSamples = + mCurrentPage.mGranulePosition - mPrevGranulePosition; + mFirstPacketInPage = true; + + mPrevGranulePosition = mCurrentPage.mGranulePosition; + mCurrentPageSize = n; mNextLaceIndex = 0; @@ -435,6 +508,10 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { buffer->meta_data()->setInt64(kKeyTime, timeUs); } + buffer->meta_data()->setInt32( + kKeyValidSamples, mCurrentPageSamples); + mFirstPacketInPage = false; + *out = buffer; return OK; diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp index e4ed5e6..f58c16d 100644 --- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp +++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp @@ -171,6 +171,10 @@ status_t AACDecoder::read( mInputBuffer->release(); mInputBuffer = NULL; } + + // Make sure that the next buffer output does not still + // depend on fragments from the last one decoded. + PVMP4AudioDecoderResetBuffer(mDecoderBuf); } else { seekTimeUs = -1; } diff --git a/media/libstagefright/codecs/amrnb/common/include/frame_type_3gpp.h b/media/libstagefright/codecs/amrnb/common/include/frame_type_3gpp.h index 0620367..3ebd929 100644 --- a/media/libstagefright/codecs/amrnb/common/include/frame_type_3gpp.h +++ b/media/libstagefright/codecs/amrnb/common/include/frame_type_3gpp.h @@ -87,22 +87,22 @@ extern "C" enum Frame_Type_3GPP { - AMR_475 = 0, - AMR_515, - AMR_59, - AMR_67, - AMR_74, - AMR_795, - AMR_102, - AMR_122, - AMR_SID, - GSM_EFR_SID, - TDMA_EFR_SID, - PDC_EFR_SID, - FOR_FUTURE_USE1, - FOR_FUTURE_USE2, - FOR_FUTURE_USE3, - AMR_NO_DATA + AMR_475 = 0, /* 4.75 kbps */ + AMR_515, /* 5.15 kbps */ + AMR_59, /* 5.9 kbps */ + AMR_67, /* 6.7 kbps */ + AMR_74, /* 7.4 kbps */ + AMR_795, /* 7.95 kbps */ + AMR_102, /* 10.2 kbps */ + AMR_122, /* 12.2 kbps */ + AMR_SID, /* GSM AMR DTX */ + GSM_EFR_SID, /* GSM EFR DTX */ + TDMA_EFR_SID, /* TDMA EFR DTX */ + PDC_EFR_SID, /* PDC EFR DTX */ + FOR_FUTURE_USE1, /* Unused 1 */ + FOR_FUTURE_USE2, /* Unused 2 */ + FOR_FUTURE_USE3, /* Unused 3 */ + AMR_NO_DATA /* No data */ }; /*---------------------------------------------------------------------------- diff --git a/media/libstagefright/codecs/amrnb/dec/src/gsmamr_dec.h b/media/libstagefright/codecs/amrnb/dec/src/gsmamr_dec.h index 673a94a..a9fdb1c 100644 --- a/media/libstagefright/codecs/amrnb/dec/src/gsmamr_dec.h +++ b/media/libstagefright/codecs/amrnb/dec/src/gsmamr_dec.h @@ -87,6 +87,8 @@ terms listed above has been obtained from the copyright holder. #include "gsm_amr_typedefs.h" #include "pvamrnbdecoder_api.h" +#include "frame_type_3gpp.h" + /*--------------------------------------------------------------------------*/ #ifdef __cplusplus extern "C" @@ -117,25 +119,6 @@ extern "C" /*---------------------------------------------------------------------------- ; ENUMERATED TYPEDEF'S ----------------------------------------------------------------------------*/ - enum Frame_Type_3GPP - { - AMR_475 = 0, /* 4.75 kbps */ - AMR_515, /* 5.15 kbps */ - AMR_59, /* 5.9 kbps */ - AMR_67, /* 6.7 kbps */ - AMR_74, /* 7.4 kbps */ - AMR_795, /* 7.95 kbps */ - AMR_102, /* 10.2 kbps */ - AMR_122, /* 12.2 kbps */ - AMR_SID, /* GSM AMR DTX */ - GSM_EFR_SID, /* GSM EFR DTX */ - TDMA_EFR_SID, /* TDMA EFR DTX */ - PDC_EFR_SID, /* PDC EFR DTX */ - FOR_FUTURE_USE1, /* Unused 1 */ - FOR_FUTURE_USE2, /* Unused 2 */ - FOR_FUTURE_USE3, /* Unused 3 */ - AMR_NO_DATA - }; /* No data */ /*---------------------------------------------------------------------------- ; STRUCTURES TYPEDEF'S diff --git a/media/libstagefright/codecs/amrnb/enc/src/gsmamr_enc.h b/media/libstagefright/codecs/amrnb/enc/src/gsmamr_enc.h index 390a44d..67c7aa3 100644 --- a/media/libstagefright/codecs/amrnb/enc/src/gsmamr_enc.h +++ b/media/libstagefright/codecs/amrnb/enc/src/gsmamr_enc.h @@ -86,6 +86,7 @@ terms listed above has been obtained from the copyright holder. ----------------------------------------------------------------------------*/ #include "gsm_amr_typedefs.h" +#include "frame_type_3gpp.h" /*--------------------------------------------------------------------------*/ #ifdef __cplusplus @@ -137,26 +138,6 @@ extern "C" N_MODES /* Not Used */ }; - enum Frame_Type_3GPP - { - AMR_475 = 0, /* 4.75 kbps */ - AMR_515, /* 5.15 kbps */ - AMR_59, /* 5.9 kbps */ - AMR_67, /* 6.7 kbps */ - AMR_74, /* 7.4 kbps */ - AMR_795, /* 7.95 kbps */ - AMR_102, /* 10.2 kbps */ - AMR_122, /* 12.2 kbps */ - AMR_SID, /* GSM AMR DTX */ - GSM_EFR_SID, /* GSM EFR DTX */ - TDMA_EFR_SID, /* TDMA EFR DTX */ - PDC_EFR_SID, /* PDC EFR DTX */ - FOR_FUTURE_USE1, /* Unused 1 */ - FOR_FUTURE_USE2, /* Unused 2 */ - FOR_FUTURE_USE3, /* Unused 3 */ - AMR_NO_DATA /* No data */ - }; - /*---------------------------------------------------------------------------- ; STRUCTURES TYPEDEF'S ----------------------------------------------------------------------------*/ diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp index c4a8280..59dd740 100644 --- a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp +++ b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp @@ -132,6 +132,10 @@ status_t MP3Decoder::read( mInputBuffer->release(); mInputBuffer = NULL; } + + // Make sure that the next buffer output does not still + // depend on fragments from the last one decoded. + pvmp3_InitDecoder(mConfig, mDecoderBuf); } else { seekTimeUs = -1; } diff --git a/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp b/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp index 53f0638..703b41e 100644 --- a/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp +++ b/media/libstagefright/codecs/vorbis/dec/VorbisDecoder.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "VorbisDecoder" +#include <utils/Log.h> + #include "VorbisDecoder.h" #include <media/stagefright/MediaBufferGroup.h> @@ -108,6 +112,7 @@ status_t VorbisDecoder::start(MetaData *params) { mAnchorTimeUs = 0; mNumFramesOutput = 0; + mNumFramesLeftOnPage = 0; mStarted = true; return OK; @@ -188,6 +193,13 @@ int VorbisDecoder::decodePacket(MediaBuffer *packet, MediaBuffer *out) { } } + if (numFrames > mNumFramesLeftOnPage) { + LOGV("discarding %d frames at end of page", + numFrames - mNumFramesLeftOnPage); + numFrames = mNumFramesLeftOnPage; + } + mNumFramesLeftOnPage -= numFrames; + out->set_range(0, numFrames * sizeof(int16_t) * mNumChannels); return numFrames; @@ -226,6 +238,12 @@ status_t VorbisDecoder::read( CHECK(seekTimeUs < 0); } + int32_t numPageSamples; + if (inputBuffer->meta_data()->findInt32( + kKeyValidSamples, &numPageSamples)) { + mNumFramesLeftOnPage = numPageSamples; + } + MediaBuffer *outputBuffer; CHECK_EQ(mBufferGroup->acquire_buffer(&outputBuffer), OK); diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 1f3946c..600faca 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -92,6 +92,9 @@ struct AwesomePlayer { // This is a mask of MediaExtractor::Flags. uint32_t flags() const; + void postAudioEOS(); + void postAudioSeekComplete(); + private: friend struct AwesomeEvent; diff --git a/media/libstagefright/include/VorbisDecoder.h b/media/libstagefright/include/VorbisDecoder.h index e9a488a..13e8b77 100644 --- a/media/libstagefright/include/VorbisDecoder.h +++ b/media/libstagefright/include/VorbisDecoder.h @@ -55,6 +55,7 @@ private: int32_t mSampleRate; int64_t mAnchorTimeUs; int64_t mNumFramesOutput; + int32_t mNumFramesLeftOnPage; vorbis_dsp_state *mState; vorbis_info *mVi; diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 9952783..47cca80 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -389,20 +389,23 @@ void ATSParser::Stream::parsePES(ABitReader *br) { // ES data follows. - onPayloadData( - PTS_DTS_flags, PTS, DTS, - br->data(), br->numBitsLeft() / 8); - if (PES_packet_length != 0) { CHECK_GE(PES_packet_length, PES_header_data_length + 3); unsigned dataLength = PES_packet_length - 3 - PES_header_data_length; - CHECK_EQ(br->numBitsLeft(), dataLength * 8); + CHECK_GE(br->numBitsLeft(), dataLength * 8); + + onPayloadData( + PTS_DTS_flags, PTS, DTS, br->data(), dataLength); br->skipBits(dataLength * 8); } else { + onPayloadData( + PTS_DTS_flags, PTS, DTS, + br->data(), br->numBitsLeft() / 8); + size_t payloadSizeBits = br->numBitsLeft(); CHECK((payloadSizeBits % 8) == 0); @@ -491,7 +494,7 @@ static sp<ABuffer> MakeAVCCodecSpecificData( CHECK(picParamSet != NULL); buffer->setRange(stopOffset, size - stopOffset); - LOGI("buffer has %d bytes left.", buffer->size()); + LOGV("buffer has %d bytes left.", buffer->size()); size_t csdSize = 1 + 3 + 1 + 1 @@ -527,6 +530,8 @@ static bool getNextNALUnit( const uint8_t *data = *_data; size_t size = *_size; + // hexdump(data, size); + *nalStart = NULL; *nalSize = 0; @@ -572,18 +577,23 @@ static bool getNextNALUnit( ++offset; } - CHECK_LT(offset + 2, size); - *nalStart = &data[startOffset]; *nalSize = endOffset - startOffset; - *_data = &data[offset]; - *_size = size - offset; + if (offset + 2 < size) { + *_data = &data[offset]; + *_size = size - offset; + } else { + *_data = NULL; + *_size = 0; + } return true; } sp<ABuffer> MakeCleanAVCData(const uint8_t *data, size_t size) { + // hexdump(data, size); + const uint8_t *tmpData = data; size_t tmpSize = size; @@ -591,6 +601,7 @@ sp<ABuffer> MakeCleanAVCData(const uint8_t *data, size_t size) { const uint8_t *nalStart; size_t nalSize; while (getNextNALUnit(&tmpData, &tmpSize, &nalStart, &nalSize)) { + // hexdump(nalStart, nalSize); totalSize += 4 + nalSize; } @@ -615,15 +626,15 @@ static sp<ABuffer> FindMPEG2ADTSConfig( CHECK_EQ(br.getBits(2), 0u); br.getBits(1); // protection_absent unsigned profile = br.getBits(2); - LOGI("profile = %u", profile); + LOGV("profile = %u", profile); CHECK_NE(profile, 3u); unsigned sampling_freq_index = br.getBits(4); br.getBits(1); // private_bit unsigned channel_configuration = br.getBits(3); CHECK_NE(channel_configuration, 0u); - LOGI("sampling_freq_index = %u", sampling_freq_index); - LOGI("channel_configuration = %u", channel_configuration); + LOGV("sampling_freq_index = %u", sampling_freq_index); + LOGV("channel_configuration = %u", channel_configuration); CHECK_LE(sampling_freq_index, 11u); static const int32_t kSamplingFreq[] = { @@ -707,8 +718,8 @@ void ATSParser::Stream::onPayloadData( sp<ABuffer> csd = FindMPEG2ADTSConfig(buffer, &sampleRate, &channelCount); - LOGI("sampleRate = %d", sampleRate); - LOGI("channelCount = %d", channelCount); + LOGV("sampleRate = %d", sampleRate); + LOGV("channelCount = %d", channelCount); meta->setInt32(kKeySampleRate, sampleRate); meta->setInt32(kKeyChannelCount, channelCount); @@ -716,7 +727,7 @@ void ATSParser::Stream::onPayloadData( meta->setData(kKeyESDS, 0, csd->data(), csd->size()); } - LOGI("created source!"); + LOGV("created source!"); mSource = new AnotherPacketSource(meta); // fall through @@ -915,7 +926,10 @@ void ATSParser::parseTS(ABitReader *br) { unsigned adaptation_field_control = br->getBits(2); LOGV("adaptation_field_control = %u", adaptation_field_control); - MY_LOGV("continuity_counter = %u", br->getBits(4)); + unsigned continuity_counter = br->getBits(4); + LOGV("continuity_counter = %u", continuity_counter); + + // LOGI("PID = 0x%04x, continuity_counter = %u", PID, continuity_counter); if (adaptation_field_control == 2 || adaptation_field_control == 3) { parseAdaptationField(br); diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp index 56ca375..2417305 100644 --- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp +++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp @@ -32,6 +32,8 @@ namespace android { +static const size_t kTSPacketSize = 188; + struct MPEG2TSSource : public MediaSource { MPEG2TSSource( const sp<MPEG2TSExtractor> &extractor, @@ -126,27 +128,37 @@ sp<MetaData> MPEG2TSExtractor::getMetaData() { void MPEG2TSExtractor::init() { bool haveAudio = false; bool haveVideo = false; + int numPacketsParsed = 0; while (feedMore() == OK) { ATSParser::SourceType type; if (haveAudio && haveVideo) { break; } - if (haveVideo) { - type = ATSParser::MPEG2ADTS_AUDIO; - } else { - type = ATSParser::AVC_VIDEO; + if (!haveVideo) { + sp<AnotherPacketSource> impl = + (AnotherPacketSource *)mParser->getSource( + ATSParser::AVC_VIDEO).get(); + + if (impl != NULL) { + haveVideo = true; + mSourceImpls.push(impl); + } } - sp<AnotherPacketSource> impl = - (AnotherPacketSource *)mParser->getSource(type).get(); - if (impl != NULL) { - if (type == ATSParser::MPEG2ADTS_AUDIO) { + if (!haveAudio) { + sp<AnotherPacketSource> impl = + (AnotherPacketSource *)mParser->getSource( + ATSParser::MPEG2ADTS_AUDIO).get(); + + if (impl != NULL) { haveAudio = true; - } else { - haveVideo = true; + mSourceImpls.push(impl); } - mSourceImpls.push(impl); + } + + if (++numPacketsParsed > 1500) { + break; } } @@ -156,8 +168,6 @@ void MPEG2TSExtractor::init() { status_t MPEG2TSExtractor::feedMore() { Mutex::Autolock autoLock(mLock); - static const size_t kTSPacketSize = 188; - uint8_t packet[kTSPacketSize]; ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize); @@ -176,23 +186,18 @@ status_t MPEG2TSExtractor::feedMore() { bool SniffMPEG2TS( const sp<DataSource> &source, String8 *mimeType, float *confidence, sp<AMessage> *) { -#if 0 - char header; - if (source->readAt(0, &header, 1) != 1 || header != 0x47) { - return false; + for (int i = 0; i < 5; ++i) { + char header; + if (source->readAt(kTSPacketSize * i, &header, 1) != 1 + || header != 0x47) { + return false; + } } - *confidence = 0.05f; + *confidence = 0.1f; mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2TS); return true; -#else - // For now we're going to never identify this type of stream, since we'd - // just base our decision on a single byte... - // Instead you can instantiate an MPEG2TSExtractor by explicitly stating - // its proper mime type in the call to MediaExtractor::Create(...). - return false; -#endif } } // namespace android diff --git a/native/android/Android.mk b/native/android/Android.mk index cc35a3a..44ec83f 100644 --- a/native/android/Android.mk +++ b/native/android/Android.mk @@ -12,6 +12,7 @@ LOCAL_SRC_FILES:= \ looper.cpp \ native_activity.cpp \ native_window.cpp \ + obb.cpp \ sensor.cpp \ storage_manager.cpp diff --git a/native/android/obb.cpp b/native/android/obb.cpp new file mode 100644 index 0000000..e0cb1a6 --- /dev/null +++ b/native/android/obb.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_TAG "NObb" + +#include <android/obb.h> + +#include <utils/Log.h> +#include <utils/ObbFile.h> + +using namespace android; + +struct AObbInfo : public ObbFile {}; + +AObbInfo* AObbScanner_getObbInfo(const char* filename) { + AObbInfo* obbFile = new AObbInfo(); + if (obbFile == NULL || !obbFile->readFrom(filename)) { + delete obbFile; + return NULL; + } + obbFile->incStrong((void*)AObbScanner_getObbInfo); + return static_cast<AObbInfo*>(obbFile); +} + +void AObbInfo_delete(AObbInfo* obbInfo) { + if (obbInfo != NULL) { + obbInfo->decStrong((void*)AObbScanner_getObbInfo); + } +} + +const char* AObbInfo_getPackageName(AObbInfo* obbInfo) { + return obbInfo->getPackageName(); +} + +int32_t AObbInfo_getVersion(AObbInfo* obbInfo) { + return obbInfo->getVersion(); +} + +int32_t AObbInfo_getFlags(AObbInfo* obbInfo) { + return obbInfo->getFlags(); +} diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp index 6dbe746..2f20641 100644 --- a/native/android/storage_manager.cpp +++ b/native/android/storage_manager.cpp @@ -38,20 +38,20 @@ public: mStorageManager(mgr) {} - virtual void onObbResult(const android::String16& filename, const android::String16& state) { - LOGD("Got obb result (%s, %s)\n", String8(filename).string(), String8(state).string()); - } + virtual void onObbResult(const android::String16& filename, const android::String16& state); }; struct AStorageManager : public RefBase { protected: - void* mObbCallback; + AStorageManager_obbCallbackFunc mObbCallback; + void* mObbCallbackData; sp<ObbActionListener> mObbActionListener; sp<IMountService> mMountService; public: - AStorageManager() : - mObbCallback(NULL) + AStorageManager() + : mObbCallback(NULL) + , mObbCallbackData(NULL) { } @@ -73,8 +73,15 @@ public: return true; } - void setObbCallback(void* cb) { + void setObbCallback(AStorageManager_obbCallbackFunc cb, void* data) { mObbCallback = cb; + mObbCallbackData = data; + } + + void fireCallback(const char* filename, const char* state) { + if (mObbCallback != NULL) { + mObbCallback(filename, state, mObbCallbackData); + } } void mountObb(const char* filename, const char* key) { @@ -85,7 +92,7 @@ public: void unmountObb(const char* filename, const bool force) { String16 filename16(filename); - mMountService->unmountObb(filename16, force); + mMountService->unmountObb(filename16, force, mObbActionListener); } int isObbMounted(const char* filename) { @@ -104,6 +111,10 @@ public: } }; +void ObbActionListener::onObbResult(const android::String16& filename, const android::String16& state) { + mStorageManager->fireCallback(String8(filename).string(), String8(state).string()); +} + AStorageManager* AStorageManager_new() { sp<AStorageManager> mgr = new AStorageManager(); @@ -120,8 +131,8 @@ void AStorageManager_delete(AStorageManager* mgr) { } } -void AStorageManager_setObbCallback(AStorageManager* mgr, void* cb) { - mgr->setObbCallback(cb); +void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data) { + mgr->setObbCallback(cb, data); } void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key) { diff --git a/native/include/android/obb.h b/native/include/android/obb.h new file mode 100644 index 0000000..65e9b2a --- /dev/null +++ b/native/include/android/obb.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_OBB_H +#define ANDROID_OBB_H + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct AObbInfo; +typedef struct AObbInfo AObbInfo; + +enum { + AOBBINFO_OVERLAY = 0x0001, +}; + +/** + * Scan an OBB and get information about it. + */ +AObbInfo* AObbScanner_getObbInfo(const char* filename); + +/** + * Destroy the AObbInfo object. You must call this when finished with the object. + */ +void AObbInfo_delete(AObbInfo* obbInfo); + +/** + * Get the package name for the OBB. + */ +const char* AObbInfo_getPackageName(AObbInfo* obbInfo); + +/** + * Get the version of an OBB file. + */ +int32_t AObbInfo_getVersion(AObbInfo* obbInfo); + +/** + * Get the flags of an OBB file. + */ +int32_t AObbInfo_getFlags(AObbInfo* obbInfo); + +#ifdef __cplusplus +}; +#endif + +#endif // ANDROID_OBB_H diff --git a/native/include/android/storage_manager.h b/native/include/android/storage_manager.h index bbed8a4..6f925c1 100644 --- a/native/include/android/storage_manager.h +++ b/native/include/android/storage_manager.h @@ -37,17 +37,22 @@ AStorageManager* AStorageManager_new(); void AStorageManager_delete(AStorageManager* mgr); /** - * Callback to call when requested OBB is complete. + * Callback function for asynchronous calls made on OBB files. */ -void AStorageManager_setObbCallback(AStorageManager* mgr, void* cb); +typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const char* state, void* data); /** - * Attempts to mount an OBB file. + * Callback to call when requested asynchronous OBB operation is complete. + */ +void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data); + +/** + * Attempts to mount an OBB file. This is an asynchronous operation. */ void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key); /** - * Attempts to unmount an OBB file. + * Attempts to unmount an OBB file. This is an asynchronous operation. */ void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force); @@ -66,4 +71,4 @@ const char* AStorageManager_getMountedObbPath(AStorageManager* mgr, const char* }; #endif -#endif // ANDROID_PACKAGE_MANAGER_H +#endif // ANDROID_STORAGE_MANAGER_H diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index f08bd3c..eb86277 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -156,7 +156,12 @@ public class DefaultContainerService extends IntentService { } public ObbInfo getObbInfo(String filename) { - return ObbScanner.getObbInfo(filename); + try { + return ObbScanner.getObbInfo(filename); + } catch (IOException e) { + Log.d(TAG, "Couldn't get OBB info", e); + return null; + } } }; diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png Binary files differindex 157491e..a0e59cf 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png Binary files differindex aea18ed..1626895 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png Binary files differindex 1a25a2c..3c2e2b9 100755..100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png Binary files differindex 2134d49..bb41db0 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index a58e311..12d1d5c 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -71,6 +71,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:fadingEdge="none" + android:overscrollMode="ifContentScrolls" > <com.android.systemui.statusbar.NotificationLinearLayout android:id="@+id/notificationLinearLayout" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java b/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java index d89d093..31b78b6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CarrierLabel.java @@ -99,7 +99,7 @@ public class CarrierLabel extends TextView { } if (showSpn && spn != null) { if (something) { - str.append(' '); + str.append('\n'); } str.append(spn); something = true; diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 8a732ed..97b8086 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -5332,6 +5332,15 @@ size_t AudioFlinger::EffectModule::removeHandle(const wp<EffectHandle>& handle) } } + // Release effect engine here so that it is done immediately. Otherwise it will be released + // by the destructor when the last strong reference on the this object is released which can + // happen after next process is called on this effect. + if (size == 0 && mEffectInterface != NULL) { + // release effect engine + EffectRelease(mEffectInterface); + mEffectInterface = NULL; + } + return size; } @@ -6145,21 +6154,36 @@ sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromId_l(int // Must be called with EffectChain::mLock locked void AudioFlinger::EffectChain::process_l() { + sp<ThreadBase> thread = mThread.promote(); + if (thread == 0) { + LOGW("process_l(): cannot promote mixer thread"); + return; + } + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + bool isGlobalSession = (mSessionId == AudioSystem::SESSION_OUTPUT_MIX) || + (mSessionId == AudioSystem::SESSION_OUTPUT_STAGE); + bool tracksOnSession = false; + if (!isGlobalSession) { + tracksOnSession = + playbackThread->hasAudioSession(mSessionId) & PlaybackThread::TRACK_SESSION; + } + size_t size = mEffects.size(); - for (size_t i = 0; i < size; i++) { - mEffects[i]->process(); + // do not process effect if no track is present in same audio session + if (isGlobalSession || tracksOnSession) { + for (size_t i = 0; i < size; i++) { + mEffects[i]->process(); + } } for (size_t i = 0; i < size; i++) { mEffects[i]->updateState(); } // if no track is active, input buffer must be cleared here as the mixer process // will not do it - if (mSessionId > 0 && activeTracks() == 0) { - sp<ThreadBase> thread = mThread.promote(); - if (thread != 0) { - size_t numSamples = thread->frameCount() * thread->channelCount(); - memset(mInBuffer, 0, numSamples * sizeof(int16_t)); - } + if (tracksOnSession && + activeTracks() == 0) { + size_t numSamples = playbackThread->frameCount() * playbackThread->channelCount(); + memset(mInBuffer, 0, numSamples * sizeof(int16_t)); } } diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index cfba07a..265d613 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -46,6 +46,7 @@ import android.os.storage.IObbActionListener; import android.os.storage.StorageResultCode; import android.util.Slog; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -148,7 +149,7 @@ class MountService extends IMountService.Stub * Mounted OBB tracking information. Used to track the current state of all * OBBs. */ - final private Map<IObbActionListener, ObbState> mObbMounts = new HashMap<IObbActionListener, ObbState>(); + final private Map<IObbActionListener, List<ObbState>> mObbMounts = new HashMap<IObbActionListener, List<ObbState>>(); final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>(); class ObbState implements IBinder.DeathRecipient { @@ -160,13 +161,13 @@ class MountService extends IMountService.Stub } // OBB source filename - String filename; + final String filename; // Token of remote Binder caller - IObbActionListener token; + final IObbActionListener token; // Binder.callingUid() - public int callerUid; + final public int callerUid; // Whether this is mounted currently. boolean mounted; @@ -225,9 +226,9 @@ class MountService extends IMountService.Stub private static final int MAX_UNMOUNT_RETRIES = 4; class UnmountCallBack { - String path; + final String path; + final boolean force; int retries; - boolean force; UnmountCallBack(String path, boolean force) { retries = 0; @@ -242,7 +243,7 @@ class MountService extends IMountService.Stub } class UmsEnableCallBack extends UnmountCallBack { - String method; + final String method; UmsEnableCallBack(String path, String method, boolean force) { super(path, force); @@ -1450,11 +1451,6 @@ class MountService extends IMountService.Stub mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); } - private boolean isCallerOwnerOfPackageOrSystem(String packageName) { - final int callerUid = Binder.getCallingUid(); - return isUidOwnerOfPackageOrSystem(packageName, callerUid); - } - private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) { if (callerUid == android.os.Process.SYSTEM_UID) { return true; @@ -1506,6 +1502,12 @@ class MountService extends IMountService.Stub waitForReady(); warnOnNotMounted(); + if (filename == null) { + throw new IllegalArgumentException("filename cannot be null"); + } else if (token == null) { + throw new IllegalArgumentException("token cannot be null"); + } + final ObbState obbState; synchronized (mObbMounts) { @@ -1513,10 +1515,6 @@ class MountService extends IMountService.Stub throw new IllegalArgumentException("OBB file is already mounted"); } - if (mObbMounts.containsKey(token)) { - throw new IllegalArgumentException("You may only have one OBB mounted at a time"); - } - final int callerUid = Binder.getCallingUid(); obbState = new ObbState(filename, token, callerUid); addObbState(obbState); @@ -1536,6 +1534,12 @@ class MountService extends IMountService.Stub } public void unmountObb(String filename, boolean force, IObbActionListener token) { + if (filename == null) { + throw new IllegalArgumentException("filename cannot be null"); + } else if (token == null) { + throw new IllegalArgumentException("token cannot be null"); + } + final ObbState obbState; synchronized (mObbMounts) { @@ -1543,6 +1547,12 @@ class MountService extends IMountService.Stub throw new IllegalArgumentException("OBB is not mounted"); } obbState = mObbPathToStateMap.get(filename); + + if (Binder.getCallingUid() != obbState.callerUid) { + throw new SecurityException("caller UID does not match original mount caller UID"); + } else if (!token.asBinder().equals(obbState.token.asBinder())) { + throw new SecurityException("caller does not match original mount caller"); + } } UnmountObbAction action = new UnmountObbAction(obbState, force); @@ -1554,14 +1564,25 @@ class MountService extends IMountService.Stub private void addObbState(ObbState obbState) { synchronized (mObbMounts) { - mObbMounts.put(obbState.token, obbState); + List<ObbState> obbStates = mObbMounts.get(obbState.token); + if (obbStates == null) { + obbStates = new ArrayList<ObbState>(); + mObbMounts.put(obbState.token, obbStates); + } + obbStates.add(obbState); mObbPathToStateMap.put(obbState.filename, obbState); } } private void removeObbState(ObbState obbState) { synchronized (mObbMounts) { - mObbMounts.remove(obbState.token); + final List<ObbState> obbStates = mObbMounts.get(obbState.token); + if (obbStates != null) { + obbStates.remove(obbState); + } + if (obbStates == null || obbStates.isEmpty()) { + mObbMounts.remove(obbState.token); + } mObbPathToStateMap.remove(obbState.filename); } } @@ -1737,7 +1758,7 @@ class MountService extends IMountService.Stub } } - abstract void handleExecute() throws RemoteException; + abstract void handleExecute() throws RemoteException, IOException; abstract void handleError(); } @@ -1749,8 +1770,12 @@ class MountService extends IMountService.Stub mKey = key; } - public void handleExecute() throws RemoteException { - ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename); + public void handleExecute() throws RemoteException, IOException { + final ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename); + if (obbInfo == null) { + throw new IOException("Couldn't read OBB file: " + mObbState.filename); + } + if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) { throw new IllegalArgumentException("Caller package does not match OBB file"); } @@ -1773,15 +1798,17 @@ class MountService extends IMountService.Stub if (rc == StorageResultCode.OperationSucceeded) { try { - mObbState.token.onObbResult(mObbState.filename, "mounted"); + mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_MOUNTED); } catch (RemoteException e) { Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); } } else { - Slog.e(TAG, "Couldn't mount OBB file"); + Slog.e(TAG, "Couldn't mount OBB file: " + rc); // We didn't succeed, so remove this from the mount-set. removeObbState(mObbState); + + mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL); } } @@ -1789,7 +1816,7 @@ class MountService extends IMountService.Stub removeObbState(mObbState); try { - mObbState.token.onObbResult(mObbState.filename, "error"); + mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL); } catch (RemoteException e) { Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename); } @@ -1818,11 +1845,10 @@ class MountService extends IMountService.Stub mForceUnmount = force; } - public void handleExecute() throws RemoteException { - ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename); - - if (!isCallerOwnerOfPackageOrSystem(obbInfo.packageName)) { - throw new IllegalArgumentException("Caller package does not match OBB file"); + public void handleExecute() throws RemoteException, IOException { + final ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename); + if (obbInfo == null) { + throw new IOException("Couldn't read OBB file: " + mObbState.filename); } int rc = StorageResultCode.OperationSucceeded; @@ -1843,13 +1869,13 @@ class MountService extends IMountService.Stub removeObbState(mObbState); try { - mObbState.token.onObbResult(mObbState.filename, "unmounted"); + mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_UNMOUNTED); } catch (RemoteException e) { Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); } } else { try { - mObbState.token.onObbResult(mObbState.filename, "error"); + mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL); } catch (RemoteException e) { Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); } @@ -1860,7 +1886,7 @@ class MountService extends IMountService.Stub removeObbState(mObbState); try { - mObbState.token.onObbResult(mObbState.filename, "error"); + mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL); } catch (RemoteException e) { Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename); } diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 5d32b74..71105f1 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -2105,6 +2105,7 @@ class PowerManagerService extends IPowerManager.Stub } public void userActivity(long time, boolean noChangeLights) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); userActivity(time, -1, noChangeLights, OTHER_EVENT, false); } @@ -2128,7 +2129,6 @@ class PowerManagerService extends IPowerManager.Stub private void userActivity(long time, long timeoutOverride, boolean noChangeLights, int eventType, boolean force) { - //mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0) && (eventType == CHEEK_EVENT || eventType == TOUCH_EVENT)) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index a80ab1d..7f42429 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -19,6 +19,7 @@ package com.android.server; import com.android.server.am.ActivityManagerService; import com.android.internal.os.BinderInternal; import com.android.internal.os.SamplingProfilerIntegration; +import com.trustedlogic.trustednfc.android.server.NfcService; import dalvik.system.VMRuntime; import dalvik.system.Zygote; @@ -41,6 +42,7 @@ import android.server.BluetoothA2dpService; import android.server.BluetoothService; import android.server.search.SearchManagerService; import android.util.EventLog; +import android.util.Log; import android.util.Slog; import android.accounts.AccountManagerService; @@ -409,6 +411,20 @@ class ServerThread extends Thread { } catch (Throwable e) { Slog.e(TAG, "Failure starting Recognition Service", e); } + + try { + Slog.i(TAG, "Nfc Service"); + NfcService nfc; + try { + nfc = new NfcService(context); + } catch (UnsatisfiedLinkError e) { // gross hack to detect NFC + nfc = null; + Slog.w(TAG, "No NFC support"); + } + ServiceManager.addService(Context.NFC_SERVICE, nfc); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting NFC Service", e); + } try { Slog.i(TAG, "DiskStats Service"); diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index c047e10..3d95bf0 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -2209,7 +2209,7 @@ public class WifiService extends IWifiManager.Stub { } private boolean acquireWifiLockLocked(WifiLock wifiLock) { - Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock); + if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock); mLocks.addLock(wifiLock); @@ -2279,7 +2279,7 @@ public class WifiService extends IWifiManager.Stub { WifiLock wifiLock = mLocks.removeLock(lock); - Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock); + if (DBG) Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock); hadLock = (wifiLock != null); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 22cd8ff..34753e7 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -4218,29 +4218,75 @@ public final class ActivityManagerService extends ActivityManagerNative } private final boolean checkHoldingPermissionsLocked(IPackageManager pm, - ProviderInfo pi, int uid, int modeFlags) { + ProviderInfo pi, Uri uri, int uid, int modeFlags) { + boolean readPerm = (modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0; + boolean writePerm = (modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0; + if (DEBUG_URI_PERMISSION) Slog.v(TAG, + "checkHoldingPermissionsLocked: uri=" + uri + " uid=" + uid); try { - if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { - if ((pi.readPermission != null) && + // Is the component private from the target uid? + final boolean prv = !pi.exported && pi.applicationInfo.uid != uid; + + // Acceptable if the there is no read permission needed from the + // target or the target is holding the read permission. + if (!readPerm) { + if ((!prv && pi.readPermission == null) || (pm.checkUidPermission(pi.readPermission, uid) - != PackageManager.PERMISSION_GRANTED)) { - return false; + == PackageManager.PERMISSION_GRANTED)) { + readPerm = true; } } - if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { - if ((pi.writePermission != null) && + + // Acceptable if the there is no write permission needed from the + // target or the target is holding the read permission. + if (!writePerm) { + if (!prv && (pi.writePermission == null) || (pm.checkUidPermission(pi.writePermission, uid) - != PackageManager.PERMISSION_GRANTED)) { - return false; + == PackageManager.PERMISSION_GRANTED)) { + writePerm = true; } } - if (!pi.exported && pi.applicationInfo.uid != uid) { - return false; + + // Acceptable if there is a path permission matching the URI that + // the target holds the permission on. + PathPermission[] pps = pi.pathPermissions; + if (pps != null && (!readPerm || !writePerm)) { + final String path = uri.getPath(); + int i = pps.length; + while (i > 0 && (!readPerm || !writePerm)) { + i--; + PathPermission pp = pps[i]; + if (!readPerm) { + final String pprperm = pp.getReadPermission(); + if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Checking read perm for " + + pprperm + " for " + pp.getPath() + + ": match=" + pp.match(path) + + " check=" + pm.checkUidPermission(pprperm, uid)); + if (pprperm != null && pp.match(path) && + (pm.checkUidPermission(pprperm, uid) + == PackageManager.PERMISSION_GRANTED)) { + readPerm = true; + } + } + if (!writePerm) { + final String ppwperm = pp.getWritePermission(); + if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Checking write perm " + + ppwperm + " for " + pp.getPath() + + ": match=" + pp.match(path) + + " check=" + pm.checkUidPermission(ppwperm, uid)); + if (ppwperm != null && pp.match(path) && + (pm.checkUidPermission(ppwperm, uid) + == PackageManager.PERMISSION_GRANTED)) { + writePerm = true; + } + } + } } - return true; } catch (RemoteException e) { return false; } + + return readPerm && writePerm; } private final boolean checkUriPermissionLocked(Uri uri, int uid, @@ -4333,7 +4379,7 @@ public final class ActivityManagerService extends ActivityManagerNative } // First... does the target actually need this permission? - if (checkHoldingPermissionsLocked(pm, pi, targetUid, modeFlags)) { + if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) { // No need to grant the target this permission. if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Target " + targetPkg + " already has full permission to " + uri); @@ -4367,7 +4413,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Third... does the caller itself have permission to access // this uri? - if (!checkHoldingPermissionsLocked(pm, pi, callingUid, modeFlags)) { + if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) { if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) { throw new SecurityException("Uid " + callingUid + " does not have permission to uri " + uri); @@ -4535,7 +4581,7 @@ public final class ActivityManagerService extends ActivityManagerNative } // Does the caller have this permission on the URI? - if (!checkHoldingPermissionsLocked(pm, pi, callingUid, modeFlags)) { + if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) { // Right now, if you are not the original owner of the permission, // you are not allowed to revoke it. //if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) { diff --git a/services/java/com/trustedlogic/trustednfc/android/server/NfcService.java b/services/java/com/trustedlogic/trustednfc/android/server/NfcService.java new file mode 100644 index 0000000..431b798 --- /dev/null +++ b/services/java/com/trustedlogic/trustednfc/android/server/NfcService.java @@ -0,0 +1,2111 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.trustedlogic.trustednfc.android.server; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.ListIterator; +import java.util.Set; + +import com.trustedlogic.trustednfc.android.ILlcpConnectionlessSocket; +import com.trustedlogic.trustednfc.android.ILlcpServiceSocket; +import com.trustedlogic.trustednfc.android.INfcManager; +import com.trustedlogic.trustednfc.android.ILlcpSocket; +import com.trustedlogic.trustednfc.android.INfcTag; +import com.trustedlogic.trustednfc.android.IP2pInitiator; +import com.trustedlogic.trustednfc.android.IP2pTarget; +import com.trustedlogic.trustednfc.android.LlcpPacket; +import com.trustedlogic.trustednfc.android.NdefMessage; +import com.trustedlogic.trustednfc.android.NfcException; +import com.trustedlogic.trustednfc.android.NfcManager; +import com.trustedlogic.trustednfc.android.internal.NativeLlcpConnectionlessSocket; +import com.trustedlogic.trustednfc.android.internal.NativeLlcpServiceSocket; +import com.trustedlogic.trustednfc.android.internal.NativeLlcpSocket; +import com.trustedlogic.trustednfc.android.internal.NativeNfcManager; +import com.trustedlogic.trustednfc.android.internal.NativeNfcTag; +import com.trustedlogic.trustednfc.android.internal.NativeP2pDevice; +import com.trustedlogic.trustednfc.android.internal.ErrorCodes; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.os.RemoteException; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; +import android.util.Log; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +public class NfcService extends INfcManager.Stub implements Runnable { + + /** + * NFC Service tag + */ + private static final String TAG = "NfcService"; + + /** + * NFC features disabled state + */ + private static final short NFC_STATE_DISABLED = 0x00; + + /** + * NFC features enabled state + */ + private static final short NFC_STATE_ENABLED = 0x01; + + /** + * NFC Discovery for Reader mode + */ + private static final int DISCOVERY_MODE_READER = 0; + + /** + * NFC Discovery for Card Emulation Mode + */ + private static final int DISCOVERY_MODE_CARD_EMULATION = 2; + + /** + * LLCP Service Socket type + */ + private static final int LLCP_SERVICE_SOCKET_TYPE = 0; + + /** + * LLCP Socket type + */ + private static final int LLCP_SOCKET_TYPE = 1; + + /** + * LLCP Connectionless socket type + */ + private static final int LLCP_CONNECTIONLESS_SOCKET_TYPE = 2; + + /** + * Maximun number of sockets managed + */ + private static final int LLCP_SOCKET_NB_MAX = 5; + + /** + * Default value for the Maximum Information Unit parameter + */ + private static final int LLCP_LTO_DEFAULT_VALUE = 150; + + /** + * Default value for the Maximum Information Unit parameter + */ + private static final int LLCP_LTO_MAX_VALUE = 255; + + /** + * Maximun value for the Receive Window + */ + private static final int LLCP_RW_MAX_VALUE = 15; + + /** + * Default value for the Maximum Information Unit parameter + */ + private static final int LLCP_MIU_DEFAULT_VALUE = 128; + + /** + * Default value for the Maximum Information Unit parameter + */ + private static final int LLCP_MIU_MAX_VALUE = 2176; + + /** + * Default value for the Well Known Service List parameter + */ + private static final int LLCP_WKS_DEFAULT_VALUE = 1; + + /** + * Max value for the Well Known Service List parameter + */ + private static final int LLCP_WKS_MAX_VALUE = 15; + + /** + * Default value for the Option parameter + */ + private static final int LLCP_OPT_DEFAULT_VALUE = 0; + + /** + * Max value for the Option parameter + */ + private static final int LLCP_OPT_MAX_VALUE = 3; + + /** + * LLCP Properties + */ + private static final int PROPERTY_LLCP_LTO = 0; + + private static final int PROPERTY_LLCP_MIU = 1; + + private static final int PROPERTY_LLCP_WKS = 2; + + private static final int PROPERTY_LLCP_OPT = 3; + + private static final String PROPERTY_LLCP_LTO_VALUE = "llcp.lto"; + + private static final String PROPERTY_LLCP_MIU_VALUE = "llcp.miu"; + + private static final String PROPERTY_LLCP_WKS_VALUE = "llcp.wks"; + + private static final String PROPERTY_LLCP_OPT_VALUE = "llcp.opt"; + + /** + * NFC Reader Properties + */ + private static final int PROPERTY_NFC_DISCOVERY_A = 4; + + private static final int PROPERTY_NFC_DISCOVERY_B = 5; + + private static final int PROPERTY_NFC_DISCOVERY_F = 6; + + private static final int PROPERTY_NFC_DISCOVERY_15693 = 7; + + private static final int PROPERTY_NFC_DISCOVERY_NFCIP = 8; + + private static final String PROPERTY_NFC_DISCOVERY_A_VALUE = "discovery.iso14443A"; + + private static final String PROPERTY_NFC_DISCOVERY_B_VALUE = "discovery.iso14443B"; + + private static final String PROPERTY_NFC_DISCOVERY_F_VALUE = "discovery.felica"; + + private static final String PROPERTY_NFC_DISCOVERY_15693_VALUE = "discovery.iso15693"; + + private static final String PROPERTY_NFC_DISCOVERY_NFCIP_VALUE = "discovery.nfcip"; + + private Context mContext; + + private HashMap<Integer, Object> mObjectMap = new HashMap<Integer, Object>(); + + private HashMap<Integer, Object> mSocketMap = new HashMap<Integer, Object>(); + + private LinkedList<RegisteredSocket> mRegisteredSocketList = new LinkedList<RegisteredSocket>(); + + private int mLlcpLinkState = NfcManager.LLCP_LINK_STATE_DEACTIVATED; + + private int mGeneratedSocketHandle = 0; + + private int mNbSocketCreated = 0; + + private boolean mIsNfcEnabled = false; + + private NfcHandler mNfcHandler; + + private int mSelectedSeId = 0; + + private int mTimeout = 0; + + private int mNfcState; + + private int mNfcSecureElementState; + + private boolean mOpenPending = false; + + private NativeNfcManager mManager; + + private ILlcpSocket mLlcpSocket = new ILlcpSocket.Stub() { + + public int close(int nativeHandle) throws RemoteException { + NativeLlcpSocket socket = null; + boolean isSuccess = false; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpSocket) findSocket(nativeHandle); + if (socket != null) { + if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) { + isSuccess = socket.doClose(); + if (isSuccess) { + /* Remove the socket closed from the hmap */ + RemoveSocket(nativeHandle); + /* Update mNbSocketCreated */ + mNbSocketCreated--; + return ErrorCodes.SUCCESS; + } else { + return ErrorCodes.ERROR_IO; + } + } else { + /* Remove the socket closed from the hmap */ + RemoveSocket(nativeHandle); + + /* Remove registered socket from the list */ + RemoveRegisteredSocket(nativeHandle); + + /* Update mNbSocketCreated */ + mNbSocketCreated--; + + return ErrorCodes.SUCCESS; + } + } else { + return ErrorCodes.ERROR_IO; + } + } + + public int connect(int nativeHandle, int sap) throws RemoteException { + NativeLlcpSocket socket = null; + boolean isSuccess = false; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpSocket) findSocket(nativeHandle); + if (socket != null) { + isSuccess = socket.doConnect(sap, socket.getConnectTimeout()); + if (isSuccess) { + return ErrorCodes.SUCCESS; + } else { + return ErrorCodes.ERROR_IO; + } + } else { + return ErrorCodes.ERROR_IO; + } + + } + + public int connectByName(int nativeHandle, String sn) throws RemoteException { + NativeLlcpSocket socket = null; + boolean isSuccess = false; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpSocket) findSocket(nativeHandle); + if (socket != null) { + isSuccess = socket.doConnectBy(sn, socket.getConnectTimeout()); + if (isSuccess) { + return ErrorCodes.SUCCESS; + } else { + return ErrorCodes.ERROR_IO; + } + } else { + return ErrorCodes.ERROR_IO; + } + + } + + public int getConnectTimeout(int nativeHandle) throws RemoteException { + NativeLlcpSocket socket = null; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpSocket) findSocket(nativeHandle); + if (socket != null) { + return socket.getConnectTimeout(); + } else { + return 0; + } + } + + public int getLocalSap(int nativeHandle) throws RemoteException { + NativeLlcpSocket socket = null; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpSocket) findSocket(nativeHandle); + if (socket != null) { + return socket.getSap(); + } else { + return 0; + } + } + + public int getLocalSocketMiu(int nativeHandle) throws RemoteException { + NativeLlcpSocket socket = null; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpSocket) findSocket(nativeHandle); + if (socket != null) { + return socket.getMiu(); + } else { + return 0; + } + } + + public int getLocalSocketRw(int nativeHandle) throws RemoteException { + NativeLlcpSocket socket = null; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpSocket) findSocket(nativeHandle); + if (socket != null) { + return socket.getRw(); + } else { + return 0; + } + } + + public int getRemoteSocketMiu(int nativeHandle) throws RemoteException { + NativeLlcpSocket socket = null; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpSocket) findSocket(nativeHandle); + if (socket != null) { + if (socket.doGetRemoteSocketMiu() != 0) { + return socket.doGetRemoteSocketMiu(); + } else { + return ErrorCodes.ERROR_SOCKET_NOT_CONNECTED; + } + } else { + return ErrorCodes.ERROR_SOCKET_NOT_CONNECTED; + } + } + + public int getRemoteSocketRw(int nativeHandle) throws RemoteException { + NativeLlcpSocket socket = null; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpSocket) findSocket(nativeHandle); + if (socket != null) { + if (socket.doGetRemoteSocketRw() != 0) { + return socket.doGetRemoteSocketRw(); + } else { + return ErrorCodes.ERROR_SOCKET_NOT_CONNECTED; + } + } else { + return ErrorCodes.ERROR_SOCKET_NOT_CONNECTED; + } + } + + public int receive(int nativeHandle, byte[] receiveBuffer) throws RemoteException { + NativeLlcpSocket socket = null; + int receiveLength = 0; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpSocket) findSocket(nativeHandle); + if (socket != null) { + receiveLength = socket.doReceive(receiveBuffer); + if (receiveLength != 0) { + return receiveLength; + } else { + return ErrorCodes.ERROR_IO; + } + } else { + return ErrorCodes.ERROR_IO; + } + } + + public int send(int nativeHandle, byte[] data) throws RemoteException { + NativeLlcpSocket socket = null; + boolean isSuccess = false; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpSocket) findSocket(nativeHandle); + if (socket != null) { + isSuccess = socket.doSend(data); + if (isSuccess) { + return ErrorCodes.SUCCESS; + } else { + return ErrorCodes.ERROR_IO; + } + } else { + return ErrorCodes.ERROR_IO; + } + } + + public void setConnectTimeout(int nativeHandle, int timeout) throws RemoteException { + NativeLlcpSocket socket = null; + + /* find the socket in the hmap */ + socket = (NativeLlcpSocket) findSocket(nativeHandle); + if (socket != null) { + socket.setConnectTimeout(timeout); + } + } + + }; + + private ILlcpServiceSocket mLlcpServerSocketService = new ILlcpServiceSocket.Stub() { + + public int accept(int nativeHandle) throws RemoteException { + NativeLlcpServiceSocket socket = null; + NativeLlcpSocket clientSocket = null; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + if (mNbSocketCreated < LLCP_SOCKET_NB_MAX) { + /* find the socket in the hmap */ + socket = (NativeLlcpServiceSocket) findSocket(nativeHandle); + if (socket != null) { + clientSocket = socket.doAccept(socket.getAcceptTimeout(), socket.getMiu(), + socket.getRw(), socket.getLinearBufferLength()); + if (clientSocket != null) { + /* Add the socket into the socket map */ + mSocketMap.put(clientSocket.getHandle(), clientSocket); + mNbSocketCreated++; + return clientSocket.getHandle(); + } else { + return ErrorCodes.ERROR_IO; + } + } else { + return ErrorCodes.ERROR_IO; + } + } else { + return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES; + } + + } + + public void close(int nativeHandle) throws RemoteException { + NativeLlcpServiceSocket socket = null; + boolean isSuccess = false; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpServiceSocket) findSocket(nativeHandle); + if (socket != null) { + if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) { + isSuccess = socket.doClose(); + if (isSuccess) { + /* Remove the socket closed from the hmap */ + RemoveSocket(nativeHandle); + /* Update mNbSocketCreated */ + mNbSocketCreated--; + } + } else { + /* Remove the socket closed from the hmap */ + RemoveSocket(nativeHandle); + + /* Remove registered socket from the list */ + RemoveRegisteredSocket(nativeHandle); + + /* Update mNbSocketCreated */ + mNbSocketCreated--; + } + } + } + + public int getAcceptTimeout(int nativeHandle) throws RemoteException { + NativeLlcpServiceSocket socket = null; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpServiceSocket) findSocket(nativeHandle); + if (socket != null) { + return socket.getAcceptTimeout(); + } else { + return 0; + } + } + + public void setAcceptTimeout(int nativeHandle, int timeout) throws RemoteException { + NativeLlcpServiceSocket socket = null; + + /* find the socket in the hmap */ + socket = (NativeLlcpServiceSocket) findSocket(nativeHandle); + if (socket != null) { + socket.setAcceptTimeout(timeout); + } + } + }; + + private ILlcpConnectionlessSocket mLlcpConnectionlessSocketService = new ILlcpConnectionlessSocket.Stub() { + + public void close(int nativeHandle) throws RemoteException { + NativeLlcpConnectionlessSocket socket = null; + boolean isSuccess = false; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpConnectionlessSocket) findSocket(nativeHandle); + if (socket != null) { + if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) { + isSuccess = socket.doClose(); + if (isSuccess) { + /* Remove the socket closed from the hmap */ + RemoveSocket(nativeHandle); + /* Update mNbSocketCreated */ + mNbSocketCreated--; + } + } else { + /* Remove the socket closed from the hmap */ + RemoveSocket(nativeHandle); + + /* Remove registered socket from the list */ + RemoveRegisteredSocket(nativeHandle); + + /* Update mNbSocketCreated */ + mNbSocketCreated--; + } + } + } + + public int getSap(int nativeHandle) throws RemoteException { + NativeLlcpConnectionlessSocket socket = null; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpConnectionlessSocket) findSocket(nativeHandle); + if (socket != null) { + return socket.getSap(); + } else { + return 0; + } + } + + public LlcpPacket receiveFrom(int nativeHandle) throws RemoteException { + NativeLlcpConnectionlessSocket socket = null; + LlcpPacket packet; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return null; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpConnectionlessSocket) findSocket(nativeHandle); + if (socket != null) { + packet = socket.doReceiveFrom(socket.getLinkMiu()); + if (packet != null) { + return packet; + } + return null; + } else { + return null; + } + } + + public int sendTo(int nativeHandle, LlcpPacket packet) throws RemoteException { + NativeLlcpConnectionlessSocket socket = null; + boolean isSuccess = false; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the socket in the hmap */ + socket = (NativeLlcpConnectionlessSocket) findSocket(nativeHandle); + if (socket != null) { + isSuccess = socket.doSendTo(packet.getRemoteSap(), packet.getDataBuffer()); + if (isSuccess) { + return ErrorCodes.SUCCESS; + } else { + return ErrorCodes.ERROR_IO; + } + } else { + return ErrorCodes.ERROR_IO; + } + } + }; + + private INfcTag mNfcTagService = new INfcTag.Stub() { + + public int close(int nativeHandle) throws RemoteException { + NativeNfcTag tag = null; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the tag in the hmap */ + tag = (NativeNfcTag) findObject(nativeHandle); + if (tag != null) { + if (tag.doDisconnect()) { + /* Remove the device from the hmap */ + RemoveObject(nativeHandle); + /* Restart polling loop for notification */ + mManager.enableDiscovery(DISCOVERY_MODE_READER); + mOpenPending = false; + return ErrorCodes.SUCCESS; + } + + } + /* Restart polling loop for notification */ + mManager.enableDiscovery(DISCOVERY_MODE_READER); + mOpenPending = false; + return ErrorCodes.ERROR_DISCONNECT; + } + + public int connect(int nativeHandle) throws RemoteException { + NativeNfcTag tag = null; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the tag in the hmap */ + tag = (NativeNfcTag) findObject(nativeHandle); + if (tag != null) { + if (tag.doConnect()) + return ErrorCodes.SUCCESS; + } + /* Restart polling loop for notification */ + mManager.enableDiscovery(DISCOVERY_MODE_READER); + mOpenPending = false; + return ErrorCodes.ERROR_CONNECT; + } + + public String getType(int nativeHandle) throws RemoteException { + NativeNfcTag tag = null; + String type; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return null; + } + + /* find the tag in the hmap */ + tag = (NativeNfcTag) findObject(nativeHandle); + if (tag != null) { + type = tag.getType(); + return type; + } + return null; + } + + public byte[] getUid(int nativeHandle) throws RemoteException { + NativeNfcTag tag = null; + byte[] uid; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return null; + } + + /* find the tag in the hmap */ + tag = (NativeNfcTag) findObject(nativeHandle); + if (tag != null) { + uid = tag.getUid(); + return uid; + } + return null; + } + + public boolean isNdef(int nativeHandle) throws RemoteException { + NativeNfcTag tag = null; + boolean isSuccess = false; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return isSuccess; + } + + /* find the tag in the hmap */ + tag = (NativeNfcTag) findObject(nativeHandle); + if (tag != null) { + isSuccess = tag.checkNDEF(); + } + return isSuccess; + } + + public byte[] transceive(int nativeHandle, byte[] data) throws RemoteException { + NativeNfcTag tag = null; + byte[] response; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return null; + } + + /* find the tag in the hmap */ + tag = (NativeNfcTag) findObject(nativeHandle); + if (tag != null) { + response = tag.doTransceive(data); + return response; + } + return null; + } + + public NdefMessage read(int nativeHandle) throws RemoteException { + NativeNfcTag tag; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return null; + } + + /* find the tag in the hmap */ + tag = (NativeNfcTag) findObject(nativeHandle); + if (tag != null) { + byte[] buf = tag.doRead(); + if (buf == null) + return null; + + /* Create an NdefMessage */ + try { + return new NdefMessage(buf); + } catch (NfcException e) { + return null; + } + } + return null; + } + + public boolean write(int nativeHandle, NdefMessage msg) throws RemoteException { + NativeNfcTag tag; + boolean isSuccess = false; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return isSuccess; + } + + /* find the tag in the hmap */ + tag = (NativeNfcTag) findObject(nativeHandle); + if (tag != null) { + isSuccess = tag.doWrite(msg.toByteArray()); + } + return isSuccess; + + } + + }; + + private IP2pInitiator mP2pInitiatorService = new IP2pInitiator.Stub() { + + public byte[] getGeneralBytes(int nativeHandle) throws RemoteException { + NativeP2pDevice device; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return null; + } + + /* find the device in the hmap */ + device = (NativeP2pDevice) findObject(nativeHandle); + if (device != null) { + byte[] buff = device.getGeneralBytes(); + if (buff == null) + return null; + return buff; + } + return null; + } + + public int getMode(int nativeHandle) throws RemoteException { + NativeP2pDevice device; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the device in the hmap */ + device = (NativeP2pDevice) findObject(nativeHandle); + if (device != null) { + return device.getMode(); + } + return ErrorCodes.ERROR_INVALID_PARAM; + } + + public byte[] receive(int nativeHandle) throws RemoteException { + NativeP2pDevice device; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return null; + } + + /* find the device in the hmap */ + device = (NativeP2pDevice) findObject(nativeHandle); + if (device != null) { + byte[] buff = device.doReceive(); + if (buff == null) + return null; + return buff; + } + /* Restart polling loop for notification */ + mManager.enableDiscovery(DISCOVERY_MODE_READER); + mOpenPending = false; + return null; + } + + public boolean send(int nativeHandle, byte[] data) throws RemoteException { + NativeP2pDevice device; + boolean isSuccess = false; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return isSuccess; + } + + /* find the device in the hmap */ + device = (NativeP2pDevice) findObject(nativeHandle); + if (device != null) { + isSuccess = device.doSend(data); + } + return isSuccess; + } + }; + + private IP2pTarget mP2pTargetService = new IP2pTarget.Stub() { + + public int connect(int nativeHandle) throws RemoteException { + NativeP2pDevice device; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the device in the hmap */ + device = (NativeP2pDevice) findObject(nativeHandle); + if (device != null) { + if (device.doConnect()) { + return ErrorCodes.SUCCESS; + } + } + return ErrorCodes.ERROR_CONNECT; + } + + public boolean disconnect(int nativeHandle) throws RemoteException { + NativeP2pDevice device; + boolean isSuccess = false; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return isSuccess; + } + + /* find the device in the hmap */ + device = (NativeP2pDevice) findObject(nativeHandle); + if (device != null) { + if (isSuccess = device.doDisconnect()) { + mOpenPending = false; + /* remove the device from the hmap */ + RemoveObject(nativeHandle); + /* Restart polling loop for notification */ + mManager.enableDiscovery(DISCOVERY_MODE_READER); + } + } + return isSuccess; + + } + + public byte[] getGeneralBytes(int nativeHandle) throws RemoteException { + NativeP2pDevice device; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return null; + } + + /* find the device in the hmap */ + device = (NativeP2pDevice) findObject(nativeHandle); + if (device != null) { + byte[] buff = device.getGeneralBytes(); + if (buff == null) + return null; + return buff; + } + return null; + } + + public int getMode(int nativeHandle) throws RemoteException { + NativeP2pDevice device; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + /* find the device in the hmap */ + device = (NativeP2pDevice) findObject(nativeHandle); + if (device != null) { + return device.getMode(); + } + return ErrorCodes.ERROR_INVALID_PARAM; + } + + public byte[] transceive(int nativeHandle, byte[] data) throws RemoteException { + NativeP2pDevice device; + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return null; + } + + /* find the device in the hmap */ + device = (NativeP2pDevice) findObject(nativeHandle); + if (device != null) { + byte[] buff = device.doTransceive(data); + if (buff == null) + return null; + return buff; + } + return null; + } + }; + + private class NfcHandler extends Handler { + + @Override + public void handleMessage(Message msg) { + try { + + } catch (Exception e) { + // Log, don't crash! + Log.e(TAG, "Exception in NfcHandler.handleMessage:", e); + } + } + + }; + + public NfcService(Context context) { + super(); + mContext = context; + mManager = new NativeNfcManager(mContext); + + mContext.registerReceiver(mNfcServiceReceiver, new IntentFilter( + NativeNfcManager.INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION)); + + mContext.registerReceiver(mNfcServiceReceiver, new IntentFilter( + NfcManager.LLCP_LINK_STATE_CHANGED_ACTION)); + + mContext.registerReceiver(mNfcServiceReceiver, new IntentFilter( + NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION)); + + Thread thread = new Thread(null, this, "NfcService"); + thread.start(); + + mManager.initializeNativeStructure(); + + int nfcState = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_ON, 0); + + if (nfcState == NFC_STATE_ENABLED) { + if (this._enable()) { + } + } + + } + + public void run() { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + Looper.prepare(); + mNfcHandler = new NfcHandler(); + Looper.loop(); + } + + public void cancel() throws RemoteException { + mContext.enforceCallingPermission(android.Manifest.permission.NFC_RAW, + "NFC_RAW permission required to cancel NFC opening"); + if (mOpenPending) { + mOpenPending = false; + mManager.doCancel(); + /* Restart polling loop for notification */ + mManager.enableDiscovery(DISCOVERY_MODE_READER); + } + } + + public int createLlcpConnectionlessSocket(int sap) throws RemoteException { + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + mContext.enforceCallingPermission(android.Manifest.permission.NFC_LLCP, + "NFC_LLCP permission required for LLCP operations with NFC service"); + + /* Check SAP is not already used */ + + /* Check nb socket created */ + if (mNbSocketCreated < LLCP_SOCKET_NB_MAX) { + /* Store the socket handle */ + int sockeHandle = mGeneratedSocketHandle; + + if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) { + NativeLlcpConnectionlessSocket socket; + + socket = mManager.doCreateLlcpConnectionlessSocket(sap); + if (socket != null) { + /* Update the number of socket created */ + mNbSocketCreated++; + + /* Add the socket into the socket map */ + mSocketMap.put(sockeHandle, socket); + + return sockeHandle; + } else { + /* + * socket creation error - update the socket handle + * generation + */ + mGeneratedSocketHandle -= 1; + + /* Get Error Status */ + int errorStatus = mManager.doGetLastError(); + + switch (errorStatus) { + case ErrorCodes.ERROR_BUFFER_TO_SMALL: + return ErrorCodes.ERROR_BUFFER_TO_SMALL; + case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES: + return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES; + default: + return ErrorCodes.ERROR_SOCKET_CREATION; + } + } + } else { + /* Check SAP is not already used */ + if (!CheckSocketSap(sap)) { + return ErrorCodes.ERROR_SAP_USED; + } + + NativeLlcpConnectionlessSocket socket = new NativeLlcpConnectionlessSocket(sap); + + /* Add the socket into the socket map */ + mSocketMap.put(sockeHandle, socket); + + /* Update the number of socket created */ + mNbSocketCreated++; + + /* Create new registered socket */ + RegisteredSocket registeredSocket = new RegisteredSocket( + LLCP_CONNECTIONLESS_SOCKET_TYPE, sockeHandle, sap); + + /* Put this socket into a list of registered socket */ + mRegisteredSocketList.add(registeredSocket); + } + + /* update socket handle generation */ + mGeneratedSocketHandle++; + + return sockeHandle; + + } else { + /* No socket available */ + return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES; + } + + } + + public int createLlcpServiceSocket(int sap, String sn, int miu, int rw, int linearBufferLength) + throws RemoteException { + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + mContext.enforceCallingPermission(android.Manifest.permission.NFC_LLCP, + "NFC_LLCP permission required for LLCP operations with NFC service"); + + if (mNbSocketCreated < LLCP_SOCKET_NB_MAX) { + int sockeHandle = mGeneratedSocketHandle; + + if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) { + NativeLlcpServiceSocket socket; + + socket = mManager.doCreateLlcpServiceSocket(sap, sn, miu, rw, linearBufferLength); + if (socket != null) { + /* Update the number of socket created */ + mNbSocketCreated++; + /* Add the socket into the socket map */ + mSocketMap.put(sockeHandle, socket); + } else { + /* socket creation error - update the socket handle counter */ + mGeneratedSocketHandle -= 1; + + /* Get Error Status */ + int errorStatus = mManager.doGetLastError(); + + switch (errorStatus) { + case ErrorCodes.ERROR_BUFFER_TO_SMALL: + return ErrorCodes.ERROR_BUFFER_TO_SMALL; + case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES: + return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES; + default: + return ErrorCodes.ERROR_SOCKET_CREATION; + } + } + } else { + + /* Check SAP is not already used */ + if (!CheckSocketSap(sap)) { + return ErrorCodes.ERROR_SAP_USED; + } + + /* Service Name */ + if (!CheckSocketServiceName(sn)) { + return ErrorCodes.ERROR_SERVICE_NAME_USED; + } + + /* Check socket options */ + if (!CheckSocketOptions(miu, rw, linearBufferLength)) { + return ErrorCodes.ERROR_SOCKET_OPTIONS; + } + + NativeLlcpServiceSocket socket = new NativeLlcpServiceSocket(sn); + + /* Add the socket into the socket map */ + mSocketMap.put(sockeHandle, socket); + + /* Update the number of socket created */ + mNbSocketCreated++; + + /* Create new registered socket */ + RegisteredSocket registeredSocket = new RegisteredSocket(LLCP_SERVICE_SOCKET_TYPE, + sockeHandle, sap, sn, miu, rw, linearBufferLength); + + /* Put this socket into a list of registered socket */ + mRegisteredSocketList.add(registeredSocket); + } + + /* update socket handle generation */ + mGeneratedSocketHandle += 1; + + Log.d(TAG, "Llcp Service Socket Handle =" + sockeHandle); + return sockeHandle; + } else { + /* No socket available */ + return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES; + } + } + + public int createLlcpSocket(int sap, int miu, int rw, int linearBufferLength) + throws RemoteException { + + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + mContext.enforceCallingPermission(android.Manifest.permission.NFC_LLCP, + "NFC_LLCP permission required for LLCP operations with NFC service"); + + if (mNbSocketCreated < LLCP_SOCKET_NB_MAX) { + + int sockeHandle = mGeneratedSocketHandle; + + if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) { + NativeLlcpSocket socket; + + socket = mManager.doCreateLlcpSocket(sap, miu, rw, linearBufferLength); + + if (socket != null) { + /* Update the number of socket created */ + mNbSocketCreated++; + /* Add the socket into the socket map */ + mSocketMap.put(sockeHandle, socket); + } else { + /* + * socket creation error - update the socket handle + * generation + */ + mGeneratedSocketHandle -= 1; + + /* Get Error Status */ + int errorStatus = mManager.doGetLastError(); + + switch (errorStatus) { + case ErrorCodes.ERROR_BUFFER_TO_SMALL: + return ErrorCodes.ERROR_BUFFER_TO_SMALL; + case ErrorCodes.ERROR_INSUFFICIENT_RESOURCES: + return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES; + default: + return ErrorCodes.ERROR_SOCKET_CREATION; + } + } + } else { + + /* Check SAP is not already used */ + if (!CheckSocketSap(sap)) { + return ErrorCodes.ERROR_SAP_USED; + } + + /* Check Socket options */ + if (!CheckSocketOptions(miu, rw, linearBufferLength)) { + return ErrorCodes.ERROR_SOCKET_OPTIONS; + } + + NativeLlcpSocket socket = new NativeLlcpSocket(sap, miu, rw); + + /* Add the socket into the socket map */ + mSocketMap.put(sockeHandle, socket); + + /* Update the number of socket created */ + mNbSocketCreated++; + /* Create new registered socket */ + RegisteredSocket registeredSocket = new RegisteredSocket(LLCP_SOCKET_TYPE, + sockeHandle, sap, miu, rw, linearBufferLength); + + /* Put this socket into a list of registered socket */ + mRegisteredSocketList.add(registeredSocket); + } + + /* update socket handle generation */ + mGeneratedSocketHandle++; + + return sockeHandle; + } else { + /* No socket available */ + return ErrorCodes.ERROR_INSUFFICIENT_RESOURCES; + } + } + + public int deselectSecureElement() throws RemoteException { + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + if (mSelectedSeId == 0) { + return ErrorCodes.ERROR_NO_SE_CONNECTED; + } + + mContext.enforceCallingPermission(android.Manifest.permission.NFC_ADMIN, + "NFC_ADMIN permission required to deselect NFC Secure Element"); + + mManager.doDeselectSecureElement(mSelectedSeId); + mNfcSecureElementState = 0; + mSelectedSeId = 0; + + /* Store that a secure element is deselected */ + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.NFC_SECURE_ELEMENT_ON, 0); + + /* Reset Secure Element ID */ + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.NFC_SECURE_ELEMENT_ID, 0); + + + return ErrorCodes.SUCCESS; + } + + public boolean disable() throws RemoteException { + boolean isSuccess = false; + mContext.enforceCallingPermission(android.Manifest.permission.NFC_ADMIN, + "NFC_ADMIN permission required to disable NFC service"); + if (isEnabled()) { + isSuccess = mManager.deinitialize(); + if (isSuccess) { + mIsNfcEnabled = false; + } + } + + updateNfcOnSetting(); + + return isSuccess; + } + + public boolean enable() throws RemoteException { + boolean isSuccess = false; + mContext.enforceCallingPermission(android.Manifest.permission.NFC_ADMIN, + "NFC_ADMIN permission required to enable NFC service"); + if (!isEnabled()) { + reset(); + isSuccess = _enable(); + } + return isSuccess; + } + + private boolean _enable() { + boolean isSuccess = mManager.initialize(); + if (isSuccess) { + /* Check persistent properties */ + checkProperties(); + + /* Check Secure Element setting */ + mNfcSecureElementState = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_SECURE_ELEMENT_ON, 0); + + if (mNfcSecureElementState == 1) { + + int secureElementId = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_SECURE_ELEMENT_ID, 0); + int[] Se_list = mManager.doGetSecureElementList(); + if (Se_list != null) { + for (int i = 0; i < Se_list.length; i++) { + if (Se_list[i] == secureElementId) { + mManager.doSelectSecureElement(Se_list[i]); + mSelectedSeId = Se_list[i]; + break; + } + } + } + } + + /* Start polling loop */ + mManager.enableDiscovery(DISCOVERY_MODE_READER); + + mIsNfcEnabled = true; + } else { + mIsNfcEnabled = false; + } + + updateNfcOnSetting(); + + return isSuccess; + } + + private void updateNfcOnSetting() { + int state; + + if (mIsNfcEnabled) { + state = NFC_STATE_ENABLED; + } else { + state = NFC_STATE_DISABLED; + } + + Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_ON, state); + } + + private void checkProperties() { + int value; + + /* LLCP LTO */ + value = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_LTO, + LLCP_LTO_DEFAULT_VALUE); + Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_LTO, value); + mManager.doSetProperties(PROPERTY_LLCP_LTO, value); + + /* LLCP MIU */ + value = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_MIU, + LLCP_MIU_DEFAULT_VALUE); + Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_MIU, value); + mManager.doSetProperties(PROPERTY_LLCP_MIU, value); + + /* LLCP WKS */ + value = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_WKS, + LLCP_WKS_DEFAULT_VALUE); + Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_WKS, value); + mManager.doSetProperties(PROPERTY_LLCP_WKS, value); + + /* LLCP OPT */ + value = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_OPT, + LLCP_OPT_DEFAULT_VALUE); + Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_OPT, value); + mManager.doSetProperties(PROPERTY_LLCP_OPT, value); + + /* NFC READER A */ + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_DISCOVERY_A, 1); + Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_A, + value); + mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_A, value); + + /* NFC READER B */ + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_DISCOVERY_B, 1); + Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_B, + value); + mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_B, value); + + /* NFC READER F */ + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_DISCOVERY_F, 1); + Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_F, + value); + mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_F, value); + + /* NFC READER 15693 */ + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_DISCOVERY_15693, 1); + Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_15693, + value); + mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_15693, value); + + /* NFC NFCIP */ + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_DISCOVERY_NFCIP, 1); + Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_NFCIP, + value); + mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_NFCIP, value); + } + + public ILlcpConnectionlessSocket getLlcpConnectionlessInterface() throws RemoteException { + return mLlcpConnectionlessSocketService; + } + + public ILlcpSocket getLlcpInterface() throws RemoteException { + return mLlcpSocket; + } + + public ILlcpServiceSocket getLlcpServiceInterface() throws RemoteException { + return mLlcpServerSocketService; + } + + public INfcTag getNfcTagInterface() throws RemoteException { + return mNfcTagService; + } + + public int getOpenTimeout() throws RemoteException { + return mTimeout; + } + + public IP2pInitiator getP2pInitiatorInterface() throws RemoteException { + return mP2pInitiatorService; + } + + public IP2pTarget getP2pTargetInterface() throws RemoteException { + return mP2pTargetService; + } + + public String getProperties(String param) throws RemoteException { + int value; + + if (param == null) { + return "Wrong parameter"; + } + + if (param.equals(PROPERTY_LLCP_LTO_VALUE)) { + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_LLCP_LTO, 0); + } else if (param.equals(PROPERTY_LLCP_MIU_VALUE)) { + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_LLCP_MIU, 0); + } else if (param.equals(PROPERTY_LLCP_WKS_VALUE)) { + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_LLCP_WKS, 0); + } else if (param.equals(PROPERTY_LLCP_OPT_VALUE)) { + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_LLCP_OPT, 0); + } else if (param.equals(PROPERTY_NFC_DISCOVERY_A_VALUE)) { + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_DISCOVERY_A, 0); + } else if (param.equals(PROPERTY_NFC_DISCOVERY_B_VALUE)) { + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_DISCOVERY_B, 0); + } else if (param.equals(PROPERTY_NFC_DISCOVERY_F_VALUE)) { + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_DISCOVERY_F, 0); + } else if (param.equals(PROPERTY_NFC_DISCOVERY_NFCIP_VALUE)) { + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_DISCOVERY_NFCIP, 0); + } else if (param.equals(PROPERTY_NFC_DISCOVERY_15693_VALUE)) { + value = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NFC_DISCOVERY_15693, 0); + } else { + return "Unknown property"; + } + + if (param.equals(PROPERTY_NFC_DISCOVERY_A_VALUE) + || param.equals(PROPERTY_NFC_DISCOVERY_B_VALUE) + || param.equals(PROPERTY_NFC_DISCOVERY_F_VALUE) + || param.equals(PROPERTY_NFC_DISCOVERY_NFCIP_VALUE) + || param.equals(PROPERTY_NFC_DISCOVERY_15693_VALUE)) { + if (value == 0) { + return "false"; + } else if (value == 1) { + return "true"; + } else { + return "Unknown Value"; + } + }else{ + return "" + value; + } + + } + + public int[] getSecureElementList() throws RemoteException { + int[] list = null; + if (mIsNfcEnabled == true) { + list = mManager.doGetSecureElementList(); + } + return list; + } + + public int getSelectedSecureElement() throws RemoteException { + return mSelectedSeId; + } + + public boolean isEnabled() throws RemoteException { + return mIsNfcEnabled; + } + + public int openP2pConnection() throws RemoteException { + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + mContext.enforceCallingPermission(android.Manifest.permission.NFC_RAW, + "NFC_RAW permission required to open NFC P2P connection"); + if (!mOpenPending) { + NativeP2pDevice device; + mOpenPending = true; + device = mManager.doOpenP2pConnection(mTimeout); + if (device != null) { + /* add device to the Hmap */ + mObjectMap.put(device.getHandle(), device); + return device.getHandle(); + } else { + mOpenPending = false; + /* Restart polling loop for notification */ + mManager.enableDiscovery(DISCOVERY_MODE_READER); + return ErrorCodes.ERROR_IO; + } + } else { + return ErrorCodes.ERROR_BUSY; + } + + } + + public int openTagConnection() throws RemoteException { + NativeNfcTag tag; + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + mContext.enforceCallingPermission(android.Manifest.permission.NFC_RAW, + "NFC_RAW permission required to open NFC Tag connection"); + if (!mOpenPending) { + mOpenPending = true; + tag = mManager.doOpenTagConnection(mTimeout); + if (tag != null) { + mObjectMap.put(tag.getHandle(), tag); + return tag.getHandle(); + } else { + mOpenPending = false; + /* Restart polling loop for notification */ + mManager.enableDiscovery(DISCOVERY_MODE_READER); + return ErrorCodes.ERROR_IO; + } + } else { + return ErrorCodes.ERROR_BUSY; + } + } + + public int selectSecureElement(int seId) throws RemoteException { + // Check if NFC is enabled + if (!mIsNfcEnabled) { + return ErrorCodes.ERROR_NOT_INITIALIZED; + } + + if (mSelectedSeId == seId) { + return ErrorCodes.ERROR_SE_ALREADY_SELECTED; + } + + if (mSelectedSeId != 0) { + return ErrorCodes.ERROR_SE_CONNECTED; + } + + mContext.enforceCallingPermission(android.Manifest.permission.NFC_ADMIN, + "NFC_ADMIN permission required to select NFC Secure Element"); + + mSelectedSeId = seId; + mManager.doSelectSecureElement(mSelectedSeId); + + /* Store that a secure element is selected */ + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.NFC_SECURE_ELEMENT_ON, 1); + + /* Store the ID of the Secure Element Selected */ + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.NFC_SECURE_ELEMENT_ID, mSelectedSeId); + + mNfcSecureElementState = 1; + + return ErrorCodes.SUCCESS; + + } + + public void setOpenTimeout(int timeout) throws RemoteException { + mContext.enforceCallingPermission(android.Manifest.permission.NFC_RAW, + "NFC_RAW permission required to set NFC connection timeout"); + mTimeout = timeout; + } + + public int setProperties(String param, String value) throws RemoteException { + mContext.enforceCallingPermission(android.Manifest.permission.NFC_ADMIN, + "NFC_ADMIN permission required to set NFC Properties"); + + if (isEnabled()) { + return ErrorCodes.ERROR_NFC_ON; + } + + int val; + + /* Check params validity */ + if (param == null || value == null) { + return ErrorCodes.ERROR_INVALID_PARAM; + } + + if (param.equals(PROPERTY_LLCP_LTO_VALUE)) { + val = Integer.parseInt(value); + + /* Check params */ + if (val > LLCP_LTO_MAX_VALUE) + return ErrorCodes.ERROR_INVALID_PARAM; + + /* Store value */ + Settings.System + .putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_LTO, val); + + /* Update JNI */ + mManager.doSetProperties(PROPERTY_LLCP_LTO, val); + + } else if (param.equals(PROPERTY_LLCP_MIU_VALUE)) { + val = Integer.parseInt(value); + + /* Check params */ + if ((val < LLCP_MIU_DEFAULT_VALUE) || (val > LLCP_MIU_MAX_VALUE)) + return ErrorCodes.ERROR_INVALID_PARAM; + + /* Store value */ + Settings.System + .putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_MIU, val); + + /* Update JNI */ + mManager.doSetProperties(PROPERTY_LLCP_MIU, val); + + } else if (param.equals(PROPERTY_LLCP_WKS_VALUE)) { + val = Integer.parseInt(value); + + /* Check params */ + if (val > LLCP_WKS_MAX_VALUE) + return ErrorCodes.ERROR_INVALID_PARAM; + + /* Store value */ + Settings.System + .putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_WKS, val); + + /* Update JNI */ + mManager.doSetProperties(PROPERTY_LLCP_WKS, val); + + } else if (param.equals(PROPERTY_LLCP_OPT_VALUE)) { + val = Integer.parseInt(value); + + /* Check params */ + if (val > LLCP_OPT_MAX_VALUE) + return ErrorCodes.ERROR_INVALID_PARAM; + + /* Store value */ + Settings.System + .putInt(mContext.getContentResolver(), Settings.System.NFC_LLCP_OPT, val); + + /* Update JNI */ + mManager.doSetProperties(PROPERTY_LLCP_OPT, val); + + } else if (param.equals(PROPERTY_NFC_DISCOVERY_A_VALUE)) { + + /* Check params */ + if (value.equals("true")) { + val = 1; + } else if (value.equals("false")) { + val = 0; + } else { + return ErrorCodes.ERROR_INVALID_PARAM; + } + /* Store value */ + Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_A, + val); + + /* Update JNI */ + mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_A, val); + + } else if (param.equals(PROPERTY_NFC_DISCOVERY_B_VALUE)) { + + /* Check params */ + if (value.equals("true")) { + val = 1; + } else if (value.equals("false")) { + val = 0; + } else { + return ErrorCodes.ERROR_INVALID_PARAM; + } + + /* Store value */ + Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_B, + val); + + /* Update JNI */ + mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_B, val); + + } else if (param.equals(PROPERTY_NFC_DISCOVERY_F_VALUE)) { + + /* Check params */ + if (value.equals("true")) { + val = 1; + } else if (value.equals("false")) { + val = 0; + } else { + return ErrorCodes.ERROR_INVALID_PARAM; + } + + /* Store value */ + Settings.System.putInt(mContext.getContentResolver(), Settings.System.NFC_DISCOVERY_F, + val); + + /* Update JNI */ + mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_F, val); + + } else if (param.equals(PROPERTY_NFC_DISCOVERY_15693_VALUE)) { + + /* Check params */ + if (value.equals("true")) { + val = 1; + } else if (value.equals("false")) { + val = 0; + } else { + return ErrorCodes.ERROR_INVALID_PARAM; + } + + /* Store value */ + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.NFC_DISCOVERY_15693, val); + + /* Update JNI */ + mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_15693, val); + + } else if (param.equals(PROPERTY_NFC_DISCOVERY_NFCIP_VALUE)) { + + /* Check params */ + if (value.equals("true")) { + val = 1; + } else if (value.equals("false")) { + val = 0; + } else { + return ErrorCodes.ERROR_INVALID_PARAM; + } + + /* Store value */ + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.NFC_DISCOVERY_NFCIP, val); + + /* Update JNI */ + mManager.doSetProperties(PROPERTY_NFC_DISCOVERY_NFCIP, val); + } else { + return ErrorCodes.ERROR_INVALID_PARAM; + } + + return ErrorCodes.SUCCESS; + } + + // Reset all internals + private void reset() { + + // Clear tables + mObjectMap.clear(); + mSocketMap.clear(); + mRegisteredSocketList.clear(); + + // Reset variables + mLlcpLinkState = NfcManager.LLCP_LINK_STATE_DEACTIVATED; + mNbSocketCreated = 0; + mIsNfcEnabled = false; + mSelectedSeId = 0; + mTimeout = 0; + mNfcState = NFC_STATE_DISABLED; + mOpenPending = false; + } + + private Object findObject(int key) { + Object device = null; + + device = mObjectMap.get(key); + + return device; + } + + private void RemoveObject(int key) { + mObjectMap.remove(key); + } + + private Object findSocket(int key) { + Object socket = null; + + socket = mSocketMap.get(key); + + return socket; + } + + private void RemoveSocket(int key) { + mSocketMap.remove(key); + } + + private boolean CheckSocketSap(int sap) { + /* List of sockets registered */ + ListIterator<RegisteredSocket> it = mRegisteredSocketList.listIterator(); + + while (it.hasNext()) { + RegisteredSocket registeredSocket = it.next(); + + if (sap == registeredSocket.mSap) { + /* SAP already used */ + return false; + } + } + return true; + } + + private boolean CheckSocketOptions(int miu, int rw, int linearBufferlength) { + + if (rw > LLCP_RW_MAX_VALUE || miu < LLCP_MIU_DEFAULT_VALUE || linearBufferlength < miu) { + return false; + } + return true; + } + + private boolean CheckSocketServiceName(String sn) { + + /* List of sockets registered */ + ListIterator<RegisteredSocket> it = mRegisteredSocketList.listIterator(); + + while (it.hasNext()) { + RegisteredSocket registeredSocket = it.next(); + + if (sn.equals(registeredSocket.mServiceName)) { + /* Service Name already used */ + return false; + } + } + return true; + } + + private void RemoveRegisteredSocket(int nativeHandle) { + /* check if sockets are registered */ + ListIterator<RegisteredSocket> it = mRegisteredSocketList.listIterator(); + + while (it.hasNext()) { + RegisteredSocket registeredSocket = it.next(); + if (registeredSocket.mHandle == nativeHandle) { + /* remove the registered socket from the list */ + it.remove(); + Log.d(TAG, "socket removed"); + } + } + } + + /* + * RegisteredSocket class to store the creation request of socket until the + * LLCP link in not activated + */ + private class RegisteredSocket { + private int mType; + + private int mHandle; + + private int mSap; + + private int mMiu; + + private int mRw; + + private String mServiceName; + + private int mlinearBufferLength; + + RegisteredSocket(int type, int handle, int sap, String sn, int miu, int rw, + int linearBufferLength) { + mType = type; + mHandle = handle; + mSap = sap; + mServiceName = sn; + mRw = rw; + mMiu = miu; + mlinearBufferLength = linearBufferLength; + } + + RegisteredSocket(int type, int handle, int sap, int miu, int rw, int linearBufferLength) { + mType = type; + mHandle = handle; + mSap = sap; + mRw = rw; + mMiu = miu; + mlinearBufferLength = linearBufferLength; + } + + RegisteredSocket(int type, int handle, int sap) { + mType = type; + mHandle = handle; + mSap = sap; + } + } + + private BroadcastReceiver mNfcServiceReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "Internal NFC Intent received"); + + /* LLCP Link deactivation */ + if (intent.getAction().equals(NfcManager.LLCP_LINK_STATE_CHANGED_ACTION)) { + mLlcpLinkState = intent.getIntExtra(NfcManager.LLCP_LINK_STATE_CHANGED_EXTRA, + NfcManager.LLCP_LINK_STATE_DEACTIVATED); + + if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_DEACTIVATED) { + /* restart polling loop */ + mManager.enableDiscovery(DISCOVERY_MODE_READER); + } + + } + /* LLCP Link activation */ + else if (intent.getAction().equals( + NativeNfcManager.INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION)) { + + mLlcpLinkState = intent.getIntExtra( + NativeNfcManager.INTERNAL_LLCP_LINK_STATE_CHANGED_EXTRA, + NfcManager.LLCP_LINK_STATE_DEACTIVATED); + + if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) { + /* check if sockets are registered */ + ListIterator<RegisteredSocket> it = mRegisteredSocketList.listIterator(); + + Log.d(TAG, "Nb socket resgistered = " + mRegisteredSocketList.size()); + + while (it.hasNext()) { + RegisteredSocket registeredSocket = it.next(); + + switch (registeredSocket.mType) { + case LLCP_SERVICE_SOCKET_TYPE: + Log.d(TAG, "Registered Llcp Service Socket"); + NativeLlcpServiceSocket serviceSocket; + + serviceSocket = mManager.doCreateLlcpServiceSocket( + registeredSocket.mSap, registeredSocket.mServiceName, + registeredSocket.mMiu, registeredSocket.mRw, + registeredSocket.mlinearBufferLength); + + if (serviceSocket != null) { + /* Add the socket into the socket map */ + mSocketMap.put(registeredSocket.mHandle, serviceSocket); + } else { + /* + * socket creation error - update the socket + * handle counter + */ + mGeneratedSocketHandle -= 1; + } + break; + + case LLCP_SOCKET_TYPE: + Log.d(TAG, "Registered Llcp Socket"); + NativeLlcpSocket clientSocket; + clientSocket = mManager.doCreateLlcpSocket(registeredSocket.mSap, + registeredSocket.mMiu, registeredSocket.mRw, + registeredSocket.mlinearBufferLength); + if (clientSocket != null) { + /* Add the socket into the socket map */ + mSocketMap.put(registeredSocket.mHandle, clientSocket); + } else { + /* + * socket creation error - update the socket + * handle counter + */ + mGeneratedSocketHandle -= 1; + } + break; + + case LLCP_CONNECTIONLESS_SOCKET_TYPE: + Log.d(TAG, "Registered Llcp Connectionless Socket"); + NativeLlcpConnectionlessSocket connectionlessSocket; + connectionlessSocket = mManager + .doCreateLlcpConnectionlessSocket(registeredSocket.mSap); + if (connectionlessSocket != null) { + /* Add the socket into the socket map */ + mSocketMap.put(registeredSocket.mHandle, connectionlessSocket); + } else { + /* + * socket creation error - update the socket + * handle counter + */ + mGeneratedSocketHandle -= 1; + } + break; + + } + } + + /* Remove all registered socket from the list */ + mRegisteredSocketList.clear(); + + /* Broadcast Intent Link LLCP activated */ + Intent LlcpLinkIntent = new Intent(); + LlcpLinkIntent.setAction(NfcManager.LLCP_LINK_STATE_CHANGED_ACTION); + + LlcpLinkIntent.putExtra(NfcManager.LLCP_LINK_STATE_CHANGED_EXTRA, + NfcManager.LLCP_LINK_STATE_ACTIVATED); + + Log.d(TAG, "Broadcasting LLCP activation"); + mContext.sendOrderedBroadcast(LlcpLinkIntent, + android.Manifest.permission.NFC_LLCP); + } + } + /* Target Deactivated */ + else if (intent.getAction().equals( + NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION)) { + if(mOpenPending != false){ + mOpenPending = false; + } + /* Restart polling loop for notification */ + mManager.enableDiscovery(DISCOVERY_MODE_READER); + + } + } + }; +} diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java index 3b6de6f..0d983b5 100644 --- a/telephony/java/com/android/internal/telephony/Connection.java +++ b/telephony/java/com/android/internal/telephony/Connection.java @@ -42,6 +42,7 @@ public abstract class Connection { NUMBER_UNREACHABLE, /* cannot reach the peer */ INVALID_CREDENTIALS, /* invalid credentials */ OUT_OF_NETWORK, /* calling from out of network is not allowed */ + SERVER_ERROR, /* server error */ TIMED_OUT, /* client timed out */ LOST_SIGNAL, LIMIT_EXCEEDED, /* eg GSM ACM limit exceeded */ diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java index 999da9f..bceceda 100755 --- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java +++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java @@ -441,18 +441,23 @@ public class SipPhone extends SipPhoneBase { @Override public void hangup() throws CallStateException { synchronized (SipPhone.class) { - Log.v(LOG_TAG, "hang up call: " + getState() + ": " + this - + " on phone " + getPhone()); - CallStateException excp = null; - for (Connection c : connections) { - try { - c.hangup(); - } catch (CallStateException e) { - excp = e; + if (state.isAlive()) { + Log.d(LOG_TAG, "hang up call: " + getState() + ": " + this + + " on phone " + getPhone()); + CallStateException excp = null; + for (Connection c : connections) { + try { + c.hangup(); + } catch (CallStateException e) { + excp = e; + } } + if (excp != null) throw excp; + setState(State.DISCONNECTING); + } else { + Log.d(LOG_TAG, "hang up dead call: " + getState() + ": " + + this + " on phone " + getPhone()); } - if (excp != null) throw excp; - setState(State.DISCONNECTING); } } @@ -637,14 +642,14 @@ public class SipPhone extends SipPhoneBase { @Override public void onCallEstablished(SipAudioCall call) { - call.startAudio(); onChanged(call); + if (mState == Call.State.ACTIVE) call.startAudio(); } @Override public void onCallHeld(SipAudioCall call) { - call.startAudio(); onChanged(call); + if (mState == Call.State.HOLDING) call.startAudio(); } @Override @@ -784,11 +789,13 @@ public class SipPhone extends SipPhoneBase { public void hangup() throws CallStateException { synchronized (SipPhone.class) { Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": " - + ": on phone " + getPhone().getPhoneName()); + + mState + ": on phone " + getPhone().getPhoneName()); try { - if (mSipAudioCall != null) mSipAudioCall.endCall(); - setState(Call.State.DISCONNECTING); - setDisconnectCause(DisconnectCause.LOCAL); + if (mState.isAlive()) { + if (mSipAudioCall != null) mSipAudioCall.endCall(); + setState(Call.State.DISCONNECTING); + setDisconnectCause(DisconnectCause.LOCAL); + } } catch (SipException e) { throw new CallStateException("hangup(): " + e); } @@ -876,8 +883,10 @@ public class SipPhone extends SipPhoneBase { case SipErrorCode.CROSS_DOMAIN_AUTHENTICATION: onError(Connection.DisconnectCause.OUT_OF_NETWORK); break; - case SipErrorCode.SOCKET_ERROR: case SipErrorCode.SERVER_ERROR: + onError(Connection.DisconnectCause.SERVER_ERROR); + break; + case SipErrorCode.SOCKET_ERROR: case SipErrorCode.CLIENT_ERROR: default: Log.w(LOG_TAG, "error: " + SipErrorCode.toString(errorCode) diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk index 135a633..b27ce0e 100644 --- a/tools/layoutlib/Android.mk +++ b/tools/layoutlib/Android.mk @@ -25,15 +25,11 @@ include $(CLEAR_VARS) # We need to process the framework classes.jar file, but we can't # depend directly on it (private vars won't be inherited correctly). # So, we depend on framework's BUILT file. -built_framework_dep := \ - $(call intermediates-dir-for,JAVA_LIBRARIES,framework)/javalib.jar -built_framework_classes := \ - $(call intermediates-dir-for,JAVA_LIBRARIES,framework)/classes.jar +built_framework_dep := $(call java-lib-deps,framework) +built_framework_classes := $(call java-lib-files,framework) -built_core_dep := \ - $(call intermediates-dir-for,JAVA_LIBRARIES,core)/javalib.jar -built_core_classes := \ - $(call intermediates-dir-for,JAVA_LIBRARIES,core)/classes.jar +built_core_dep := $(call java-lib-deps,core) +built_core_classes := $(call java-lib-files,core) built_layoutlib_create_jar := $(call intermediates-dir-for, \ JAVA_LIBRARIES,layoutlib_create,HOST)/javalib.jar diff --git a/voip/java/android/net/rtp/AudioCodec.java b/voip/java/android/net/rtp/AudioCodec.java index 4851a46..f171806 100644 --- a/voip/java/android/net/rtp/AudioCodec.java +++ b/voip/java/android/net/rtp/AudioCodec.java @@ -81,7 +81,7 @@ public class AudioCodec { public static final AudioCodec AMR = new AudioCodec(97, "AMR/8000", null); // TODO: add rest of the codecs when the native part is done. - private static final AudioCodec[] sCodecs = {PCMU, PCMA}; + private static final AudioCodec[] sCodecs = {GSM_EFR, GSM, PCMU, PCMA}; private AudioCodec(int type, String rtpmap, String fmtp) { this.type = type; diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java index 91677a2..4321d7b 100644 --- a/voip/java/com/android/server/sip/SipSessionGroup.java +++ b/voip/java/com/android/server/sip/SipSessionGroup.java @@ -28,7 +28,6 @@ import android.net.sip.ISipSessionListener; import android.net.sip.SipErrorCode; import android.net.sip.SipProfile; import android.net.sip.SipSession; -import android.net.sip.SipSessionAdapter; import android.text.TextUtils; import android.util.Log; @@ -82,7 +81,6 @@ class SipSessionGroup implements SipListener { private static final boolean DEBUG = true; private static final boolean DEBUG_PING = DEBUG && false; private static final String ANONYMOUS = "anonymous"; - private static final String SERVER_ERROR_PREFIX = "Response: "; private static final int EXPIRY_TIME = 3600; // in seconds private static final int CANCEL_CALL_TIMER = 3; // in seconds @@ -960,6 +958,11 @@ class SipSessionGroup implements SipListener { int statusCode = response.getStatusCode(); switch (statusCode) { case Response.RINGING: + case Response.CALL_IS_BEING_FORWARDED: + case Response.QUEUED: + case Response.SESSION_PROGRESS: + // feedback any provisional responses (except TRYING) as + // ring back for better UX if (mState == SipSession.State.OUTGOING_CALL) { mState = SipSession.State.OUTGOING_CALL_RING_BACK; mProxy.onRingingBack(this); @@ -1099,8 +1102,8 @@ class SipSessionGroup implements SipListener { } private String createErrorMessage(Response response) { - return String.format(SERVER_ERROR_PREFIX + "%s (%d)", - response.getReasonPhrase(), response.getStatusCode()); + return String.format("%s (%d)", response.getReasonPhrase(), + response.getStatusCode()); } private void establishCall() { @@ -1204,8 +1207,6 @@ class SipSessionGroup implements SipListener { return SipErrorCode.INVALID_REMOTE_URI; } else if (exception instanceof IOException) { return SipErrorCode.SOCKET_ERROR; - } else if (message.startsWith(SERVER_ERROR_PREFIX)) { - return SipErrorCode.SERVER_ERROR; } else { return SipErrorCode.CLIENT_ERROR; } diff --git a/voip/jni/rtp/AmrCodec.cpp b/voip/jni/rtp/AmrCodec.cpp new file mode 100644 index 0000000..9a2227d --- /dev/null +++ b/voip/jni/rtp/AmrCodec.cpp @@ -0,0 +1,97 @@ +/* + * Copyrightm (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "AudioCodec.h" + +#include "gsmamr_dec.h" +#include "gsmamr_enc.h" + +namespace { + +class GsmEfrCodec : public AudioCodec +{ +public: + GsmEfrCodec() { + if (AMREncodeInit(&mEncoder, &mSidSync, false)) { + mEncoder = NULL; + } + if (GSMInitDecode(&mDecoder, (Word8 *)"RTP")) { + mDecoder = NULL; + } + } + + ~GsmEfrCodec() { + if (mEncoder) { + AMREncodeExit(&mEncoder, &mSidSync); + } + if (mDecoder) { + GSMDecodeFrameExit(&mDecoder); + } + } + + int set(int sampleRate, const char *fmtp) { + return (sampleRate == 8000 && mEncoder && mDecoder) ? 160 : -1; + } + + int encode(void *payload, int16_t *samples); + int decode(int16_t *samples, void *payload, int length); + +private: + void *mEncoder; + void *mSidSync; + void *mDecoder; +}; + +int GsmEfrCodec::encode(void *payload, int16_t *samples) +{ + unsigned char *bytes = (unsigned char *)payload; + Frame_Type_3GPP type; + + int length = AMREncode(mEncoder, mSidSync, MR122, + samples, bytes, &type, AMR_TX_WMF); + + if (type == AMR_122 && length == 32) { + bytes[0] = 0xC0 | (bytes[1] >> 4); + for (int i = 1; i < 31; ++i) { + bytes[i] = (bytes[i] << 4) | (bytes[i + 1] >> 4); + } + return 31; + } + return -1; +} + +int GsmEfrCodec::decode(int16_t *samples, void *payload, int length) +{ + unsigned char *bytes = (unsigned char *)payload; + if (length == 31 && (bytes[0] >> 4) == 0x0C) { + for (int i = 0; i < 30; ++i) { + bytes[i] = (bytes[i] << 4) | (bytes[i + 1] >> 4); + } + bytes[30] <<= 4; + + if (AMRDecode(mDecoder, AMR_122, bytes, samples, MIME_IETF) == 31) { + return 160; + } + } + return -1; +} + +} // namespace + +AudioCodec *newGsmEfrCodec() +{ + return new GsmEfrCodec; +} diff --git a/voip/jni/rtp/Android.mk b/voip/jni/rtp/Android.mk index a364355..5909c0d 100644 --- a/voip/jni/rtp/Android.mk +++ b/voip/jni/rtp/Android.mk @@ -26,16 +26,29 @@ LOCAL_SRC_FILES := \ util.cpp \ rtp_jni.cpp +LOCAL_SRC_FILES += \ + AmrCodec.cpp \ + G711Codec.cpp \ + GsmCodec.cpp + LOCAL_SHARED_LIBRARIES := \ libnativehelper \ libcutils \ libutils \ - libmedia + libmedia \ + libstagefright -LOCAL_STATIC_LIBRARIES := +LOCAL_STATIC_LIBRARIES := libgsm LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) + $(JNI_H_INCLUDE) \ + external/libgsm/inc \ + frameworks/base/media/libstagefright/codecs/amrnb/common/include \ + frameworks/base/media/libstagefright/codecs/amrnb/common/ \ + frameworks/base/media/libstagefright/codecs/amrnb/enc/include \ + frameworks/base/media/libstagefright/codecs/amrnb/enc/src \ + frameworks/base/media/libstagefright/codecs/amrnb/dec/include \ + frameworks/base/media/libstagefright/codecs/amrnb/dec/src LOCAL_CFLAGS += -fvisibility=hidden diff --git a/voip/jni/rtp/AudioCodec.cpp b/voip/jni/rtp/AudioCodec.cpp index 4d8d36c..afc193c 100644 --- a/voip/jni/rtp/AudioCodec.cpp +++ b/voip/jni/rtp/AudioCodec.cpp @@ -18,124 +18,10 @@ #include "AudioCodec.h" -namespace { - -int8_t gExponents[128] = { - 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, -}; - -//------------------------------------------------------------------------------ - -class UlawCodec : public AudioCodec -{ -public: - int set(int sampleRate, const char *fmtp) { - mSampleCount = sampleRate / 50; - return mSampleCount; - } - int encode(void *payload, int16_t *samples); - int decode(int16_t *samples, void *payload, int length); -private: - int mSampleCount; -}; - -int UlawCodec::encode(void *payload, int16_t *samples) -{ - int8_t *ulaws = (int8_t *)payload; - for (int i = 0; i < mSampleCount; ++i) { - int sample = samples[i]; - int sign = (sample >> 8) & 0x80; - if (sample < 0) { - sample = -sample; - } - sample += 132; - if (sample > 32767) { - sample = 32767; - } - int exponent = gExponents[sample >> 8]; - int mantissa = (sample >> (exponent + 3)) & 0x0F; - ulaws[i] = ~(sign | (exponent << 4) | mantissa); - } - return mSampleCount; -} - -int UlawCodec::decode(int16_t *samples, void *payload, int length) -{ - int8_t *ulaws = (int8_t *)payload; - for (int i = 0; i < length; ++i) { - int ulaw = ~ulaws[i]; - int exponent = (ulaw >> 4) & 0x07; - int mantissa = ulaw & 0x0F; - int sample = (((mantissa << 3) + 132) << exponent) - 132; - samples[i] = (ulaw < 0 ? -sample : sample); - } - return length; -} - -AudioCodec *newUlawCodec() -{ - return new UlawCodec; -} - -//------------------------------------------------------------------------------ - -class AlawCodec : public AudioCodec -{ -public: - int set(int sampleRate, const char *fmtp) { - mSampleCount = sampleRate / 50; - return mSampleCount; - } - int encode(void *payload, int16_t *samples); - int decode(int16_t *samples, void *payload, int length); -private: - int mSampleCount; -}; - -int AlawCodec::encode(void *payload, int16_t *samples) -{ - int8_t *alaws = (int8_t *)payload; - for (int i = 0; i < mSampleCount; ++i) { - int sample = samples[i]; - int sign = (sample >> 8) & 0x80; - if (sample < 0) { - sample = -sample; - } - if (sample > 32767) { - sample = 32767; - } - int exponent = gExponents[sample >> 8]; - int mantissa = (sample >> (exponent == 0 ? 4 : exponent + 3)) & 0x0F; - alaws[i] = (sign | (exponent << 4) | mantissa) ^ 0xD5; - } - return mSampleCount; -} - -int AlawCodec::decode(int16_t *samples, void *payload, int length) -{ - int8_t *alaws = (int8_t *)payload; - for (int i = 0; i < length; ++i) { - int alaw = alaws[i] ^ 0x55; - int exponent = (alaw >> 4) & 0x07; - int mantissa = alaw & 0x0F; - int sample = (exponent == 0 ? (mantissa << 4) + 8 : - ((mantissa << 3) + 132) << exponent); - samples[i] = (alaw < 0 ? sample : -sample); - } - return length; -} - -AudioCodec *newAlawCodec() -{ - return new AlawCodec; -} +extern AudioCodec *newAlawCodec(); +extern AudioCodec *newUlawCodec(); +extern AudioCodec *newGsmCodec(); +extern AudioCodec *newGsmEfrCodec(); struct AudioCodecType { const char *name; @@ -143,11 +29,11 @@ struct AudioCodecType { } gAudioCodecTypes[] = { {"PCMA", newAlawCodec}, {"PCMU", newUlawCodec}, + {"GSM", newGsmCodec}, + {"GSM-EFR", newGsmEfrCodec}, {NULL, NULL}, }; -} // namespace - AudioCodec *newAudioCodec(const char *codecName) { AudioCodecType *type = gAudioCodecTypes; diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp index 81d4dfc..f09edb6 100644 --- a/voip/jni/rtp/AudioGroup.cpp +++ b/voip/jni/rtp/AudioGroup.cpp @@ -86,8 +86,6 @@ public: void decode(int tick); private: - bool isNatAddress(struct sockaddr_storage *addr); - enum { NORMAL = 0, SEND_ONLY = 1, @@ -101,6 +99,7 @@ private: AudioCodec *mCodec; uint32_t mCodecMagic; uint32_t mDtmfMagic; + bool mFixRemote; int mTick; int mSampleRate; @@ -181,6 +180,20 @@ bool AudioStream::set(int mode, int socket, sockaddr_storage *remote, if (codec) { mRemote = *remote; mCodec = codec; + + // Here we should never get an private address, but some buggy proxy + // servers do give us one. To solve this, we replace the address when + // the first time we successfully decode an incoming packet. + mFixRemote = false; + if (remote->ss_family == AF_INET) { + unsigned char *address = + (unsigned char *)&((sockaddr_in *)remote)->sin_addr; + if (address[0] == 10 || + (address[0] == 172 && (address[1] >> 4) == 1) || + (address[0] == 192 && address[1] == 168)) { + mFixRemote = true; + } + } } LOGD("stream[%d] is configured as %s %dkHz %dms", mSocket, @@ -318,16 +331,6 @@ void AudioStream::encode(int tick, AudioStream *chain) sizeof(mRemote)); } -bool AudioStream::isNatAddress(struct sockaddr_storage *addr) { - if (addr->ss_family != AF_INET) return false; - struct sockaddr_in *s4 = (struct sockaddr_in *)addr; - unsigned char *d = (unsigned char *) &s4->sin_addr; - if ((d[0] == 10) - || ((d[0] == 172) && (d[1] & 0x10)) - || ((d[0] == 192) && (d[1] == 168))) return true; - return false; -} - void AudioStream::decode(int tick) { char c; @@ -375,21 +378,11 @@ void AudioStream::decode(int tick) MSG_TRUNC | MSG_DONTWAIT) >> 1; } else { __attribute__((aligned(4))) uint8_t buffer[2048]; - struct sockaddr_storage src_addr; - socklen_t addrlen; + sockaddr_storage remote; + socklen_t len = sizeof(remote); + length = recvfrom(mSocket, buffer, sizeof(buffer), - MSG_TRUNC|MSG_DONTWAIT, (sockaddr*)&src_addr, &addrlen); - - // The following if clause is for fixing the target address if - // proxy server did not replace the NAT address with its media - // port in SDP. Although it is proxy server's responsibility for - // replacing the connection address with correct one, we will change - // the target address as we detect the difference for now until we - // know the best way to get rid of this issue. - if ((memcmp((void*)&src_addr, (void*)&mRemote, addrlen) != 0) && - isNatAddress(&mRemote)) { - memcpy((void*)&mRemote, (void*)&src_addr, addrlen); - } + MSG_TRUNC | MSG_DONTWAIT, (sockaddr *)&remote, &len); // Do we need to check SSRC, sequence, and timestamp? They are not // reliable but at least they can be used to identify duplicates? @@ -409,8 +402,12 @@ void AudioStream::decode(int tick) if (length >= 0) { length = mCodec->decode(samples, &buffer[offset], length); } + if (length > 0 && mFixRemote) { + mRemote = remote; + mFixRemote = false; + } } - if (length != mSampleCount) { + if (length <= 0) { LOGD("stream[%d] decoder error", mSocket); return; } @@ -461,18 +458,15 @@ private: EC_ENABLED = 3, LAST_MODE = 3, }; - int mMode; + AudioStream *mChain; int mEventQueue; volatile int mDtmfEvent; + int mMode; + int mSampleRate; int mSampleCount; int mDeviceSocket; - AudioTrack mTrack; - AudioRecord mRecord; - - bool networkLoop(); - bool deviceLoop(); class NetworkThread : public Thread { @@ -490,10 +484,7 @@ private: private: AudioGroup *mGroup; - bool threadLoop() - { - return mGroup->networkLoop(); - } + bool threadLoop(); }; sp<NetworkThread> mNetworkThread; @@ -504,9 +495,6 @@ private: bool start() { - char c; - while (recv(mGroup->mDeviceSocket, &c, 1, MSG_DONTWAIT) == 1); - if (run("Device", ANDROID_PRIORITY_AUDIO) != NO_ERROR) { LOGE("cannot start device thread"); return false; @@ -516,10 +504,7 @@ private: private: AudioGroup *mGroup; - bool threadLoop() - { - return mGroup->deviceLoop(); - } + bool threadLoop(); }; sp<DeviceThread> mDeviceThread; }; @@ -539,8 +524,6 @@ AudioGroup::~AudioGroup() { mNetworkThread->requestExitAndWait(); mDeviceThread->requestExitAndWait(); - mTrack.stop(); - mRecord.stop(); close(mEventQueue); close(mDeviceSocket); while (mChain) { @@ -559,40 +542,9 @@ bool AudioGroup::set(int sampleRate, int sampleCount) return false; } + mSampleRate = sampleRate; mSampleCount = sampleCount; - // Find out the frame count for AudioTrack and AudioRecord. - int output = 0; - int input = 0; - if (AudioTrack::getMinFrameCount(&output, AudioSystem::VOICE_CALL, - sampleRate) != NO_ERROR || output <= 0 || - AudioRecord::getMinFrameCount(&input, sampleRate, - AudioSystem::PCM_16_BIT, 1) != NO_ERROR || input <= 0) { - LOGE("cannot compute frame count"); - return false; - } - LOGD("reported frame count: output %d, input %d", output, input); - - if (output < sampleCount * 2) { - output = sampleCount * 2; - } - if (input < sampleCount * 2) { - input = sampleCount * 2; - } - LOGD("adjusted frame count: output %d, input %d", output, input); - - // Initialize AudioTrack and AudioRecord. - if (mTrack.set(AudioSystem::VOICE_CALL, sampleRate, AudioSystem::PCM_16_BIT, - AudioSystem::CHANNEL_OUT_MONO, output) != NO_ERROR || - mRecord.set(AUDIO_SOURCE_MIC, sampleRate, AudioSystem::PCM_16_BIT, - AudioSystem::CHANNEL_IN_MONO, input) != NO_ERROR) { - LOGE("cannot initialize audio device"); - return false; - } - LOGD("latency: output %d, input %d", mTrack.latency(), mRecord.latency()); - - // TODO: initialize echo canceler here. - // Create device socket. int pair[2]; if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair)) { @@ -610,13 +562,11 @@ bool AudioGroup::set(int sampleRate, int sampleCount) return false; } - // Give device socket a reasonable timeout and buffer size. + // Give device socket a reasonable timeout. timeval tv; tv.tv_sec = 0; tv.tv_usec = 1000 * sampleCount / sampleRate * 500; - if (setsockopt(pair[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) || - setsockopt(pair[0], SOL_SOCKET, SO_RCVBUF, &output, sizeof(output)) || - setsockopt(pair[1], SOL_SOCKET, SO_SNDBUF, &output, sizeof(output))) { + if (setsockopt(pair[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) { LOGE("setsockopt: %s", strerror(errno)); return false; } @@ -644,29 +594,10 @@ bool AudioGroup::setMode(int mode) return true; } + mDeviceThread->requestExitAndWait(); LOGD("group[%d] switches from mode %d to %d", mDeviceSocket, mMode, mode); mMode = mode; - - mDeviceThread->requestExitAndWait(); - if (mode == ON_HOLD) { - mTrack.stop(); - mRecord.stop(); - return true; - } - - mTrack.start(); - if (mode == MUTED) { - mRecord.stop(); - } else { - mRecord.start(); - } - - if (!mDeviceThread->start()) { - mTrack.stop(); - mRecord.stop(); - return false; - } - return true; + return (mode == ON_HOLD) || mDeviceThread->start(); } bool AudioGroup::sendDtmf(int event) @@ -741,15 +672,16 @@ bool AudioGroup::remove(int socket) return true; } -bool AudioGroup::networkLoop() +bool AudioGroup::NetworkThread::threadLoop() { + AudioStream *chain = mGroup->mChain; int tick = elapsedRealtime(); int deadline = tick + 10; int count = 0; - for (AudioStream *stream = mChain; stream; stream = stream->mNext) { + for (AudioStream *stream = chain; stream; stream = stream->mNext) { if (!stream->mTick || tick - stream->mTick >= 0) { - stream->encode(tick, mChain); + stream->encode(tick, chain); } if (deadline - stream->mTick > 0) { deadline = stream->mTick; @@ -757,12 +689,12 @@ bool AudioGroup::networkLoop() ++count; } - if (mDtmfEvent != -1) { - int event = mDtmfEvent; - for (AudioStream *stream = mChain; stream; stream = stream->mNext) { + int event = mGroup->mDtmfEvent; + if (event != -1) { + for (AudioStream *stream = chain; stream; stream = stream->mNext) { stream->sendDtmf(event); } - mDtmfEvent = -1; + mGroup->mDtmfEvent = -1; } deadline -= tick; @@ -771,7 +703,7 @@ bool AudioGroup::networkLoop() } epoll_event events[count]; - count = epoll_wait(mEventQueue, events, count, deadline); + count = epoll_wait(mGroup->mEventQueue, events, count, deadline); if (count == -1) { LOGE("epoll_wait: %s", strerror(errno)); return false; @@ -783,70 +715,125 @@ bool AudioGroup::networkLoop() return true; } -bool AudioGroup::deviceLoop() +bool AudioGroup::DeviceThread::threadLoop() { - int16_t output[mSampleCount]; + int mode = mGroup->mMode; + int sampleRate = mGroup->mSampleRate; + int sampleCount = mGroup->mSampleCount; + int deviceSocket = mGroup->mDeviceSocket; + + // Find out the frame count for AudioTrack and AudioRecord. + int output = 0; + int input = 0; + if (AudioTrack::getMinFrameCount(&output, AudioSystem::VOICE_CALL, + sampleRate) != NO_ERROR || output <= 0 || + AudioRecord::getMinFrameCount(&input, sampleRate, + AudioSystem::PCM_16_BIT, 1) != NO_ERROR || input <= 0) { + LOGE("cannot compute frame count"); + return false; + } + LOGD("reported frame count: output %d, input %d", output, input); + + if (output < sampleCount * 2) { + output = sampleCount * 2; + } + if (input < sampleCount * 2) { + input = sampleCount * 2; + } + LOGD("adjusted frame count: output %d, input %d", output, input); - if (recv(mDeviceSocket, output, sizeof(output), 0) <= 0) { - memset(output, 0, sizeof(output)); + // Initialize AudioTrack and AudioRecord. + AudioTrack track; + AudioRecord record; + if (track.set(AudioSystem::VOICE_CALL, sampleRate, AudioSystem::PCM_16_BIT, + AudioSystem::CHANNEL_OUT_MONO, output) != NO_ERROR || + record.set(AUDIO_SOURCE_MIC, sampleRate, AudioSystem::PCM_16_BIT, + AudioSystem::CHANNEL_IN_MONO, input) != NO_ERROR) { + LOGE("cannot initialize audio device"); + return false; } + LOGD("latency: output %d, input %d", track.latency(), record.latency()); - int16_t input[mSampleCount]; - int toWrite = mSampleCount; - int toRead = (mMode == MUTED) ? 0 : mSampleCount; - int chances = 100; + // TODO: initialize echo canceler here. - while (--chances > 0 && (toWrite > 0 || toRead > 0)) { - if (toWrite > 0) { - AudioTrack::Buffer buffer; - buffer.frameCount = toWrite; + // Give device socket a reasonable buffer size. + setsockopt(deviceSocket, SOL_SOCKET, SO_RCVBUF, &output, sizeof(output)); + setsockopt(deviceSocket, SOL_SOCKET, SO_SNDBUF, &output, sizeof(output)); - status_t status = mTrack.obtainBuffer(&buffer, 1); - if (status == NO_ERROR) { - memcpy(buffer.i8, &output[mSampleCount - toWrite], buffer.size); - toWrite -= buffer.frameCount; - mTrack.releaseBuffer(&buffer); - } else if (status != TIMED_OUT && status != WOULD_BLOCK) { - LOGE("cannot write to AudioTrack"); - return false; - } + // Drain device socket. + char c; + while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1); + + // Start your engine! + track.start(); + if (mode != MUTED) { + record.start(); + } + + while (!exitPending()) { + int16_t output[sampleCount]; + if (recv(deviceSocket, output, sizeof(output), 0) <= 0) { + memset(output, 0, sizeof(output)); } - if (toRead > 0) { - AudioRecord::Buffer buffer; - buffer.frameCount = mRecord.frameCount(); - - status_t status = mRecord.obtainBuffer(&buffer, 1); - if (status == NO_ERROR) { - int count = ((int)buffer.frameCount < toRead) ? - buffer.frameCount : toRead; - memcpy(&input[mSampleCount - toRead], buffer.i8, count * 2); - toRead -= count; - if (buffer.frameCount < mRecord.frameCount()) { - buffer.frameCount = count; + int16_t input[sampleCount]; + int toWrite = sampleCount; + int toRead = (mode == MUTED) ? 0 : sampleCount; + int chances = 100; + + while (--chances > 0 && (toWrite > 0 || toRead > 0)) { + if (toWrite > 0) { + AudioTrack::Buffer buffer; + buffer.frameCount = toWrite; + + status_t status = track.obtainBuffer(&buffer, 1); + if (status == NO_ERROR) { + int offset = sampleCount - toWrite; + memcpy(buffer.i8, &output[offset], buffer.size); + toWrite -= buffer.frameCount; + track.releaseBuffer(&buffer); + } else if (status != TIMED_OUT && status != WOULD_BLOCK) { + LOGE("cannot write to AudioTrack"); + break; + } + } + + if (toRead > 0) { + AudioRecord::Buffer buffer; + buffer.frameCount = record.frameCount(); + + status_t status = record.obtainBuffer(&buffer, 1); + if (status == NO_ERROR) { + int count = ((int)buffer.frameCount < toRead) ? + buffer.frameCount : toRead; + memcpy(&input[sampleCount - toRead], buffer.i8, count * 2); + toRead -= count; + if (buffer.frameCount < record.frameCount()) { + buffer.frameCount = count; + } + record.releaseBuffer(&buffer); + } else if (status != TIMED_OUT && status != WOULD_BLOCK) { + LOGE("cannot read from AudioRecord"); + break; } - mRecord.releaseBuffer(&buffer); - } else if (status != TIMED_OUT && status != WOULD_BLOCK) { - LOGE("cannot read from AudioRecord"); - return false; } } - } - if (!chances) { - LOGE("device loop timeout"); - return false; - } + if (chances <= 0) { + LOGE("device loop timeout"); + break; + } - if (mMode != MUTED) { - if (mMode == NORMAL) { - send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT); - } else { - // TODO: Echo canceller runs here. - send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT); + if (mode != MUTED) { + if (mode == NORMAL) { + send(deviceSocket, input, sizeof(input), MSG_DONTWAIT); + } else { + // TODO: Echo canceller runs here. + send(deviceSocket, input, sizeof(input), MSG_DONTWAIT); + } } } - return true; + return false; } //------------------------------------------------------------------------------ diff --git a/voip/jni/rtp/G711Codec.cpp b/voip/jni/rtp/G711Codec.cpp new file mode 100644 index 0000000..091afa9 --- /dev/null +++ b/voip/jni/rtp/G711Codec.cpp @@ -0,0 +1,138 @@ +/* + * Copyrightm (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "AudioCodec.h" + +namespace { + +int8_t gExponents[128] = { + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +}; + +//------------------------------------------------------------------------------ + +class UlawCodec : public AudioCodec +{ +public: + int set(int sampleRate, const char *fmtp) { + mSampleCount = sampleRate / 50; + return mSampleCount; + } + int encode(void *payload, int16_t *samples); + int decode(int16_t *samples, void *payload, int length); +private: + int mSampleCount; +}; + +int UlawCodec::encode(void *payload, int16_t *samples) +{ + int8_t *ulaws = (int8_t *)payload; + for (int i = 0; i < mSampleCount; ++i) { + int sample = samples[i]; + int sign = (sample >> 8) & 0x80; + if (sample < 0) { + sample = -sample; + } + sample += 132; + if (sample > 32767) { + sample = 32767; + } + int exponent = gExponents[sample >> 8]; + int mantissa = (sample >> (exponent + 3)) & 0x0F; + ulaws[i] = ~(sign | (exponent << 4) | mantissa); + } + return mSampleCount; +} + +int UlawCodec::decode(int16_t *samples, void *payload, int length) +{ + int8_t *ulaws = (int8_t *)payload; + for (int i = 0; i < length; ++i) { + int ulaw = ~ulaws[i]; + int exponent = (ulaw >> 4) & 0x07; + int mantissa = ulaw & 0x0F; + int sample = (((mantissa << 3) + 132) << exponent) - 132; + samples[i] = (ulaw < 0 ? -sample : sample); + } + return length; +} + +//------------------------------------------------------------------------------ + +class AlawCodec : public AudioCodec +{ +public: + int set(int sampleRate, const char *fmtp) { + mSampleCount = sampleRate / 50; + return mSampleCount; + } + int encode(void *payload, int16_t *samples); + int decode(int16_t *samples, void *payload, int length); +private: + int mSampleCount; +}; + +int AlawCodec::encode(void *payload, int16_t *samples) +{ + int8_t *alaws = (int8_t *)payload; + for (int i = 0; i < mSampleCount; ++i) { + int sample = samples[i]; + int sign = (sample >> 8) & 0x80; + if (sample < 0) { + sample = -sample; + } + if (sample > 32767) { + sample = 32767; + } + int exponent = gExponents[sample >> 8]; + int mantissa = (sample >> (exponent == 0 ? 4 : exponent + 3)) & 0x0F; + alaws[i] = (sign | (exponent << 4) | mantissa) ^ 0xD5; + } + return mSampleCount; +} + +int AlawCodec::decode(int16_t *samples, void *payload, int length) +{ + int8_t *alaws = (int8_t *)payload; + for (int i = 0; i < length; ++i) { + int alaw = alaws[i] ^ 0x55; + int exponent = (alaw >> 4) & 0x07; + int mantissa = alaw & 0x0F; + int sample = (exponent == 0 ? (mantissa << 4) + 8 : + ((mantissa << 3) + 132) << exponent); + samples[i] = (alaw < 0 ? sample : -sample); + } + return length; +} + +} // namespace + +AudioCodec *newUlawCodec() +{ + return new UlawCodec; +} + +AudioCodec *newAlawCodec() +{ + return new AlawCodec; +} diff --git a/voip/jni/rtp/GsmCodec.cpp b/voip/jni/rtp/GsmCodec.cpp new file mode 100644 index 0000000..8d2286e --- /dev/null +++ b/voip/jni/rtp/GsmCodec.cpp @@ -0,0 +1,74 @@ +/* + * Copyrightm (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "AudioCodec.h" + +extern "C" { +#include "gsm.h" +} + +namespace { + +class GsmCodec : public AudioCodec +{ +public: + GsmCodec() { + mEncode = gsm_create(); + mDecode = gsm_create(); + } + + ~GsmCodec() { + if (mEncode) { + gsm_destroy(mEncode); + } + if (mDecode) { + gsm_destroy(mDecode); + } + } + + int set(int sampleRate, const char *fmtp) { + return (sampleRate == 8000 && mEncode && mDecode) ? 160 : -1; + } + + int encode(void *payload, int16_t *samples); + int decode(int16_t *samples, void *payload, int length); + +private: + gsm mEncode; + gsm mDecode; +}; + +int GsmCodec::encode(void *payload, int16_t *samples) +{ + gsm_encode(mEncode, samples, (unsigned char *)payload); + return 33; +} + +int GsmCodec::decode(int16_t *samples, void *payload, int length) +{ + if (length == 33 && + gsm_decode(mDecode, (unsigned char *)payload, samples) == 0) { + return 160; + } + return -1; +} + +} // namespace + +AudioCodec *newGsmCodec() +{ + return new GsmCodec; +} |