diff options
667 files changed, 42498 insertions, 12363 deletions
diff --git a/Android.mk b/Android.mk index fcb66ae2d51c..08ee65effab1 100644 --- a/Android.mk +++ b/Android.mk @@ -115,6 +115,7 @@ LOCAL_SRC_FILES += \ core/java/android/content/pm/IPackageMoveObserver.aidl \ core/java/android/content/pm/IPackageStatsObserver.aidl \ core/java/android/database/IContentObserver.aidl \ + core/java/android/hardware/IUsbManager.aidl \ core/java/android/net/IConnectivityManager.aidl \ core/java/android/net/INetworkManagementEventObserver.aidl \ core/java/android/net/IThrottleManager.aidl \ diff --git a/api/11.xml b/api/11.xml index ed4a2e027e4c..76669e35ae51 100644 --- a/api/11.xml +++ b/api/11.xml @@ -90722,6 +90722,8 @@ > <parameter name="surfaceTexture" type="android.graphics.SurfaceTexture"> </parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> </method> <method name="setZoomChangeListener" return="void" @@ -207456,22 +207458,22 @@ </method> <method name="getDeviceId" return="int" - abstract="false" + abstract="true" native="false" synchronized="false" static="false" - final="true" + final="false" deprecated="not deprecated" visibility="public" > </method> <method name="getSource" return="int" - abstract="false" + abstract="true" native="false" synchronized="false" static="false" - final="true" + final="false" deprecated="not deprecated" visibility="public" > diff --git a/api/current.xml b/api/current.xml index ed4a2e027e4c..4e454719d714 100644 --- a/api/current.xml +++ b/api/current.xml @@ -111,6 +111,17 @@ visibility="public" > </field> +<field name="ACCESS_USB" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.permission.ACCESS_USB"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ACCESS_WIFI_STATE" type="java.lang.String" transient="false" @@ -22680,6 +22691,19 @@ <parameter name="id" type="int"> </parameter> </method> +<method name="dispatchGenericMotionEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="ev" type="android.view.MotionEvent"> +</parameter> +</method> <method name="dispatchKeyEvent" return="boolean" abstract="false" @@ -23510,6 +23534,19 @@ visibility="public" > </method> +<method name="onGenericMotionEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.MotionEvent"> +</parameter> +</method> <method name="onKeyDown" return="boolean" abstract="false" @@ -25027,6 +25064,17 @@ <parameter name="packageName" type="java.lang.String"> </parameter> </method> +<field name="MOVE_TASK_NO_USER_ACTION" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="MOVE_TASK_WITH_HOME" type="int" transient="false" @@ -25458,6 +25506,16 @@ visibility="public" > </field> +<field name="persistentId" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="ActivityManager.RunningAppProcessInfo" extends="java.lang.Object" @@ -27506,6 +27564,19 @@ visibility="public" > </method> +<method name="dispatchGenericMotionEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="ev" type="android.view.MotionEvent"> +</parameter> +</method> <method name="dispatchKeyEvent" return="boolean" abstract="false" @@ -27861,6 +27932,19 @@ visibility="public" > </method> +<method name="onGenericMotionEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.MotionEvent"> +</parameter> +</method> <method name="onKeyDown" return="boolean" abstract="false" @@ -28744,6 +28828,31 @@ deprecated="not deprecated" visibility="public" > +<method name="completedDownload" + return="long" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="title" type="java.lang.String"> +</parameter> +<parameter name="description" type="java.lang.String"> +</parameter> +<parameter name="isMediaScannerScannable" type="boolean"> +</parameter> +<parameter name="mimeType" type="java.lang.String"> +</parameter> +<parameter name="path" type="java.lang.String"> +</parameter> +<parameter name="length" type="long"> +</parameter> +<parameter name="showNotification" type="boolean"> +</parameter> +</method> <method name="enqueue" return="long" abstract="false" @@ -29147,6 +29256,17 @@ visibility="public" > </field> +<field name="INTENT_EXTRAS_SORT_BY_SIZE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.app.DownloadManager.extra_sortBySize"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="PAUSED_QUEUED_FOR_WIFI" type="int" transient="false" @@ -29538,6 +29658,17 @@ visibility="public" > </field> +<field name="VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION" + type="int" + transient="false" + volatile="false" + value="3" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="ExpandableListActivity" extends="android.app.Activity" @@ -30582,6 +30713,19 @@ <parameter name="visibleCrumbs" type="int"> </parameter> </method> +<method name="setOnBreadCrumbClickListener" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="listener" type="android.app.FragmentBreadCrumbs.OnBreadCrumbClickListener"> +</parameter> +</method> <method name="setParentTitle" return="void" abstract="false" @@ -30615,6 +30759,29 @@ </parameter> </method> </class> +<interface name="FragmentBreadCrumbs.OnBreadCrumbClickListener" + abstract="true" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="onBreadCrumbClick" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="backStack" type="android.app.FragmentManager.BackStackEntry"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +</interface> <class name="FragmentManager" extends="java.lang.Object" abstract="true" @@ -49100,6 +49267,17 @@ visibility="public" > </field> +<field name="USB_SERVICE" + type="java.lang.String" + transient="false" + volatile="false" + value=""usb"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="VIBRATOR_SERVICE" type="java.lang.String" transient="false" @@ -76250,6 +76428,17 @@ <parameter name="offsetXY" type="int[]"> </parameter> </method> +<method name="getByteCount" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getConfig" return="android.graphics.Bitmap.Config" abstract="false" @@ -76272,6 +76461,17 @@ visibility="public" > </method> +<method name="getGenerationId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getHeight" return="int" abstract="false" @@ -76489,6 +76689,19 @@ visibility="public" > </method> +<method name="sameAs" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="other" type="android.graphics.Bitmap"> +</parameter> +</method> <method name="setDensity" return="void" abstract="false" @@ -76502,6 +76715,19 @@ <parameter name="density" type="int"> </parameter> </method> +<method name="setHasAlpha" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="hasAlpha" type="boolean"> +</parameter> +</method> <method name="setPixel" return="void" abstract="false" @@ -90722,6 +90948,8 @@ > <parameter name="surfaceTexture" type="android.graphics.SurfaceTexture"> </parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> </method> <method name="setZoomChangeListener" return="void" @@ -93948,6 +94176,1510 @@ > </field> </class> +<class name="UsbAccessory" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + 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="getManufacturer" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getModel" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getType" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getVersion" + return="java.lang.String" + 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="parcel" type="android.os.Parcel"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="UsbConstants" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<constructor name="UsbConstants" + type="android.hardware.UsbConstants" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<field name="USB_CLASS_APP_SPEC" + type="int" + transient="false" + volatile="false" + value="254" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_AUDIO" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_CDC_DATA" + type="int" + transient="false" + volatile="false" + value="10" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_COMM" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_CONTENT_SEC" + type="int" + transient="false" + volatile="false" + value="13" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_CSCID" + type="int" + transient="false" + volatile="false" + value="11" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_HID" + type="int" + transient="false" + volatile="false" + value="3" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_HUB" + type="int" + transient="false" + volatile="false" + value="9" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_MASS_STORAGE" + type="int" + transient="false" + volatile="false" + value="8" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_MISC" + type="int" + transient="false" + volatile="false" + value="239" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_PER_INTERFACE" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_PHYSICA" + type="int" + transient="false" + volatile="false" + value="5" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_PRINTER" + type="int" + transient="false" + volatile="false" + value="7" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_STILL_IMAGE" + type="int" + transient="false" + volatile="false" + value="6" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_VENDOR_SPEC" + type="int" + transient="false" + volatile="false" + value="255" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_VIDEO" + type="int" + transient="false" + volatile="false" + value="14" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CLASS_WIRELESS_CONTROLLER" + type="int" + transient="false" + volatile="false" + value="224" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_DIR_IN" + type="int" + transient="false" + volatile="false" + value="128" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_DIR_OUT" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_ENDPOINT_DIR_MASK" + type="int" + transient="false" + volatile="false" + value="128" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_ENDPOINT_NUMBER_MASK" + type="int" + transient="false" + volatile="false" + value="15" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_ENDPOINT_XFERTYPE_MASK" + type="int" + transient="false" + volatile="false" + value="3" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_ENDPOINT_XFER_BULK" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_ENDPOINT_XFER_CONTROL" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_ENDPOINT_XFER_INT" + type="int" + transient="false" + volatile="false" + value="3" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_ENDPOINT_XFER_ISOC" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_INTERFACE_SUBCLASS_BOOT" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_SUBCLASS_VENDOR_SPEC" + type="int" + transient="false" + volatile="false" + value="255" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_TYPE_CLASS" + type="int" + transient="false" + volatile="false" + value="32" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_TYPE_MASK" + type="int" + transient="false" + volatile="false" + value="96" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_TYPE_RESERVED" + type="int" + transient="false" + volatile="false" + value="96" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_TYPE_STANDARD" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_TYPE_VENDOR" + type="int" + transient="false" + volatile="false" + value="64" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="UsbDevice" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.os.Parcelable"> +</implements> +<method name="bulkTransfer" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="endpoint" type="android.hardware.UsbEndpoint"> +</parameter> +<parameter name="buffer" type="byte[]"> +</parameter> +<parameter name="length" type="int"> +</parameter> +<parameter name="timeout" type="int"> +</parameter> +</method> +<method name="claimInterface" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="intf" type="android.hardware.UsbInterface"> +</parameter> +<parameter name="force" type="boolean"> +</parameter> +</method> +<method name="close" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="controlTransfer" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="requestType" type="int"> +</parameter> +<parameter name="request" type="int"> +</parameter> +<parameter name="value" type="int"> +</parameter> +<parameter name="index" type="int"> +</parameter> +<parameter name="buffer" type="byte[]"> +</parameter> +<parameter name="length" type="int"> +</parameter> +<parameter name="timeout" type="int"> +</parameter> +</method> +<method name="describeContents" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDeviceClass" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDeviceId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDeviceId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="name" type="java.lang.String"> +</parameter> +</method> +<method name="getDeviceName" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDeviceName" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="id" type="int"> +</parameter> +</method> +<method name="getDeviceProtocol" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDeviceSubclass" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getFileDescriptor" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getInterface" + return="android.hardware.UsbInterface" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="index" type="int"> +</parameter> +</method> +<method name="getInterfaceCount" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getProductId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getSerial" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getVendorId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="releaseInterface" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="intf" type="android.hardware.UsbInterface"> +</parameter> +</method> +<method name="requestWait" + return="android.hardware.UsbRequest" + 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="parcel" type="android.os.Parcel"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="UsbEndpoint" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + 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="getAddress" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getAttributes" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDevice" + return="android.hardware.UsbDevice" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDirection" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getEndpointNumber" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getInterface" + return="android.hardware.UsbInterface" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getInterval" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getMaxPacketSize" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getType" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="writeToParcel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="parcel" type="android.os.Parcel"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="UsbInterface" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + 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="getDevice" + return="android.hardware.UsbDevice" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getEndpoint" + return="android.hardware.UsbEndpoint" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="index" type="int"> +</parameter> +</method> +<method name="getEndpointCount" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getInterfaceClass" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getInterfaceProtocol" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getInterfaceSubclass" + 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="parcel" type="android.os.Parcel"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="UsbManager" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="getAccessoryList" + return="android.hardware.UsbAccessory[]" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDeviceList" + return="java.util.HashMap<java.lang.String, android.hardware.UsbDevice>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="isFunctionEnabled" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="function" type="java.lang.String"> +</parameter> +</method> +<method name="isFunctionSupported" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="function" type="java.lang.String"> +</parameter> +</method> +<method name="openAccessory" + return="android.os.ParcelFileDescriptor" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="accessory" type="android.hardware.UsbAccessory"> +</parameter> +</method> +<method name="openDevice" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="device" type="android.hardware.UsbDevice"> +</parameter> +</method> +<field name="ACTION_USB_ACCESSORY_ATTACHED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.hardware.action.USB_ACCESSORY_ATTACHED"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ACTION_USB_ACCESSORY_DETACHED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.hardware.action.USB_ACCESSORY_DETACHED"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ACTION_USB_DEVICE_ATTACHED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.hardware.action.USB_DEVICE_ATTACHED"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ACTION_USB_DEVICE_DETACHED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.hardware.action.USB_DEVICE_DETACHED"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ACTION_USB_STATE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.hardware.action.USB_STATE"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_ACCESSORY" + type="java.lang.String" + transient="false" + volatile="false" + value=""accessory"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_ACCESSORY_MANUFACTURER" + type="java.lang.String" + transient="false" + volatile="false" + value=""accessory-manufacturer"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_ACCESSORY_PRODUCT" + type="java.lang.String" + transient="false" + volatile="false" + value=""accessory-product"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_ACCESSORY_TYPE" + type="java.lang.String" + transient="false" + volatile="false" + value=""accessory-type"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_ACCESSORY_VERSION" + type="java.lang.String" + transient="false" + volatile="false" + value=""accessory-version"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_DEVICE" + type="java.lang.String" + transient="false" + volatile="false" + value=""device"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_DEVICE_CLASS" + type="java.lang.String" + transient="false" + volatile="false" + value=""device_class"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_DEVICE_NAME" + type="java.lang.String" + transient="false" + volatile="false" + value=""device_name"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_DEVICE_PROTOCOL" + type="java.lang.String" + transient="false" + volatile="false" + value=""device_protocol"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_DEVICE_SUBCLASS" + type="java.lang.String" + transient="false" + volatile="false" + value=""device_subclass"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_PRODUCT_ID" + type="java.lang.String" + transient="false" + volatile="false" + value=""product_id"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_VENDOR_ID" + type="java.lang.String" + transient="false" + volatile="false" + value=""vendor_id"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CONFIGURATION" + type="java.lang.String" + transient="false" + volatile="false" + value=""configuration"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_CONNECTED" + type="java.lang.String" + transient="false" + volatile="false" + value=""connected"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_FUNCTION_ACCESSORY" + type="java.lang.String" + transient="false" + volatile="false" + value=""accessory"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_FUNCTION_ADB" + type="java.lang.String" + transient="false" + volatile="false" + value=""adb"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_FUNCTION_DISABLED" + type="java.lang.String" + transient="false" + volatile="false" + value=""disabled"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_FUNCTION_ENABLED" + type="java.lang.String" + transient="false" + volatile="false" + value=""enabled"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_FUNCTION_MASS_STORAGE" + type="java.lang.String" + transient="false" + volatile="false" + value=""mass_storage"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_FUNCTION_MTP" + type="java.lang.String" + transient="false" + volatile="false" + value=""mtp"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="USB_FUNCTION_RNDIS" + type="java.lang.String" + transient="false" + volatile="false" + value=""rndis"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="UsbRequest" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="UsbRequest" + type="android.hardware.UsbRequest" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="cancel" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="close" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getClientData" + return="java.lang.Object" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getEndpoint" + return="android.hardware.UsbEndpoint" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="initialize" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="endpoint" type="android.hardware.UsbEndpoint"> +</parameter> +</method> +<method name="queue" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="buffer" type="java.nio.ByteBuffer"> +</parameter> +<parameter name="length" type="int"> +</parameter> +</method> +<method name="setClientData" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="data" type="java.lang.Object"> +</parameter> +</method> +</class> </package> <package name="android.inputmethodservice" > @@ -110687,6 +112419,1517 @@ </method> </interface> </package> +<package name="android.mtp" +> +<class name="MtpClient" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="MtpClient" + type="android.mtp.MtpClient" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +</constructor> +<method name="addListener" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="listener" type="android.mtp.MtpClient.Listener"> +</parameter> +</method> +<method name="close" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="deleteObject" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceName" type="java.lang.String"> +</parameter> +<parameter name="objectHandle" type="int"> +</parameter> +</method> +<method name="getDevice" + return="android.mtp.MtpDevice" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceName" type="java.lang.String"> +</parameter> +</method> +<method name="getDevice" + return="android.mtp.MtpDevice" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="id" type="int"> +</parameter> +</method> +<method name="getDeviceList" + return="java.util.List<android.mtp.MtpDevice>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getObject" + return="byte[]" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceName" type="java.lang.String"> +</parameter> +<parameter name="objectHandle" type="int"> +</parameter> +<parameter name="objectSize" type="int"> +</parameter> +</method> +<method name="getObjectInfo" + return="android.mtp.MtpObjectInfo" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceName" type="java.lang.String"> +</parameter> +<parameter name="objectHandle" type="int"> +</parameter> +</method> +<method name="getObjectList" + return="java.util.List<android.mtp.MtpObjectInfo>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceName" type="java.lang.String"> +</parameter> +<parameter name="storageId" type="int"> +</parameter> +<parameter name="objectHandle" type="int"> +</parameter> +</method> +<method name="getStorageList" + return="java.util.List<android.mtp.MtpStorageInfo>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceName" type="java.lang.String"> +</parameter> +</method> +<method name="getThumbnail" + return="byte[]" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceName" type="java.lang.String"> +</parameter> +<parameter name="objectHandle" type="int"> +</parameter> +</method> +<method name="importFile" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="deviceName" type="java.lang.String"> +</parameter> +<parameter name="objectHandle" type="int"> +</parameter> +<parameter name="destPath" type="java.lang.String"> +</parameter> +</method> +<method name="isCamera" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="device" type="android.hardware.UsbDevice"> +</parameter> +</method> +<method name="removeListener" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="listener" type="android.mtp.MtpClient.Listener"> +</parameter> +</method> +</class> +<interface name="MtpClient.Listener" + abstract="true" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="deviceAdded" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="device" type="android.mtp.MtpDevice"> +</parameter> +</method> +<method name="deviceRemoved" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="device" type="android.mtp.MtpDevice"> +</parameter> +</method> +</interface> +<class name="MtpConstants" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<constructor name="MtpConstants" + type="android.mtp.MtpConstants" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="isAbstractObject" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="format" type="int"> +</parameter> +</method> +<field name="ASSOCIATION_TYPE_GENERIC_FOLDER" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_3GP_CONTAINER" + type="int" + transient="false" + volatile="false" + value="47492" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_AAC" + type="int" + transient="false" + volatile="false" + value="47363" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_ABSTRACT_AUDIO_ALBUM" + type="int" + transient="false" + volatile="false" + value="47619" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_ABSTRACT_AUDIO_PLAYLIST" + type="int" + transient="false" + volatile="false" + value="47625" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_ABSTRACT_AV_PLAYLIST" + type="int" + transient="false" + volatile="false" + value="47621" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_ABSTRACT_DOCUMENT" + type="int" + transient="false" + volatile="false" + value="47745" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_ABSTRACT_IMAGE_ALBUM" + type="int" + transient="false" + volatile="false" + value="47618" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_ABSTRACT_MEDIACAST" + type="int" + transient="false" + volatile="false" + value="47627" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_ABSTRACT_MULTIMEDIA_ALBUM" + type="int" + transient="false" + volatile="false" + value="47617" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_ABSTRACT_VIDEO_ALBUM" + type="int" + transient="false" + volatile="false" + value="47620" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_ABSTRACT_VIDEO_PLAYLIST" + type="int" + transient="false" + volatile="false" + value="47626" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_AIFF" + type="int" + transient="false" + volatile="false" + value="12295" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_ASF" + type="int" + transient="false" + volatile="false" + value="12300" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_ASSOCIATION" + type="int" + transient="false" + volatile="false" + value="12289" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_ASX_PLAYLIST" + type="int" + transient="false" + volatile="false" + value="47635" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_AUDIBLE" + type="int" + transient="false" + volatile="false" + value="47364" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_AVI" + type="int" + transient="false" + volatile="false" + value="12298" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_BMP" + type="int" + transient="false" + volatile="false" + value="14340" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_DPOF" + type="int" + transient="false" + volatile="false" + value="12294" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_EXECUTABLE" + type="int" + transient="false" + volatile="false" + value="12291" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_EXIF_JPEG" + type="int" + transient="false" + volatile="false" + value="14337" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_FLAC" + type="int" + transient="false" + volatile="false" + value="47366" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_GIF" + type="int" + transient="false" + volatile="false" + value="14343" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_HTML" + type="int" + transient="false" + volatile="false" + value="12293" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_JFIF" + type="int" + transient="false" + volatile="false" + value="14344" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_JP2" + type="int" + transient="false" + volatile="false" + value="14351" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_JPX" + type="int" + transient="false" + volatile="false" + value="14352" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_M3U_PLAYLIST" + type="int" + transient="false" + volatile="false" + value="47633" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_MP2" + type="int" + transient="false" + volatile="false" + value="47491" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_MP3" + type="int" + transient="false" + volatile="false" + value="12297" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_MP4_CONTAINER" + type="int" + transient="false" + volatile="false" + value="47490" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_MPEG" + type="int" + transient="false" + volatile="false" + value="12299" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_MPL_PLAYLIST" + type="int" + transient="false" + volatile="false" + value="47634" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_MS_EXCEL_SPREADSHEET" + type="int" + transient="false" + volatile="false" + value="47749" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_MS_POWERPOINT_PRESENTATION" + type="int" + transient="false" + volatile="false" + value="47750" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_MS_WORD_DOCUMENT" + type="int" + transient="false" + volatile="false" + value="47747" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_OGG" + type="int" + transient="false" + volatile="false" + value="47362" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_PICT" + type="int" + transient="false" + volatile="false" + value="14346" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_PLS_PLAYLIST" + type="int" + transient="false" + volatile="false" + value="47636" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_PNG" + type="int" + transient="false" + volatile="false" + value="14347" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_SCRIPT" + type="int" + transient="false" + volatile="false" + value="12290" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_TEXT" + type="int" + transient="false" + volatile="false" + value="12292" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_TIFF" + type="int" + transient="false" + volatile="false" + value="14349" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_TIFF_EP" + type="int" + transient="false" + volatile="false" + value="14338" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_UNDEFINED" + type="int" + transient="false" + volatile="false" + value="12288" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_UNDEFINED_AUDIO" + type="int" + transient="false" + volatile="false" + value="47360" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_UNDEFINED_COLLECTION" + type="int" + transient="false" + volatile="false" + value="47616" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_UNDEFINED_DOCUMENT" + type="int" + transient="false" + volatile="false" + value="47744" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_UNDEFINED_FIRMWARE" + type="int" + transient="false" + volatile="false" + value="47106" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_UNDEFINED_VIDEO" + type="int" + transient="false" + volatile="false" + value="47488" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_WAV" + type="int" + transient="false" + volatile="false" + value="12296" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_WINDOWS_IMAGE_FORMAT" + type="int" + transient="false" + volatile="false" + value="47233" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_WMA" + type="int" + transient="false" + volatile="false" + value="47361" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_WMV" + type="int" + transient="false" + volatile="false" + value="47489" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_WPL_PLAYLIST" + type="int" + transient="false" + volatile="false" + value="47632" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FORMAT_XML_DOCUMENT" + type="int" + transient="false" + volatile="false" + value="47746" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="PROTECTION_STATUS_NONE" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="PROTECTION_STATUS_NON_TRANSFERABLE_DATA" + type="int" + transient="false" + volatile="false" + value="32771" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="PROTECTION_STATUS_READ_ONLY" + type="int" + transient="false" + volatile="false" + value="32769" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="PROTECTION_STATUS_READ_ONLY_DATA" + type="int" + transient="false" + volatile="false" + value="32770" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="MtpDevice" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<constructor name="MtpDevice" + type="android.mtp.MtpDevice" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="device" type="android.hardware.UsbDevice"> +</parameter> +</constructor> +<method name="close" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="deleteObject" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="objectHandle" type="int"> +</parameter> +</method> +<method name="getDeviceId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDeviceInfo" + return="android.mtp.MtpDeviceInfo" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDeviceName" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getObject" + return="byte[]" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="objectHandle" type="int"> +</parameter> +<parameter name="objectSize" type="int"> +</parameter> +</method> +<method name="getObjectHandles" + return="int[]" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="storageId" type="int"> +</parameter> +<parameter name="format" type="int"> +</parameter> +<parameter name="objectHandle" type="int"> +</parameter> +</method> +<method name="getObjectInfo" + return="android.mtp.MtpObjectInfo" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="objectHandle" type="int"> +</parameter> +</method> +<method name="getParent" + return="long" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="objectHandle" type="int"> +</parameter> +</method> +<method name="getStorageID" + return="long" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="objectHandle" type="int"> +</parameter> +</method> +<method name="getStorageIds" + return="int[]" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getStorageInfo" + return="android.mtp.MtpStorageInfo" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="storageId" type="int"> +</parameter> +</method> +<method name="getThumbnail" + return="byte[]" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="objectHandle" type="int"> +</parameter> +</method> +<method name="importFile" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="objectHandle" type="int"> +</parameter> +<parameter name="destPath" type="java.lang.String"> +</parameter> +</method> +<method name="open" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="manager" type="android.hardware.UsbManager"> +</parameter> +</method> +</class> +<class name="MtpDeviceInfo" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="getManufacturer" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getModel" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getSerialNumber" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getVersion" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +</class> +<class name="MtpObjectInfo" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<method name="getAssociationDesc" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getAssociationType" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getCompressedSize" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDateCreated" + return="long" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDateModified" + return="long" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getFormat" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getImagePixDepth" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getImagePixHeight" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getImagePixWidth" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getKeywords" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getName" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getObjectHandle" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getParent" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getProtectionStatus" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getSequenceNumber" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getStorageId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getThumbCompressedSize" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getThumbFormat" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getThumbPixHeight" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getThumbPixWidth" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +</class> +<class name="MtpStorageInfo" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<method name="getDescription" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getFreeSpace" + return="long" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getMaxCapacity" + return="long" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getStorageId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getVolumeIdentifier" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +</class> +</package> <package name="android.net" > <class name="ConnectivityManager" @@ -112468,6 +115711,71 @@ <parameter name="uid" type="int"> </parameter> </method> +<method name="getUidRxPackets" + return="long" + abstract="false" + native="true" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uid" type="int"> +</parameter> +</method> +<method name="getUidTcpRxBytes" + return="long" + abstract="false" + native="true" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uid" type="int"> +</parameter> +</method> +<method name="getUidTcpRxSegments" + return="long" + abstract="false" + native="true" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uid" type="int"> +</parameter> +</method> +<method name="getUidTcpTxBytes" + return="long" + abstract="false" + native="true" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uid" type="int"> +</parameter> +</method> +<method name="getUidTcpTxSegments" + return="long" + abstract="false" + native="true" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uid" type="int"> +</parameter> +</method> <method name="getUidTxBytes" return="long" abstract="false" @@ -112481,6 +115789,71 @@ <parameter name="uid" type="int"> </parameter> </method> +<method name="getUidTxPackets" + return="long" + abstract="false" + native="true" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uid" type="int"> +</parameter> +</method> +<method name="getUidUdpRxBytes" + return="long" + abstract="false" + native="true" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uid" type="int"> +</parameter> +</method> +<method name="getUidUdpRxPackets" + return="long" + abstract="false" + native="true" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uid" type="int"> +</parameter> +</method> +<method name="getUidUdpTxBytes" + return="long" + abstract="false" + native="true" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uid" type="int"> +</parameter> +</method> +<method name="getUidUdpTxPackets" + return="long" + abstract="false" + native="true" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uid" type="int"> +</parameter> +</method> <field name="UNSUPPORTED" type="int" transient="false" @@ -114289,7 +117662,7 @@ type="android.net.http.SslCertificate" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <parameter name="issuedTo" type="java.lang.String"> @@ -115627,6 +119000,17 @@ visibility="public" > </method> +<method name="getAuthUserName" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getAutoRegistration" return="boolean" abstract="false" @@ -115829,6 +119213,19 @@ visibility="public" > </method> +<method name="setAuthUserName" + return="android.net.sip.SipProfile.Builder" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="name" type="java.lang.String"> +</parameter> +</method> <method name="setAutoRegistration" return="android.net.sip.SipProfile.Builder" abstract="false" @@ -117869,6 +121266,17 @@ visibility="public" > </field> +<field name="WIFI_MODE_FULL_HIGH_PERF" + type="int" + transient="false" + volatile="false" + value="3" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="WIFI_MODE_SCAN_ONLY" type="int" transient="false" @@ -161659,6 +165067,19 @@ visibility="public" > </method> +<method name="getVersion" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +</method> <field name="ACTION_IMAGE_CAPTURE" type="java.lang.String" transient="false" @@ -194263,10 +197684,10 @@ synchronized="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > -<parameter name="addr" type="int"> +<parameter name="ipv4Address" type="int"> </parameter> </method> <method name="formatShortFileSize" @@ -203041,6 +206462,219 @@ </parameter> </method> </class> +<class name="LruCache" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="LruCache" + type="android.util.LruCache" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="maxSize" type="int"> +</parameter> +</constructor> +<method name="create" + return="V" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +<parameter name="key" type="K"> +</parameter> +</method> +<method name="createCount" + return="int" + abstract="false" + native="false" + synchronized="true" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="entryEvicted" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +<parameter name="key" type="K"> +</parameter> +<parameter name="value" type="V"> +</parameter> +</method> +<method name="evictAll" + return="void" + abstract="false" + native="false" + synchronized="true" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="evictionCount" + return="int" + abstract="false" + native="false" + synchronized="true" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="get" + return="V" + abstract="false" + native="false" + synchronized="true" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="key" type="K"> +</parameter> +</method> +<method name="hitCount" + return="int" + abstract="false" + native="false" + synchronized="true" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="maxSize" + return="int" + abstract="false" + native="false" + synchronized="true" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="missCount" + return="int" + abstract="false" + native="false" + synchronized="true" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="put" + return="V" + abstract="false" + native="false" + synchronized="true" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="key" type="K"> +</parameter> +<parameter name="value" type="V"> +</parameter> +</method> +<method name="putCount" + return="int" + abstract="false" + native="false" + synchronized="true" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="remove" + return="V" + abstract="false" + native="false" + synchronized="true" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="key" type="K"> +</parameter> +</method> +<method name="size" + return="int" + abstract="false" + native="false" + synchronized="true" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="sizeOf" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +<parameter name="key" type="K"> +</parameter> +<parameter name="value" type="V"> +</parameter> +</method> +<method name="snapshot" + return="java.util.Map<K, V>" + abstract="false" + native="false" + synchronized="true" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="toString" + return="java.lang.String" + abstract="false" + native="false" + synchronized="true" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +</class> <class name="MalformedJsonException" extends="java.io.IOException" abstract="false" @@ -207032,7 +210666,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="rangeType" type="int"> +<parameter name="axis" type="int"> </parameter> </method> <method name="getName" @@ -207122,7 +210756,7 @@ value="8" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -207133,7 +210767,7 @@ value="2" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -207144,7 +210778,7 @@ value="3" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -207155,7 +210789,7 @@ value="6" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -207166,7 +210800,7 @@ value="7" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -207177,7 +210811,7 @@ value="4" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -207188,7 +210822,7 @@ value="5" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -207199,7 +210833,7 @@ value="0" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -207210,7 +210844,7 @@ value="1" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -207236,6 +210870,17 @@ visibility="public" > </field> +<field name="SOURCE_CLASS_JOYSTICK" + type="int" + transient="false" + volatile="false" + value="16" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="SOURCE_CLASS_MASK" type="int" transient="false" @@ -207291,6 +210936,28 @@ visibility="public" > </field> +<field name="SOURCE_GAMEPAD" + type="int" + transient="false" + volatile="false" + value="1025" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="SOURCE_JOYSTICK" + type="int" + transient="false" + volatile="false" + value="16777232" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="SOURCE_KEYBOARD" type="int" transient="false" @@ -207456,22 +211123,22 @@ </method> <method name="getDeviceId" return="int" - abstract="false" + abstract="true" native="false" synchronized="false" static="false" - final="true" + final="false" deprecated="not deprecated" visibility="public" > </method> <method name="getSource" return="int" - abstract="false" + abstract="true" native="false" synchronized="false" static="false" - final="true" + final="false" deprecated="not deprecated" visibility="public" > @@ -208261,6 +211928,17 @@ <parameter name="c" type="int"> </parameter> </method> +<method name="getDeviceId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getDisplayLabel" return="char" abstract="false" @@ -208434,6 +212112,17 @@ visibility="public" > </method> +<method name="getSource" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getUnicodeChar" return="int" abstract="false" @@ -208690,6 +212379,19 @@ <parameter name="metaState" type="int"> </parameter> </method> +<method name="setSource" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="source" type="int"> +</parameter> +</method> <method name="startTracking" return="void" abstract="false" @@ -209133,6 +212835,182 @@ visibility="public" > </field> +<field name="KEYCODE_BUTTON_1" + type="int" + transient="false" + volatile="false" + value="188" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_10" + type="int" + transient="false" + volatile="false" + value="197" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_11" + type="int" + transient="false" + volatile="false" + value="198" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_12" + type="int" + transient="false" + volatile="false" + value="199" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_13" + type="int" + transient="false" + volatile="false" + value="200" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_14" + type="int" + transient="false" + volatile="false" + value="201" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_15" + type="int" + transient="false" + volatile="false" + value="202" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_16" + type="int" + transient="false" + volatile="false" + value="203" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_2" + type="int" + transient="false" + volatile="false" + value="189" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_3" + type="int" + transient="false" + volatile="false" + value="190" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_4" + type="int" + transient="false" + volatile="false" + value="191" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_5" + type="int" + transient="false" + volatile="false" + value="192" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_6" + type="int" + transient="false" + volatile="false" + value="193" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_7" + type="int" + transient="false" + volatile="false" + value="194" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_8" + type="int" + transient="false" + volatile="false" + value="195" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_9" + type="int" + transient="false" + volatile="false" + value="196" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="KEYCODE_BUTTON_A" type="int" transient="false" @@ -212770,6 +216648,45 @@ visibility="public" > </method> +<method name="getAxisValue" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="axis" type="int"> +</parameter> +</method> +<method name="getAxisValue" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="axis" type="int"> +</parameter> +<parameter name="pointerIndex" type="int"> +</parameter> +</method> +<method name="getDeviceId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getDownTime" return="long" abstract="false" @@ -212814,6 +216731,38 @@ visibility="public" > </method> +<method name="getHistoricalAxisValue" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="axis" type="int"> +</parameter> +<parameter name="pos" type="int"> +</parameter> +</method> +<method name="getHistoricalAxisValue" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="axis" type="int"> +</parameter> +<parameter name="pointerIndex" type="int"> +</parameter> +<parameter name="pos" type="int"> +</parameter> +</method> <method name="getHistoricalEventTime" return="long" abstract="false" @@ -213251,6 +217200,17 @@ <parameter name="pointerIndex" type="int"> </parameter> </method> +<method name="getSource" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getToolMajor" return="float" abstract="false" @@ -213559,7 +217519,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="o" type="android.view.MotionEvent"> +<parameter name="other" type="android.view.MotionEvent"> </parameter> </method> <method name="obtainNoHistory" @@ -213572,7 +217532,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="o" type="android.view.MotionEvent"> +<parameter name="other" type="android.view.MotionEvent"> </parameter> </method> <method name="offsetLocation" @@ -213642,6 +217602,19 @@ <parameter name="y" type="float"> </parameter> </method> +<method name="setSource" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="source" type="int"> +</parameter> +</method> <method name="transform" return="void" abstract="false" @@ -213868,6 +217841,105 @@ visibility="public" > </field> +<field name="AXIS_ORIENTATION" + type="int" + transient="false" + volatile="false" + value="8" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AXIS_PRESSURE" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AXIS_SIZE" + type="int" + transient="false" + volatile="false" + value="3" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AXIS_TOOL_MAJOR" + type="int" + transient="false" + volatile="false" + value="6" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AXIS_TOOL_MINOR" + type="int" + transient="false" + volatile="false" + value="7" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AXIS_TOUCH_MAJOR" + type="int" + transient="false" + volatile="false" + value="4" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AXIS_TOUCH_MINOR" + type="int" + transient="false" + volatile="false" + value="5" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AXIS_X" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AXIS_Y" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="CREATOR" type="android.os.Parcelable.Creator" transient="false" @@ -213950,6 +218022,68 @@ visibility="public" > </constructor> +<constructor name="MotionEvent.PointerCoords" + type="android.view.MotionEvent.PointerCoords" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="other" type="android.view.MotionEvent.PointerCoords"> +</parameter> +</constructor> +<method name="clear" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="copyFrom" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="other" type="android.view.MotionEvent.PointerCoords"> +</parameter> +</method> +<method name="getAxisValue" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="axis" type="int"> +</parameter> +</method> +<method name="setAxisValue" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="axis" type="int"> +</parameter> +<parameter name="value" type="float"> +</parameter> +</method> <field name="orientation" type="float" transient="false" @@ -216250,6 +220384,19 @@ <parameter name="canvas" type="android.graphics.Canvas"> </parameter> </method> +<method name="dispatchGenericMotionEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.MotionEvent"> +</parameter> +</method> <method name="dispatchKeyEvent" return="boolean" abstract="false" @@ -218405,6 +222552,19 @@ <parameter name="previouslyFocusedRect" type="android.graphics.Rect"> </parameter> </method> +<method name="onGenericMotionEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.MotionEvent"> +</parameter> +</method> <method name="onKeyDown" return="boolean" abstract="false" @@ -225419,6 +229579,19 @@ <parameter name="hardwareAccelerated" type="boolean"> </parameter> </method> +<method name="superDispatchGenericMotionEvent" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.MotionEvent"> +</parameter> +</method> <method name="superDispatchKeyEvent" return="boolean" abstract="true" @@ -225764,6 +229937,19 @@ deprecated="not deprecated" visibility="public" > +<method name="dispatchGenericMotionEvent" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.MotionEvent"> +</parameter> +</method> <method name="dispatchKeyEvent" return="boolean" abstract="true" @@ -233786,6 +237972,19 @@ > <implements name="android.os.Parcelable"> </implements> +<method name="containsExtraValueKey" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="key" type="java.lang.String"> +</parameter> +</method> <method name="describeContents" return="int" abstract="false" @@ -233808,6 +238007,19 @@ visibility="public" > </method> +<method name="getExtraValueOf" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="key" type="java.lang.String"> +</parameter> +</method> <method name="getIconResId" return="int" abstract="false" @@ -234279,6 +238491,17 @@ visibility="public" > </method> +<method name="allowFileSchemeCookies" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getCookie" return="java.lang.String" abstract="false" @@ -234360,6 +238583,19 @@ <parameter name="accept" type="boolean"> </parameter> </method> +<method name="setAcceptFileSchemeCookies" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="accept" type="boolean"> +</parameter> +</method> <method name="setCookie" return="void" abstract="false" @@ -253908,6 +258144,21 @@ <parameter name="started" type="boolean"> </parameter> </method> +<method name="setDisplayedChild" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="viewId" type="int"> +</parameter> +<parameter name="childIndex" type="int"> +</parameter> +</method> <method name="setDouble" return="void" abstract="false" diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index b1b7715a8aab..9e6bcc8b1653 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -116,9 +116,7 @@ static void dumpstate() { #ifdef FWDUMP_bcm4329 run_command("DUMP WIFI STATUS", 20, - "su", "root", "dhdutil", "-i", "eth0", "dump", NULL); - run_command("DUMP WIFI FIRMWARE LOG", 60, - "su", "root", "dhdutil", "-i", "eth0", "upload", "/data/local/tmp/wlan_crash.dump", NULL); + "su", "root", "dhdutil", "-i", "wlan0", "dump", NULL); run_command("DUMP WIFI INTERNAL COUNTERS", 20, "su", "root", "wlutil", "counters", NULL); #endif @@ -143,7 +141,7 @@ static void dumpstate() { dump_file("BINDER STATS", "/sys/kernel/debug/binder/stats"); dump_file("BINDER STATE", "/sys/kernel/debug/binder/state"); - run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL); + run_command("FILESYSTEMS & FREE SPACE", 10, "su", "root", "df", NULL); dump_file("PACKAGE SETTINGS", "/data/system/packages.xml"); dump_file("PACKAGE UID ERRORS", "/data/system/uiderrors.txt"); diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c index f92acbbbf332..b2f9e800dca7 100644 --- a/cmds/dumpstate/utils.c +++ b/cmds/dumpstate/utils.c @@ -167,6 +167,7 @@ int run_command(const char *title, int timeout_seconds, const char *command, ... execvp(command, (char**) args); printf("*** exec(%s): %s\n", command, strerror(errno)); + fflush(stdout); _exit(-1); } @@ -178,7 +179,7 @@ int run_command(const char *title, int timeout_seconds, const char *command, ... if (p == pid) { if (WIFSIGNALED(status)) { printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status)); - } else if (WEXITSTATUS(status) > 0) { + } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) { printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status)); } if (title) printf("[%s: %.1fs elapsed]\n\n", command, elapsed); diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index cde1573743c1..4d49c30501f5 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -17,7 +17,7 @@ #include "installd.h" #include <diskusage/dirsize.h> -int install(const char *pkgname, int encrypted_fs_flag, uid_t uid, gid_t gid) +int install(const char *pkgname, uid_t uid, gid_t gid) { char pkgdir[PKG_PATH_MAX]; char libdir[PKG_PATH_MAX]; @@ -27,17 +27,10 @@ int install(const char *pkgname, int encrypted_fs_flag, uid_t uid, gid_t gid) return -1; } - if (encrypted_fs_flag == USE_UNENCRYPTED_FS) { - if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) - return -1; - if (create_pkg_path(libdir, PKG_LIB_PREFIX, pkgname, PKG_LIB_POSTFIX)) - return -1; - } else { - if (create_pkg_path(pkgdir, PKG_SEC_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) - return -1; - if (create_pkg_path(libdir, PKG_SEC_LIB_PREFIX, pkgname, PKG_LIB_POSTFIX)) - return -1; - } + if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) + return -1; + if (create_pkg_path(libdir, PKG_LIB_PREFIX, pkgname, PKG_LIB_POSTFIX)) + return -1; if (mkdir(pkgdir, 0751) < 0) { LOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno)); @@ -62,38 +55,26 @@ int install(const char *pkgname, int encrypted_fs_flag, uid_t uid, gid_t gid) return 0; } -int uninstall(const char *pkgname, int encrypted_fs_flag) +int uninstall(const char *pkgname) { char pkgdir[PKG_PATH_MAX]; - if (encrypted_fs_flag == USE_UNENCRYPTED_FS) { - if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) - return -1; - } else { - if (create_pkg_path(pkgdir, PKG_SEC_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) - return -1; - } + if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) + return -1; /* delete contents AND directory, no exceptions */ return delete_dir_contents(pkgdir, 1, 0); } -int renamepkg(const char *oldpkgname, const char *newpkgname, int encrypted_fs_flag) +int renamepkg(const char *oldpkgname, const char *newpkgname) { char oldpkgdir[PKG_PATH_MAX]; char newpkgdir[PKG_PATH_MAX]; - if (encrypted_fs_flag == USE_UNENCRYPTED_FS) { - if (create_pkg_path(oldpkgdir, PKG_DIR_PREFIX, oldpkgname, PKG_DIR_POSTFIX)) - return -1; - if (create_pkg_path(newpkgdir, PKG_DIR_PREFIX, newpkgname, PKG_DIR_POSTFIX)) - return -1; - } else { - if (create_pkg_path(oldpkgdir, PKG_SEC_DIR_PREFIX, oldpkgname, PKG_DIR_POSTFIX)) - return -1; - if (create_pkg_path(newpkgdir, PKG_SEC_DIR_PREFIX, newpkgname, PKG_DIR_POSTFIX)) - return -1; - } + if (create_pkg_path(oldpkgdir, PKG_DIR_PREFIX, oldpkgname, PKG_DIR_POSTFIX)) + return -1; + if (create_pkg_path(newpkgdir, PKG_DIR_PREFIX, newpkgname, PKG_DIR_POSTFIX)) + return -1; if (rename(oldpkgdir, newpkgdir) < 0) { LOGE("cannot rename dir '%s' to '%s': %s\n", oldpkgdir, newpkgdir, strerror(errno)); @@ -102,41 +83,28 @@ int renamepkg(const char *oldpkgname, const char *newpkgname, int encrypted_fs_f return 0; } -int delete_user_data(const char *pkgname, int encrypted_fs_flag) +int delete_user_data(const char *pkgname) { char pkgdir[PKG_PATH_MAX]; - if (encrypted_fs_flag == USE_UNENCRYPTED_FS) { - if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) - return -1; - } else { - if (create_pkg_path(pkgdir, PKG_SEC_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) - return -1; - } + if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) + return -1; /* delete contents, excluding "lib", but not the directory itself */ return delete_dir_contents(pkgdir, 0, "lib"); } -int delete_cache(const char *pkgname, int encrypted_fs_flag) +int delete_cache(const char *pkgname) { char cachedir[PKG_PATH_MAX]; - if (encrypted_fs_flag == USE_UNENCRYPTED_FS) { - if (create_pkg_path(cachedir, CACHE_DIR_PREFIX, pkgname, CACHE_DIR_POSTFIX)) - return -1; - } else { - if (create_pkg_path(cachedir, CACHE_SEC_DIR_PREFIX, pkgname, CACHE_DIR_POSTFIX)) - return -1; - } + if (create_pkg_path(cachedir, CACHE_DIR_PREFIX, pkgname, CACHE_DIR_POSTFIX)) + return -1; /* delete contents, not the directory, no exceptions */ return delete_dir_contents(cachedir, 0, 0); } -/* TODO(oam): depending on use case (ecryptfs or dmcrypt) - * change implementation - */ static int64_t disk_free() { struct statfs sfs; @@ -169,39 +137,6 @@ int free_cache(int64_t free_size) LOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", free_size, avail); if (avail >= free_size) return 0; - /* First try encrypted dir */ - d = opendir(PKG_SEC_DIR_PREFIX); - if (d == NULL) { - LOGE("cannot open %s: %s\n", PKG_SEC_DIR_PREFIX, strerror(errno)); - } else { - dfd = dirfd(d); - - while ((de = readdir(d))) { - if (de->d_type != DT_DIR) continue; - name = de->d_name; - - /* always skip "." and ".." */ - if (name[0] == '.') { - if (name[1] == 0) continue; - if ((name[1] == '.') && (name[2] == 0)) continue; - } - - subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY); - if (subfd < 0) continue; - - delete_dir_contents_fd(subfd, "cache"); - close(subfd); - - avail = disk_free(); - if (avail >= free_size) { - closedir(d); - return 0; - } - } - closedir(d); - } - - /* Next try unencrypted dir... */ d = opendir(PKG_DIR_PREFIX); if (d == NULL) { LOGE("cannot open %s: %s\n", PKG_DIR_PREFIX, strerror(errno)); @@ -330,7 +265,7 @@ int protect(char *pkgname, gid_t gid) int get_size(const char *pkgname, const char *apkpath, const char *fwdlock_apkpath, - int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize, int encrypted_fs_flag) + int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize) { DIR *d; int dfd; @@ -365,14 +300,8 @@ int get_size(const char *pkgname, const char *apkpath, } } - if (encrypted_fs_flag == 0) { - if (create_pkg_path(path, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) { - goto done; - } - } else { - if (create_pkg_path(path, PKG_SEC_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) { - goto done; - } + if (create_pkg_path(path, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) { + goto done; } d = opendir(path); diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c index 9ba6402f1e95..d2b2f7f9fc60 100644 --- a/cmds/installd/installd.c +++ b/cmds/installd/installd.c @@ -29,7 +29,7 @@ static int do_ping(char **arg, char reply[REPLY_MAX]) static int do_install(char **arg, char reply[REPLY_MAX]) { - return install(arg[0], atoi(arg[1]), atoi(arg[2]), atoi(arg[3])); /* pkgname, uid, gid */ + return install(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, gid */ } static int do_dexopt(char **arg, char reply[REPLY_MAX]) @@ -50,12 +50,12 @@ static int do_rm_dex(char **arg, char reply[REPLY_MAX]) static int do_remove(char **arg, char reply[REPLY_MAX]) { - return uninstall(arg[0], atoi(arg[1])); /* pkgname */ + return uninstall(arg[0]); /* pkgname */ } static int do_rename(char **arg, char reply[REPLY_MAX]) { - return renamepkg(arg[0], arg[1], atoi(arg[2])); /* oldpkgname, newpkgname */ + return renamepkg(arg[0], arg[1]); /* oldpkgname, newpkgname */ } static int do_free_cache(char **arg, char reply[REPLY_MAX]) /* TODO int:free_size */ @@ -65,7 +65,7 @@ static int do_free_cache(char **arg, char reply[REPLY_MAX]) /* TODO int:free_siz static int do_rm_cache(char **arg, char reply[REPLY_MAX]) { - return delete_cache(arg[0], atoi(arg[1])); /* pkgname */ + return delete_cache(arg[0]); /* pkgname */ } static int do_protect(char **arg, char reply[REPLY_MAX]) @@ -81,7 +81,7 @@ static int do_get_size(char **arg, char reply[REPLY_MAX]) int res = 0; /* pkgdir, apkpath */ - res = get_size(arg[0], arg[1], arg[2], &codesize, &datasize, &cachesize, atoi(arg[3])); + res = get_size(arg[0], arg[1], arg[2], &codesize, &datasize, &cachesize); /* * Each int64_t can take up 22 characters printed out. Make sure it @@ -93,7 +93,7 @@ static int do_get_size(char **arg, char reply[REPLY_MAX]) static int do_rm_user_data(char **arg, char reply[REPLY_MAX]) { - return delete_user_data(arg[0], atoi(arg[1])); /* pkgname */ + return delete_user_data(arg[0]); /* pkgname */ } static int do_movefiles(char **arg, char reply[REPLY_MAX]) @@ -119,17 +119,17 @@ struct cmdinfo { struct cmdinfo cmds[] = { { "ping", 0, do_ping }, - { "install", 4, do_install }, + { "install", 3, do_install }, { "dexopt", 3, do_dexopt }, { "movedex", 2, do_move_dex }, { "rmdex", 1, do_rm_dex }, - { "remove", 2, do_remove }, - { "rename", 3, do_rename }, + { "remove", 1, do_remove }, + { "rename", 2, do_rename }, { "freecache", 1, do_free_cache }, - { "rmcache", 2, do_rm_cache }, + { "rmcache", 1, do_rm_cache }, { "protect", 2, do_protect }, - { "getsize", 4, do_get_size }, - { "rmuserdata", 2, do_rm_user_data }, + { "getsize", 3, do_get_size }, + { "rmuserdata", 1, do_rm_user_data }, { "movefiles", 0, do_movefiles }, { "linklib", 2, do_linklib }, { "unlinklib", 1, do_unlinklib }, diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index 59475e96cfda..77b58ec10692 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -50,23 +50,16 @@ /* elements combined with a valid package name to form paths */ #define PKG_DIR_PREFIX "/data/data/" -#define PKG_SEC_DIR_PREFIX "/data/secure/data/" #define PKG_DIR_POSTFIX "" #define PKG_LIB_PREFIX "/data/data/" -#define PKG_SEC_LIB_PREFIX "/data/secure/data/" #define PKG_LIB_POSTFIX "/lib" #define CACHE_DIR_PREFIX "/data/data/" -#define CACHE_SEC_DIR_PREFIX "/data/secure/data/" #define CACHE_DIR_POSTFIX "/cache" #define APK_DIR_PREFIX "/data/app/" -/* Encrypted File SYstems constants */ -#define USE_ENCRYPTED_FS 1 -#define USE_UNENCRYPTED_FS 0 - /* other handy constants */ #define PROTECTED_DIR_PREFIX "/data/app-private/" @@ -98,16 +91,16 @@ int delete_dir_contents_fd(int dfd, const char *name); /* commands.c */ -int install(const char *pkgname, int encrypted_fs_flag, uid_t uid, gid_t gid); -int uninstall(const char *pkgname, int encrypted_fs_flag); -int renamepkg(const char *oldpkgname, const char *newpkgname, int encrypted_fs_flag); -int delete_user_data(const char *pkgname, int encrypted_fs_flag); -int delete_cache(const char *pkgname, int encrypted_fs_flag); +int install(const char *pkgname, uid_t uid, gid_t gid); +int uninstall(const char *pkgname); +int renamepkg(const char *oldpkgname, const char *newpkgname); +int delete_user_data(const char *pkgname); +int delete_cache(const char *pkgname); int move_dex(const char *src, const char *dst); int rm_dex(const char *path); int protect(char *pkgname, gid_t gid); int get_size(const char *pkgname, const char *apkpath, const char *fwdlock_apkpath, - int64_t *codesize, int64_t *datasize, int64_t *cachesize, int encrypted_fs_flag); + int64_t *codesize, int64_t *datasize, int64_t *cachesize); int free_cache(int64_t free_size); int dexopt(const char *apk_path, uid_t uid, int is_public); int movefiles(); diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c index ba7f807eb6ac..2df450f34def 100644 --- a/cmds/servicemanager/service_manager.c +++ b/cmds/servicemanager/service_manager.c @@ -34,7 +34,6 @@ static struct { { AID_MEDIA, "media.player" }, { AID_MEDIA, "media.camera" }, { AID_MEDIA, "media.audio_policy" }, - { AID_DRMIO, "drm.drmIOService" }, { AID_DRM, "drm.drmManager" }, { AID_NFC, "nfc" }, { AID_RADIO, "radio.phone" }, diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp index 1dc08ea2dc1d..74649a9bf36e 100644 --- a/cmds/stagefright/sf2.cpp +++ b/cmds/stagefright/sf2.cpp @@ -170,6 +170,7 @@ protected: mCodec->signalResume(); (new AMessage(kWhatSeek, id()))->post(5000000ll); + } else if (what == ACodec::kWhatOutputFormatChanged) { } else { CHECK_EQ(what, (int32_t)ACodec::kWhatShutdownCompleted); diff --git a/core/java/android/animation/FloatKeyframeSet.java b/core/java/android/animation/FloatKeyframeSet.java index 4009f139db73..377b5a054668 100644 --- a/core/java/android/animation/FloatKeyframeSet.java +++ b/core/java/android/animation/FloatKeyframeSet.java @@ -87,7 +87,7 @@ class FloatKeyframeSet extends KeyframeSet { } float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); return mEvaluator == null ? - prevValue + fraction * (nextValue - prevValue) : + prevValue + intervalFraction * (nextValue - prevValue) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). floatValue(); } else if (fraction >= 1f) { @@ -103,7 +103,7 @@ class FloatKeyframeSet extends KeyframeSet { } float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); return mEvaluator == null ? - prevValue + fraction * (nextValue - prevValue) : + prevValue + intervalFraction * (nextValue - prevValue) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). floatValue(); } @@ -120,7 +120,7 @@ class FloatKeyframeSet extends KeyframeSet { float prevValue = prevKeyframe.getFloatValue(); float nextValue = nextKeyframe.getFloatValue(); return mEvaluator == null ? - prevValue + fraction * (nextValue - prevValue) : + prevValue + intervalFraction * (nextValue - prevValue) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). floatValue(); } diff --git a/core/java/android/animation/IntKeyframeSet.java b/core/java/android/animation/IntKeyframeSet.java index 5629c5ef9783..7b7c876ba97b 100644 --- a/core/java/android/animation/IntKeyframeSet.java +++ b/core/java/android/animation/IntKeyframeSet.java @@ -87,7 +87,7 @@ class IntKeyframeSet extends KeyframeSet { } float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); return mEvaluator == null ? - prevValue + (int)(fraction * (nextValue - prevValue)) : + prevValue + (int)(intervalFraction * (nextValue - prevValue)) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). intValue(); } else if (fraction >= 1f) { @@ -103,7 +103,7 @@ class IntKeyframeSet extends KeyframeSet { } float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); return mEvaluator == null ? - prevValue + (int)(fraction * (nextValue - prevValue)) : + prevValue + (int)(intervalFraction * (nextValue - prevValue)) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue(); } IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0); @@ -119,7 +119,7 @@ class IntKeyframeSet extends KeyframeSet { int prevValue = prevKeyframe.getIntValue(); int nextValue = nextKeyframe.getIntValue(); return mEvaluator == null ? - prevValue + (int)(fraction * (nextValue - prevValue)) : + prevValue + (int)(intervalFraction * (nextValue - prevValue)) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). intValue(); } diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java index fa61b7129e88..6172aabb41c7 100644 --- a/core/java/android/animation/KeyframeSet.java +++ b/core/java/android/animation/KeyframeSet.java @@ -212,4 +212,13 @@ class KeyframeSet { // shouldn't reach here return mLastKeyframe.getValue(); } + + @Override + public String toString() { + String returnVal = " "; + for (int i = 0; i < mNumKeyframes; ++i) { + returnVal += mKeyframes.get(i).getValue() + " "; + } + return returnVal; + } } diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java index d3e10f394007..8b59554c4a61 100644 --- a/core/java/android/animation/LayoutTransition.java +++ b/core/java/android/animation/LayoutTransition.java @@ -617,7 +617,6 @@ public class LayoutTransition { Animator prevAnimation = currentChangingAnimations.get(child); if (prevAnimation != null) { prevAnimation.cancel(); - currentChangingAnimations.remove(child); } Animator pendingAnimation = pendingAnimations.get(child); if (pendingAnimation != null) { @@ -639,7 +638,6 @@ public class LayoutTransition { }; // Remove the animation from the cache when it ends anim.addListener(new AnimatorListenerAdapter() { - private boolean canceled = false; @Override public void onAnimationStart(Animator animator) { @@ -654,17 +652,13 @@ public class LayoutTransition { @Override public void onAnimationCancel(Animator animator) { - // we remove canceled animations immediately, not here - canceled = true; child.removeOnLayoutChangeListener(listener); layoutChangeListenerMap.remove(child); } @Override public void onAnimationEnd(Animator animator) { - if (!canceled) { - currentChangingAnimations.remove(child); - } + currentChangingAnimations.remove(child); if (mListeners != null) { for (TransitionListener listener : mListeners) { listener.endTransition(LayoutTransition.this, parent, child, @@ -719,6 +713,28 @@ public class LayoutTransition { } /** + * Cancels the currently running transition. Note that we cancel() the changing animations + * but end() the visibility animations. This is because this method is currently called + * in the context of starting a new transition, so we want to move things from their mid- + * transition positions, but we want them to have their end-transition visibility. + * + * @hide + */ + public void cancel() { + HashMap<View, Animator> currentAnimCopy = + (HashMap<View, Animator>) currentChangingAnimations.clone(); + for (Animator anim : currentAnimCopy.values()) { + anim.cancel(); + } + currentChangingAnimations.clear(); + currentAnimCopy = (HashMap<View, Animator>) currentVisibilityAnimations.clone(); + for (Animator anim : currentAnimCopy.values()) { + anim.end(); + } + currentVisibilityAnimations.clear(); + } + + /** * This method runs the animation that makes an added item appear. * * @param parent The ViewGroup to which the View is being added. @@ -810,6 +826,9 @@ public class LayoutTransition { * @param child The View being added to the ViewGroup. */ public void addChild(ViewGroup parent, View child) { + if (isRunning()) { + cancel(); + } if (mListeners != null) { for (TransitionListener listener : mListeners) { listener.startTransition(this, parent, child, APPEARING); @@ -842,6 +861,9 @@ public class LayoutTransition { * @param child The View being removed from the ViewGroup. */ public void removeChild(ViewGroup parent, View child) { + if (isRunning()) { + cancel(); + } if (mListeners != null) { for (TransitionListener listener : mListeners) { listener.startTransition(this, parent, child, DISAPPEARING); diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java index d038cd6d1a97..b8a7cb273bfd 100644 --- a/core/java/android/animation/ObjectAnimator.java +++ b/core/java/android/animation/ObjectAnimator.java @@ -394,4 +394,16 @@ public final class ObjectAnimator extends ValueAnimator { final ObjectAnimator anim = (ObjectAnimator) super.clone(); return anim; } + + @Override + public String toString() { + String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " + + mTarget; + if (mValues != null) { + for (int i = 0; i < mValues.length; ++i) { + returnVal += "\n " + mValues[i].toString(); + } + } + return returnVal; + } } diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java index 0c30aadea953..6f91fc0fd42b 100644 --- a/core/java/android/animation/PropertyValuesHolder.java +++ b/core/java/android/animation/PropertyValuesHolder.java @@ -578,6 +578,11 @@ public class PropertyValuesHolder implements Cloneable { return mAnimatedValue; } + @Override + public String toString() { + return mPropertyName + ": " + mKeyframeSet.toString(); + } + /** * Utility method to derive a setter/getter method name from a property name, where the * prefix is typically "set" or "get" and the first letter of the property name is diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 5705057f25ee..2e44d6d535c7 100755 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -895,7 +895,14 @@ public class ValueAnimator extends Animator { throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } mPlayingBackwards = playBackwards; + mCurrentIteration = 0; + mPlayingState = STOPPED; + mStartedDelay = false; + sPendingAnimations.get().add(this); if (mStartDelay == 0) { + // This sets the initial value of the animation, prior to actually starting it running + setCurrentPlayTime(getCurrentPlayTime()); + if (mListeners != null) { ArrayList<AnimatorListener> tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone(); @@ -904,13 +911,7 @@ public class ValueAnimator extends Animator { tmpListeners.get(i).onAnimationStart(this); } } - // This sets the initial value of the animation, prior to actually starting it running - setCurrentPlayTime(getCurrentPlayTime()); } - mCurrentIteration = 0; - mPlayingState = STOPPED; - mStartedDelay = false; - sPendingAnimations.get().add(this); AnimationHandler animationHandler = sAnimationHandler.get(); if (animationHandler == null) { animationHandler = new AnimationHandler(); @@ -947,6 +948,8 @@ public class ValueAnimator extends Animator { // Special case if the animation has not yet started; get it ready for ending mStartedDelay = false; startAnimation(); + } else if (!mInitialized) { + initAnimation(); } // The final value set on the target varies, depending on whether the animation // was supposed to repeat an odd number of times @@ -1204,4 +1207,15 @@ public class ValueAnimator extends Animator { sPendingAnimations.get().clear(); sDelayedAnims.get().clear(); } + + @Override + public String toString() { + String returnVal = "ValueAnimator@" + Integer.toHexString(hashCode()); + if (mValues != null) { + for (int i = 0; i < mValues.length; ++i) { + returnVal += "\n " + mValues[i].toString(); + } + } + return returnVal; + } } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d5b00554d3ec..67e480603f72 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -349,7 +349,7 @@ import java.util.HashMap; * section for more information on how the lifecycle of a process is tied * to the activities it is hosting. Note that it is important to save * persistent data in {@link #onPause} instead of {@link #onSaveInstanceState} - * because the later is not part of the lifecycle callbacks, so will not + * because the latter is not part of the lifecycle callbacks, so will not * be called in every situation as described in its documentation.</p> * * <p class="note">Be aware that these semantics will change slightly between @@ -1073,6 +1073,7 @@ public class Activity extends ContextThemeWrapper protected void onPostResume() { final Window win = getWindow(); if (win != null) win.makeActive(); + if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true); mCalled = true; } @@ -1325,6 +1326,7 @@ public class Activity extends ContextThemeWrapper * @see #onDestroy */ protected void onStop() { + if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); mCalled = true; } @@ -2115,7 +2117,39 @@ public class Activity extends ContextThemeWrapper public boolean onTrackballEvent(MotionEvent event) { return false; } - + + /** + * Called when a generic motion event was not handled by any of the + * views inside of the activity. + * <p> + * Generic motion events are dispatched to the focused view to describe + * the motions of input devices such as joysticks. The + * {@link MotionEvent#getSource() source} of the motion event specifies + * the class of input that was received. Implementations of this method + * must examine the bits in the source before processing the event. + * The following code example shows how this is done. + * </p> + * <code> + * public boolean onGenericMotionEvent(MotionEvent event) { + * if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { + * float x = event.getX(); + * float y = event.getY(); + * // process the joystick motion + * return true; + * } + * return super.onGenericMotionEvent(event); + * } + * </code> + * + * @param event The generic motion event being processed. + * + * @return Return true if you have consumed the event, false if you haven't. + * The default implementation always returns false. + */ + public boolean onGenericMotionEvent(MotionEvent event) { + return false; + } + /** * Called whenever a key, touch, or trackball event is dispatched to the * activity. Implement this method if you wish to know that the user has @@ -2298,6 +2332,24 @@ public class Activity extends ContextThemeWrapper return onTrackballEvent(ev); } + /** + * Called to process generic motion events. You can override this to + * intercept all generic motion events before they are dispatched to the + * window. Be sure to call this implementation for generic motion events + * that should be handled normally. + * + * @param ev The generic motion event. + * + * @return boolean Return true if this event was consumed. + */ + public boolean dispatchGenericMotionEvent(MotionEvent ev) { + onUserInteraction(); + if (getWindow().superDispatchGenericMotionEvent(ev)) { + return true; + } + return onGenericMotionEvent(ev); + } + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { event.setClassName(getClass().getName()); event.setPackageName(getPackageName()); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index d76b67d3ef2a..a660076b4b03 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -112,6 +112,11 @@ public class ActivityManager { public int id; /** + * The true identifier of this task, valid even if it is not running. + */ + public int persistentId; + + /** * The original Intent used to launch the task. You can use this * Intent to re-launch the task (if it is no longer running) or bring * the current task to the front. @@ -127,14 +132,6 @@ public class ActivityManager { public ComponentName origActivity; /** - * Thumbnail representation of the task's last state. Must - * use {@link ActivityManager#TASKS_GET_THUMBNAILS} to have this set. - * @hide -- this is not scalable, need to have a separate API to get - * the bitmap. - */ - public Bitmap thumbnail; - - /** * Description of the task's last state. */ public CharSequence description; @@ -148,6 +145,7 @@ public class ActivityManager { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); + dest.writeInt(persistentId); if (baseIntent != null) { dest.writeInt(1); baseIntent.writeToParcel(dest, 0); @@ -155,29 +153,19 @@ public class ActivityManager { dest.writeInt(0); } ComponentName.writeToParcel(origActivity, dest); - if (thumbnail != null) { - dest.writeInt(1); - thumbnail.writeToParcel(dest, 0); - } else { - dest.writeInt(0); - } TextUtils.writeToParcel(description, dest, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } public void readFromParcel(Parcel source) { id = source.readInt(); + persistentId = source.readInt(); if (source.readInt() != 0) { baseIntent = Intent.CREATOR.createFromParcel(source); } else { baseIntent = null; } origActivity = ComponentName.readFromParcel(source); - if (source.readInt() != 0) { - thumbnail = Bitmap.CREATOR.createFromParcel(source); - } else { - thumbnail = null; - } description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); } @@ -401,6 +389,16 @@ public class ActivityManager { return getRunningTasks(maxNum, 0, null); } + /** @hide */ + public Bitmap getTaskThumbnail(int id) throws SecurityException { + try { + return ActivityManagerNative.getDefault().getTaskThumbnail(id); + } catch (RemoteException e) { + // System dead, we will be dead too soon! + return null; + } + } + /** * Flag for {@link #moveTaskToFront(int, int)}: also move the "home" * activity along with the task, so it is positioned immediately behind @@ -409,6 +407,13 @@ public class ActivityManager { public static final int MOVE_TASK_WITH_HOME = 0x00000001; /** + * Flag for {@link #moveTaskToFront(int, int)}: don't count this as a + * user-instigated action, so the current activity will not receive a + * hint that the user is leaving. + */ + public static final int MOVE_TASK_NO_USER_ACTION = 0x00000002; + + /** * Ask that the task associated with a given task ID be moved to the * front of the stack, so it is now visible to the user. Requires that * the caller hold permission {@link android.Manifest.permission#REORDER_TASKS} diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index c095c069918b..d3d379294789 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -442,6 +442,20 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_TASK_THUMBNAIL_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + int id = data.readInt(); + Bitmap bm = getTaskThumbnail(id); + reply.writeNoException(); + if (bm != null) { + reply.writeInt(1); + bm.writeToParcel(reply, 0); + } else { + reply.writeInt(0); + } + return true; + } + case GET_SERVICES_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int maxNum = data.readInt(); @@ -1816,6 +1830,21 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return list; } + public Bitmap getTaskThumbnail(int id) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(id); + mRemote.transact(GET_TASK_THUMBNAIL_TRANSACTION, data, reply, 0); + reply.readException(); + Bitmap bm = null; + if (reply.readInt() != 0) { + bm = Bitmap.CREATOR.createFromParcel(reply); + } + data.recycle(); + reply.recycle(); + return bm; + } public List getServices(int maxNum, int flags) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 6f639906097d..8737e93495c9 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -41,7 +41,9 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.hardware.IUsbManager; import android.hardware.SensorManager; +import android.hardware.UsbManager; import android.location.CountryDetector; import android.location.ICountryDetector; import android.location.ILocationManager; @@ -399,6 +401,12 @@ class ContextImpl extends Context { return new UiModeManager(); }}); + registerService(USB_SERVICE, new StaticServiceFetcher() { + public Object createStaticService() { + IBinder b = ServiceManager.getService(USB_SERVICE); + return new UsbManager(IUsbManager.Stub.asInterface(b)); + }}); + registerService(VIBRATOR_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return new Vibrator(); diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 23d4065622ac..cc4fefc3ea3b 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -352,12 +352,14 @@ public class Dialog implements DialogInterface, Window.Callback, * Called when the dialog is starting. */ protected void onStart() { + if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true); } /** * Called to tell you that you're stopping. */ protected void onStop() { + if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); } private static final String DIALOG_SHOWING_TAG = "android:dialogShowing"; @@ -620,7 +622,39 @@ public class Dialog implements DialogInterface, Window.Callback, public boolean onTrackballEvent(MotionEvent event) { return false; } - + + /** + * Called when a generic motion event was not handled by any of the + * views inside of the dialog. + * <p> + * Generic motion events are dispatched to the focused view to describe + * the motions of input devices such as joysticks. The + * {@link MotionEvent#getSource() source} of the motion event specifies + * the class of input that was received. Implementations of this method + * must examine the bits in the source before processing the event. + * The following code example shows how this is done. + * </p> + * <code> + * public boolean onGenericMotionEvent(MotionEvent event) { + * if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { + * float x = event.getX(); + * float y = event.getY(); + * // process the joystick motion + * return true; + * } + * return super.onGenericMotionEvent(event); + * } + * </code> + * + * @param event The generic motion event being processed. + * + * @return Return true if you have consumed the event, false if you haven't. + * The default implementation always returns false. + */ + public boolean onGenericMotionEvent(MotionEvent event) { + return false; + } + public void onWindowAttributesChanged(WindowManager.LayoutParams params) { if (mDecor != null) { mWindowManager.updateViewLayout(mDecor, params); @@ -709,6 +743,23 @@ public class Dialog implements DialogInterface, Window.Callback, return onTrackballEvent(ev); } + /** + * Called to process generic motion events. You can override this to + * intercept all generic motion events before they are dispatched to the + * window. Be sure to call this implementation for generic motion events + * that should be handled normally. + * + * @param ev The generic motion event. + * + * @return boolean Return true if this event was consumed. + */ + public boolean dispatchGenericMotionEvent(MotionEvent ev) { + if (mWindow.superDispatchGenericMotionEvent(ev)) { + return true; + } + return onGenericMotionEvent(ev); + } + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { event.setClassName(getClass().getName()); event.setPackageName(mContext.getPackageName()); diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index 297d24604d00..178567fdd881 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -24,13 +24,12 @@ import android.database.Cursor; import android.database.CursorWrapper; import android.net.ConnectivityManager; import android.net.Uri; -import android.os.Binder; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.provider.Downloads; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; -import android.util.Log; +import android.text.TextUtils; import android.util.Pair; import java.io.File; @@ -53,7 +52,6 @@ import java.util.List; * download in a notification or from the downloads UI. */ public class DownloadManager { - private static final String TAG = "DownloadManager"; /** * An identifier for a particular download, unique across the system. Clients use this ID to @@ -268,6 +266,13 @@ public class DownloadManager { public final static String ACTION_VIEW_DOWNLOADS = "android.intent.action.VIEW_DOWNLOADS"; /** + * Intent extra included with {@link #ACTION_VIEW_DOWNLOADS} to start DownloadApp in + * sort-by-size mode. + */ + public final static String INTENT_EXTRAS_SORT_BY_SIZE = + "android.app.DownloadManager.extra_sortBySize"; + + /** * Intent extra included with {@link #ACTION_DOWNLOAD_COMPLETE} intents, indicating the ID (as a * long) of the download that just completed. */ @@ -368,8 +373,17 @@ public class DownloadManager { */ public static final int VISIBILITY_HIDDEN = 2; + /** + * This download shows in the notifications after completion ONLY. + * It is usuable only with + * {@link DownloadManager#completedDownload(String, String, boolean, String, + * String, long, boolean)}. + */ + public static final int VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION = 3; + /** can take any of the following values: {@link #VISIBILITY_HIDDEN} - * {@link #VISIBILITY_VISIBLE_NOTIFY_COMPLETED}, {@link #VISIBILITY_VISIBLE} + * {@link #VISIBILITY_VISIBLE_NOTIFY_COMPLETED}, {@link #VISIBILITY_VISIBLE}, + * {@link #VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION} */ private int mNotificationVisibility = VISIBILITY_VISIBLE; @@ -387,6 +401,10 @@ public class DownloadManager { mUri = uri; } + Request(String uriString) { + mUri = Uri.parse(uriString); + } + /** * Set the local destination for the downloaded file. Must be a file URI to a path on * external storage, and the calling application must have the WRITE_EXTERNAL_STORAGE @@ -1070,6 +1088,72 @@ public class DownloadManager { return null; } } + + /** + * Adds a file to the downloads database system, so it could appear in Downloads App + * (and thus become eligible for management by the Downloads App). + * <p> + * It is helpful to make the file scannable by MediaScanner by setting the param + * isMediaScannerScannable to true. It makes the file visible in media managing + * applications such as Gallery App, which could be a useful purpose of using this API. + * + * @param title the title that would appear for this file in Downloads App. + * @param description the description that would appear for this file in Downloads App. + * @param isMediaScannerScannable true if the file is to be scanned by MediaScanner. Files + * scanned by MediaScanner appear in the applications used to view media (for example, + * Gallery app). + * @param mimeType mimetype of the file. + * @param path absolute pathname to the file. The file should be world-readable, so that it can + * be managed by the Downloads App and any other app that is used to read it (for example, + * Gallery app to display the file, if the file contents represent a video/image). + * @param length length of the downloaded file + * @param showNotification true if a notification is to be sent, false otherwise + * @return an ID for the download entry added to the downloads app, unique across the system + * This ID is used to make future calls related to this download. + */ + public long completedDownload(String title, String description, + boolean isMediaScannerScannable, String mimeType, String path, long length, + boolean showNotification) { + // make sure the input args are non-null/non-zero + validateArgumentIsNonEmpty("title", title); + validateArgumentIsNonEmpty("description", description); + validateArgumentIsNonEmpty("path", path); + validateArgumentIsNonEmpty("mimeType", mimeType); + if (length <= 0) { + throw new IllegalArgumentException(" invalid value for param: totalBytes"); + } + + // if there is already an entry with the given path name in downloads.db, return its id + Request request = new Request(NON_DOWNLOADMANAGER_DOWNLOAD) + .setTitle(title) + .setDescription(description) + .setMimeType(mimeType); + ContentValues values = request.toContentValues(null); + values.put(Downloads.Impl.COLUMN_DESTINATION, + Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD); + values.put(Downloads.Impl._DATA, path); + values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_SUCCESS); + values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, length); + values.put(Downloads.Impl.COLUMN_MEDIA_SCANNED, + (isMediaScannerScannable) ? Request.SCANNABLE_VALUE_YES : + Request.SCANNABLE_VALUE_NO); + values.put(Downloads.Impl.COLUMN_VISIBILITY, (showNotification) ? + Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION : Request.VISIBILITY_HIDDEN); + Uri downloadUri = mResolver.insert(Downloads.Impl.CONTENT_URI, values); + if (downloadUri == null) { + return -1; + } + return Long.parseLong(downloadUri.getLastPathSegment()); + } + private static final String NON_DOWNLOADMANAGER_DOWNLOAD = + "non-dwnldmngr-download-dont-retry2download"; + + private static void validateArgumentIsNonEmpty(String paramName, String val) { + if (TextUtils.isEmpty(val)) { + throw new IllegalArgumentException(paramName + " can't be null"); + } + } + /** * Get the DownloadProvider URI for the download with the given ID. */ @@ -1144,7 +1228,8 @@ public class DownloadManager { private String getLocalUri() { long destinationType = getLong(getColumnIndex(Downloads.Impl.COLUMN_DESTINATION)); if (destinationType == Downloads.Impl.DESTINATION_FILE_URI || - destinationType == Downloads.Impl.DESTINATION_EXTERNAL) { + destinationType == Downloads.Impl.DESTINATION_EXTERNAL || + destinationType == Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) { String localPath = getString(getColumnIndex(COLUMN_LOCAL_FILENAME)); if (localPath == null) { return null; diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java index 3f045ac288f9..df64035b4be9 100644 --- a/core/java/android/app/FragmentBreadCrumbs.java +++ b/core/java/android/app/FragmentBreadCrumbs.java @@ -50,6 +50,26 @@ public class FragmentBreadCrumbs extends ViewGroup /** Listener to inform when a parent entry is clicked */ private OnClickListener mParentClickListener; + private OnBreadCrumbClickListener mOnBreadCrumbClickListener; + + /** + * Interface to intercept clicks on the bread crumbs. + */ + public interface OnBreadCrumbClickListener { + /** + * Called when a bread crumb is clicked. + * + * @param backStack The BackStackEntry whose bread crumb was clicked. + * May be null, if this bread crumb is for the root of the back stack. + * @param flags Additional information about the entry. Currently + * always 0. + * + * @return Return true to consume this click. Return to false to allow + * the default action (popping back stack to this entry) to occur. + */ + public boolean onBreadCrumbClick(BackStackEntry backStack, int flags); + } + public FragmentBreadCrumbs(Context context) { this(context, null); } @@ -107,6 +127,16 @@ public class FragmentBreadCrumbs extends ViewGroup updateCrumbs(); } + /** + * Sets a listener for clicks on the bread crumbs. This will be called before + * the default click action is performed. + * + * @param listener The new listener to set. Replaces any existing listener. + */ + public void setOnBreadCrumbClickListener(OnBreadCrumbClickListener listener) { + mOnBreadCrumbClickListener = listener; + } + private BackStackRecord createBackStackEntry(CharSequence title, CharSequence shortTitle) { if (title == null) return null; @@ -266,8 +296,18 @@ public class FragmentBreadCrumbs extends ViewGroup mParentClickListener.onClick(v); } } else { - mActivity.getFragmentManager().popBackStack(bse.getId(), - bse == mTopEntry? FragmentManager.POP_BACK_STACK_INCLUSIVE : 0); + if (mOnBreadCrumbClickListener != null) { + if (mOnBreadCrumbClickListener.onBreadCrumbClick( + bse == mTopEntry ? null : bse, 0)) { + return; + } + } + if (bse == mTopEntry) { + // Pop everything off the back stack. + mActivity.getFragmentManager().popBackStack(); + } else { + mActivity.getFragmentManager().popBackStack(bse.getId(), 0); + } } } } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 5d4380b3c18c..f42e8fb73e86 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -133,6 +133,7 @@ public interface IActivityManager extends IInterface { IThumbnailReceiver receiver) throws RemoteException; public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags) throws RemoteException; + public Bitmap getTaskThumbnail(int taskId) throws RemoteException; public List getServices(int maxNum, int flags) throws RemoteException; public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() throws RemoteException; @@ -514,7 +515,7 @@ public interface IActivityManager extends IInterface { int FORCE_STOP_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+78; int KILL_PIDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79; int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80; - + int GET_TASK_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81; int GET_RUNNING_APP_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+82; int GET_DEVICE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+83; int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84; diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index eccd7c9f698b..29f8caf704f2 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -148,7 +148,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { /** * Action periodically sent to a device administrator when the device password - * is expiring. + * is expiring. * * <p>The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_EXPIRE_PASSWORD} to receive @@ -266,8 +266,8 @@ public class DeviceAdminReceiver extends BroadcastReceiver { /** * Called periodically when the password is about to expire or has expired. It will typically - * be called on device boot, once per day before the password expires and at the time when it - * expires. + * be called at these times: on device boot, once per day before the password expires, + * and at the time when the password expires. * * <p>If the password is not updated by the user, this method will continue to be called * once per day until the password is changed or the device admin disables password expiration. diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index d71a7a3519f6..440cb5406b85 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -730,8 +730,10 @@ public class DevicePolicyManager { } /** - * Get the current password expiration timeout for the given admin or the aggregate - * of all admins if admin is null. + * Get the password expiration timeout for the given admin. The expiration timeout is the + * recurring expiration timeout provided in the call to + * {@link #setPasswordExpirationTimeout(ComponentName, long)} for the given admin or the + * aggregate of all policy administrators if admin is null. * * @param admin The name of the admin component to check, or null to aggregate all admins. * @return The timeout for the given admin or the minimum of all timeouts @@ -749,7 +751,9 @@ public class DevicePolicyManager { /** * Get the current password expiration time for the given admin or an aggregate of - * all admins if admin is null. + * all admins if admin is null. If the password is expired, this will return the time since + * the password expired as a negative number. If admin is null, then a composite of all + * expiration timeouts is returned - which will be the minimum of all timeouts. * * @param admin The name of the admin component to check, or null to aggregate all admins. * @return The password expiration time, in ms. diff --git a/core/java/android/app/backup/WallpaperBackupHelper.java b/core/java/android/app/backup/WallpaperBackupHelper.java index 98082006caef..d70b3d37ee8b 100644 --- a/core/java/android/app/backup/WallpaperBackupHelper.java +++ b/core/java/android/app/backup/WallpaperBackupHelper.java @@ -32,7 +32,7 @@ import java.io.File; */ public class WallpaperBackupHelper extends FileBackupHelperBase implements BackupHelper { private static final String TAG = "WallpaperBackupHelper"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; // This path must match what the WallpaperManagerService uses private static final String WALLPAPER_IMAGE = "/data/data/com.android.settings/files/wallpaper"; @@ -64,6 +64,10 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu wpm = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE); mDesiredMinWidth = (double) wpm.getDesiredMinimumWidth(); mDesiredMinHeight = (double) wpm.getDesiredMinimumHeight(); + + if (DEBUG) { + Slog.d(TAG, "dmW=" + mDesiredMinWidth + " dmH=" + mDesiredMinHeight); + } } /** @@ -94,7 +98,7 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu options.inJustDecodeBounds = true; BitmapFactory.decodeFile(STAGE_FILE, options); - if (DEBUG) Slog.v(TAG, "Restoring wallpaper image w=" + options.outWidth + if (DEBUG) Slog.d(TAG, "Restoring wallpaper image w=" + options.outWidth + " h=" + options.outHeight); // how much does the image differ from our preference? @@ -103,13 +107,13 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu if (widthRatio > 0.8 && widthRatio < 1.25 && heightRatio > 0.8 && heightRatio < 1.25) { // sufficiently close to our resolution; go ahead and use it - if (DEBUG) Slog.v(TAG, "wallpaper dimension match; using"); + if (DEBUG) Slog.d(TAG, "wallpaper dimension match; using"); f.renameTo(new File(WALLPAPER_IMAGE)); // TODO: spin a service to copy the restored image to sd/usb storage, // since it does not exist anywhere other than the private wallpaper // file. } else { - if (DEBUG) Slog.v(TAG, "dimensions too far off: wr=" + widthRatio + if (DEBUG) Slog.d(TAG, "dimensions too far off: wr=" + widthRatio + " hr=" + heightRatio); f.delete(); } diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java index 7b083f10e2bd..aa1adcbbc2a8 100644 --- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -18,7 +18,7 @@ package android.bluetooth; import android.content.Context; import android.net.ConnectivityManager; -import android.net.DhcpInfo; +import android.net.DhcpInfoInternal; import android.net.LinkAddress; import android.net.LinkCapabilities; import android.net.LinkProperties; @@ -251,23 +251,12 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { public void run() { //TODO(): Add callbacks for failure and success case. //Currently this thread runs independently. - DhcpInfo dhcpInfo = new DhcpInfo(); - if (!NetworkUtils.runDhcp(mIface, dhcpInfo)) { + DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal(); + if (!NetworkUtils.runDhcp(mIface, dhcpInfoInternal)) { Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); return; } - mLinkProperties.addLinkAddress(new LinkAddress( - NetworkUtils.intToInetAddress(dhcpInfo.ipAddress), - NetworkUtils.intToInetAddress(dhcpInfo.netmask))); - mLinkProperties.setGateway(NetworkUtils.intToInetAddress(dhcpInfo.gateway)); - InetAddress dns1Addr = NetworkUtils.intToInetAddress(dhcpInfo.dns1); - if (dns1Addr == null || dns1Addr.equals("0.0.0.0")) { - mLinkProperties.addDns(dns1Addr); - } - InetAddress dns2Addr = NetworkUtils.intToInetAddress(dhcpInfo.dns2); - if (dns2Addr == null || dns2Addr.equals("0.0.0.0")) { - mLinkProperties.addDns(dns2Addr); - } + mLinkProperties = dhcpInfoInternal.makeLinkProperties(); mLinkProperties.setInterfaceName(mIface); mNetworkInfo.setIsAvailable(true); diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java index 90d647234187..fcc19a24a4b0 100644 --- a/core/java/android/content/AbstractThreadedSyncAdapter.java +++ b/core/java/android/content/AbstractThreadedSyncAdapter.java @@ -191,13 +191,12 @@ public abstract class AbstractThreadedSyncAdapter { public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - if (isCanceled()) { - return; - } - SyncResult syncResult = new SyncResult(); ContentProviderClient provider = null; try { + if (isCanceled()) { + return; + } provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority); if (provider != null) { AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras, diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index d14cf4d56fc1..051ae9e6a827 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1663,6 +1663,16 @@ public abstract class Context { public static final String SIP_SERVICE = "sip"; /** + * Use with {@link #getSystemService} to retrieve a {@link + * android.hardware.UsbManager} for access to USB devices (as a USB host) + * and for controlling this device's behavior as a USB device. + * + * @see #getSystemService + * @see android.harware.UsbManager + */ + public static final String USB_SERVICE = "usb"; + + /** * 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/Intent.java b/core/java/android/content/Intent.java index 4706c1a56ef4..ce7f0961e899 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2796,7 +2796,7 @@ public class Intent implements Parcelable, Cloneable { * @param action The Intent action, such as ACTION_VIEW. */ public Intent(String action) { - mAction = action; + setAction(action); } /** @@ -2816,7 +2816,7 @@ public class Intent implements Parcelable, Cloneable { * @param uri The Intent data URI. */ public Intent(String action, Uri uri) { - mAction = action; + setAction(action); mData = uri; } @@ -2865,7 +2865,7 @@ public class Intent implements Parcelable, Cloneable { */ public Intent(String action, Uri uri, Context packageContext, Class<?> cls) { - mAction = action; + setAction(action); mData = uri; mComponent = new ComponentName(packageContext, cls); } @@ -2987,7 +2987,7 @@ public class Intent implements Parcelable, Cloneable { // action if (uri.startsWith("action=", i)) { - intent.mAction = value; + intent.setAction(value); } // categories @@ -4063,7 +4063,7 @@ public class Intent implements Parcelable, Cloneable { * @see #getAction */ public Intent setAction(String action) { - mAction = action; + mAction = action != null ? action.intern() : null; return this; } @@ -4167,7 +4167,7 @@ public class Intent implements Parcelable, Cloneable { if (mCategories == null) { mCategories = new HashSet<String>(); } - mCategories.add(category); + mCategories.add(category.intern()); return this; } @@ -5681,7 +5681,7 @@ public class Intent implements Parcelable, Cloneable { } public void readFromParcel(Parcel in) { - mAction = in.readString(); + setAction(in.readString()); mData = Uri.CREATOR.createFromParcel(in); mType = in.readString(); mFlags = in.readInt(); @@ -5697,7 +5697,7 @@ public class Intent implements Parcelable, Cloneable { mCategories = new HashSet<String>(); int i; for (i=0; i<N; i++) { - mCategories.add(in.readString()); + mCategories.add(in.readString().intern()); } } else { mCategories = null; diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 452fd8ae8d6a..61d74248c6be 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -461,7 +461,7 @@ public class IntentFilter implements Parcelable { * @return True if the action is explicitly mentioned in the filter. */ public final boolean hasAction(String action) { - return mActions.contains(action); + return action != null && mActions.contains(action); } /** @@ -470,14 +470,10 @@ public class IntentFilter implements Parcelable { * * @param action The desired action to look for. * - * @return True if the action is listed in the filter or the filter does - * not specify any actions. + * @return True if the action is listed in the filter. */ public final boolean matchAction(String action) { - if (action == null || mActions == null || mActions.size() == 0) { - return false; - } - return mActions.contains(action); + return hasAction(action); } /** @@ -818,9 +814,9 @@ public class IntentFilter implements Parcelable { if (mDataPaths == null) { return false; } - Iterator<PatternMatcher> i = mDataPaths.iterator(); - while (i.hasNext()) { - final PatternMatcher pe = i.next(); + final int numDataPaths = mDataPaths.size(); + for (int i = 0; i < numDataPaths; i++) { + final PatternMatcher pe = mDataPaths.get(i); if (pe.match(data)) { return true; } @@ -849,9 +845,9 @@ public class IntentFilter implements Parcelable { if (mDataAuthorities == null) { return NO_MATCH_DATA; } - Iterator<AuthorityEntry> i = mDataAuthorities.iterator(); - while (i.hasNext()) { - final AuthorityEntry ae = i.next(); + final int numDataAuthorities = mDataAuthorities.size(); + for (int i = 0; i < numDataAuthorities; i++) { + final AuthorityEntry ae = mDataAuthorities.get(i); int match = ae.match(data); if (match >= 0) { return match; @@ -1098,7 +1094,7 @@ public class IntentFilter implements Parcelable { */ public final int match(String action, String type, String scheme, Uri data, Set<String> categories, String logTag) { - if (action != null && !matchAction(action)) { + if (!matchAction(action)) { if (Config.LOGV) Log.v( logTag, "No matching action " + action + " for " + this); return NO_MATCH_ACTION; @@ -1119,11 +1115,11 @@ public class IntentFilter implements Parcelable { return dataMatch; } - String categoryMatch = matchCategories(categories); - if (categoryMatch != null) { - if (Config.LOGV) Log.v( - logTag, "No matching category " - + categoryMatch + " for " + this); + String categoryMismatch = matchCategories(categories); + if (categoryMismatch != null) { + if (Config.LOGV) { + Log.v(logTag, "No matching category " + categoryMismatch + " for " + this); + } return NO_MATCH_CATEGORY; } @@ -1469,9 +1465,9 @@ public class IntentFilter implements Parcelable { if (typeLength == slashpos+2 && type.charAt(slashpos+1) == '*') { // Need to look through all types for one that matches // our base... - final Iterator<String> it = t.iterator(); - while (it.hasNext()) { - String v = it.next(); + final int numTypes = t.size(); + for (int i = 0; i < numTypes; i++) { + final String v = t.get(i); if (type.regionMatches(0, v, 0, slashpos+1)) { return true; } diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 68cb2bcca9ca..2cb8a86f0dbf 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -237,10 +237,28 @@ public class SyncManager implements OnAccountsUpdateListener { private BroadcastReceiver mConnectivityIntentReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { - sendCheckAlarmsMessage(); + final boolean wasConnected = mDataConnectionIsConnected; + + // don't use the intent to figure out if network is connected, just check + // ConnectivityManager directly. + mDataConnectionIsConnected = readDataConnectionState(); + if (mDataConnectionIsConnected) { + if (!wasConnected) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Reconnection detected: clearing all backoffs"); + } + mSyncStorageEngine.clearAllBackoffs(mSyncQueue); + } + sendCheckAlarmsMessage(); + } } }; + private boolean readDataConnectionState() { + NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo(); + return (networkInfo != null) && networkInfo.isConnected(); + } + private BroadcastReceiver mShutdownIntentReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { @@ -1406,12 +1424,12 @@ public class SyncManager implements OnAccountsUpdateListener { public void handleMessage(Message msg) { long earliestFuturePollTime = Long.MAX_VALUE; long nextPendingSyncTime = Long.MAX_VALUE; + // Setting the value here instead of a method because we want the dumpsys logs // to have the most recent value used. try { waitUntilReadyToRun(); - NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo(); - mDataConnectionIsConnected = (networkInfo != null) && networkInfo.isConnected(); + mDataConnectionIsConnected = readDataConnectionState(); mSyncManagerWakeLock.acquire(); // Always do this first so that we be sure that any periodic syncs that // are ready to run have been converted into pending syncs. This allows the diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index c8ca61898319..a1e174b119b2 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -525,7 +525,7 @@ public class SyncStorageEngine extends Handler { } } - public void clearAllBackoffs() { + public void clearAllBackoffs(SyncQueue syncQueue) { boolean changed = false; synchronized (mAuthorities) { for (AccountInfo accountInfo : mAccounts.values()) { @@ -541,6 +541,7 @@ public class SyncStorageEngine extends Handler { } authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE; authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE; + syncQueue.onBackoffChanged(accountInfo.account, authorityInfo.authority, 0); changed = true; } } diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java index bfaeb828c9f8..3ffc714826f4 100644 --- a/core/java/android/database/AbstractCursor.java +++ b/core/java/android/database/AbstractCursor.java @@ -133,6 +133,8 @@ public abstract class AbstractCursor implements CrossProcessCursor { result.getChars(0, result.length(), data, 0); } buffer.sizeCopied = result.length(); + } else { + buffer.sizeCopied = 0; } } diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java index feea47e32e65..bdb96b17f86f 100644 --- a/core/java/android/database/sqlite/SQLiteCompiledSql.java +++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java @@ -103,6 +103,11 @@ import android.util.Log; protected void finalize() throws Throwable { try { if (nStatement == 0) return; + // don't worry about finalizing this object if it is ALREADY in the + // queue of statements to be finalized later + if (mDatabase.isInQueueOfStatementsToBeFinalized(nStatement)) { + return; + } // finalizer should NEVER get called // but if the database itself is not closed and is GC'ed, then // all sub-objects attached to the database could end up getting GC'ed too. diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index b3fd9147f23c..2e43eef3c770 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -33,21 +33,20 @@ import android.text.TextUtils; import android.util.Config; import android.util.EventLog; import android.util.Log; +import android.util.LruCache; import android.util.Pair; - import dalvik.system.BlockGuard; - import java.io.File; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.List; import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedHashMap; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.WeakHashMap; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Pattern; @@ -198,7 +197,7 @@ public class SQLiteDatabase extends SQLiteClosable { private boolean mTransactionUsingExecSql; /** Synchronize on this when accessing the database */ - private final ReentrantLock mLock = new ReentrantLock(true); + private final DatabaseReentrantLock mLock = new DatabaseReentrantLock(true); private long mLockAcquiredWallTime = 0L; private long mLockAcquiredThreadTime = 0L; @@ -261,6 +260,9 @@ public class SQLiteDatabase extends SQLiteClosable { private final WeakHashMap<SQLiteClosable, Object> mPrograms; + /** Default statement-cache size per database connection ( = instance of this class) */ + private static final int DEFAULT_SQL_CACHE_SIZE = 25; + /** * for each instance of this class, a LRU cache is maintained to store * the compiled query statement ids returned by sqlite database. @@ -273,34 +275,12 @@ public class SQLiteDatabase extends SQLiteClosable { * struct created when {@link SQLiteDatabase#openDatabase(String, CursorFactory, int)} is * invoked. * - * this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method - * (@link #setMaxSqlCacheSize(int)}). - */ - // default statement-cache size per database connection ( = instance of this class) - private int mMaxSqlCacheSize = 25; - // guarded by itself - /* package */ final Map<String, SQLiteCompiledSql> mCompiledQueries = - new LinkedHashMap<String, SQLiteCompiledSql>(mMaxSqlCacheSize + 1, 0.75f, true) { - @Override - public boolean removeEldestEntry(Map.Entry<String, SQLiteCompiledSql> eldest) { - // eldest = least-recently used entry - // if it needs to be removed to accommodate a new entry, - // close {@link SQLiteCompiledSql} represented by this entry, if not in use - // and then let it be removed from the Map. - // when this is called, the caller must be trying to add a just-compiled stmt - // to cache; i.e., caller should already have acquired database lock AND - // the lock on mCompiledQueries. do as assert of these two 2 facts. - verifyLockOwner(); - if (this.size() <= mMaxSqlCacheSize) { - // cache is not full. nothing needs to be removed - return false; - } - // cache is full. eldest will be removed. - eldest.getValue().releaseIfNotInUse(); - // return true, so that this entry is removed automatically by the caller. - return true; - } - }; + * this cache's max size is settable by calling the method + * (@link #setMaxSqlCacheSize(int)}. + */ + // guarded by this + private LruCache<String, SQLiteCompiledSql> mCompiledQueries; + /** * absolute max value that can be set by {@link #setMaxSqlCacheSize(int)} * size of each prepared-statement is between 1K - 6K, depending on the complexity of the @@ -309,11 +289,6 @@ public class SQLiteDatabase extends SQLiteClosable { public static final int MAX_SQL_CACHE_SIZE = 100; private boolean mCacheFullWarning; - /** Number of cache hits on this database connection. guarded by {@link #mCompiledQueries}. */ - private int mNumCacheHits; - /** Number of cache misses on this database connection. guarded by {@link #mCompiledQueries}. */ - private int mNumCacheMisses; - /** Used to find out where this object was created in case it never got closed. */ private final Throwable mStackTrace; @@ -414,6 +389,7 @@ public class SQLiteDatabase extends SQLiteClosable { /* package */ void lock() { lock(false); } + private static final long LOCK_WAIT_PERIOD = 30L; private void lock(boolean forced) { // make sure this method is NOT being called from a 'synchronized' method if (Thread.holdsLock(this)) { @@ -421,7 +397,22 @@ public class SQLiteDatabase extends SQLiteClosable { } verifyDbIsOpen(); if (!forced && !mLockingEnabled) return; - mLock.lock(); + boolean done = false; + while (!done) { + try { + // wait for 30sec to acquire the lock + done = mLock.tryLock(LOCK_WAIT_PERIOD, TimeUnit.SECONDS); + if (!done) { + // lock not acquired in NSec. print a message and stacktrace saying the lock + // has not been available for 30sec. + Log.w(TAG, "database lock has not been available for " + LOCK_WAIT_PERIOD + + " sec. Current Owner of the lock is " + mLock.getOwnerDescription() + + ". Continuing to wait in thread: " + Thread.currentThread().getId()); + } + } catch (InterruptedException e) { + // ignore the interruption + } + } if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) { if (mLock.getHoldCount() == 1) { // Use elapsed real-time since the CPU may sleep when waiting for IO @@ -430,6 +421,20 @@ public class SQLiteDatabase extends SQLiteClosable { } } } + private static class DatabaseReentrantLock extends ReentrantLock { + DatabaseReentrantLock(boolean fair) { + super(fair); + } + @Override + public Thread getOwner() { + return super.getOwner(); + } + public String getOwnerDescription() { + Thread t = getOwner(); + return (t== null) ? "none" : String.valueOf(t.getId()); + } + } + /** * Locks the database for exclusive access. The database lock must be held when * touch the native sqlite3* object since it is single threaded and uses @@ -1968,6 +1973,7 @@ public class SQLiteDatabase extends SQLiteClosable { if (path == null) { throw new IllegalArgumentException("path should not be null"); } + setMaxSqlCacheSize(DEFAULT_SQL_CACHE_SIZE); mFlags = flags; mPath = path; mSlowQueryThreshold = SystemProperties.getInt(LOG_SLOW_QUERIES_PROPERTY, -1); @@ -1980,7 +1986,7 @@ public class SQLiteDatabase extends SQLiteClosable { mConnectionNum = connectionNum; /* sqlite soft heap limit http://www.sqlite.org/c3ref/soft_heap_limit64.html * set it to 4 times the default cursor window size. - * TODO what is an appropriate value, considring the WAL feature which could burn + * TODO what is an appropriate value, considering the WAL feature which could burn * a lot of memory with many connections to the database. needs testing to figure out * optimal value for this. */ @@ -2134,68 +2140,56 @@ public class SQLiteDatabase extends SQLiteClosable { * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current * mapping is NOT replaced with the new mapping). */ - /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) { - synchronized(mCompiledQueries) { - // don't insert the new mapping if a mapping already exists - if (mCompiledQueries.containsKey(sql)) { - return; - } + /* package */ synchronized void addToCompiledQueries( + String sql, SQLiteCompiledSql compiledStatement) { + // don't insert the new mapping if a mapping already exists + if (mCompiledQueries.get(sql) != null) { + return; + } - int maxCacheSz = (mConnectionNum == 0) ? mMaxSqlCacheSize : - mParentConnObj.mMaxSqlCacheSize; - - if (SQLiteDebug.DEBUG_SQL_CACHE) { - boolean printWarning = (mConnectionNum == 0) - ? (!mCacheFullWarning && mCompiledQueries.size() == maxCacheSz) - : (!mParentConnObj.mCacheFullWarning && - mParentConnObj.mCompiledQueries.size() == maxCacheSz); - if (printWarning) { - /* - * cache size of {@link #mMaxSqlCacheSize} is not enough for this app. - * log a warning. - * chances are it is NOT using ? for bindargs - or cachesize is too small. - */ - Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " + - getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. "); - mCacheFullWarning = true; - Log.d(TAG, "Here are the SQL statements in Cache of database: " + mPath); - for (String s : mCompiledQueries.keySet()) { - Log.d(TAG, "Sql stament in Cache: " + s); - } + int maxCacheSz = (mConnectionNum == 0) ? mCompiledQueries.maxSize() : + mParentConnObj.mCompiledQueries.maxSize(); + + if (SQLiteDebug.DEBUG_SQL_CACHE) { + boolean printWarning = (mConnectionNum == 0) + ? (!mCacheFullWarning && mCompiledQueries.size() == maxCacheSz) + : (!mParentConnObj.mCacheFullWarning && + mParentConnObj.mCompiledQueries.size() == maxCacheSz); + if (printWarning) { + /* + * cache size is not enough for this app. log a warning. + * chances are it is NOT using ? for bindargs - or cachesize is too small. + */ + Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " + + getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. "); + mCacheFullWarning = true; + Log.d(TAG, "Here are the SQL statements in Cache of database: " + mPath); + for (String s : mCompiledQueries.snapshot().keySet()) { + Log.d(TAG, "Sql statement in Cache: " + s); } } - /* add the given SQLiteCompiledSql compiledStatement to cache. - * no need to worry about the cache size - because {@link #mCompiledQueries} - * self-limits its size to {@link #mMaxSqlCacheSize}. - */ - mCompiledQueries.put(sql, compiledStatement); } + /* add the given SQLiteCompiledSql compiledStatement to cache. + * no need to worry about the cache size - because {@link #mCompiledQueries} + * self-limits its size. + */ + mCompiledQueries.put(sql, compiledStatement); } /** package-level access for testing purposes */ - /* package */ void deallocCachedSqlStatements() { - synchronized (mCompiledQueries) { - for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) { - compiledSql.releaseSqlStatement(); - } - mCompiledQueries.clear(); + /* package */ synchronized void deallocCachedSqlStatements() { + for (SQLiteCompiledSql compiledSql : mCompiledQueries.snapshot().values()) { + compiledSql.releaseSqlStatement(); } + mCompiledQueries.evictAll(); } /** * From the compiledQueries cache, returns the compiled-statement-id for the given SQL. * Returns null, if not found in the cache. */ - /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) { - synchronized (mCompiledQueries) { - SQLiteCompiledSql compiledStatement = mCompiledQueries.get(sql); - if (compiledStatement == null) { - mNumCacheMisses++; - return null; - } - mNumCacheHits++; - return compiledStatement; - } + /* package */ synchronized SQLiteCompiledSql getCompiledStatementForSql(String sql) { + return mCompiledQueries.get(sql); } /** @@ -2213,51 +2207,56 @@ public class SQLiteDatabase extends SQLiteClosable { * the value set with previous setMaxSqlCacheSize() call. */ public void setMaxSqlCacheSize(int cacheSize) { - synchronized(mCompiledQueries) { + synchronized (this) { + LruCache<String, SQLiteCompiledSql> oldCompiledQueries = mCompiledQueries; if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) { - throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE); - } else if (cacheSize < mMaxSqlCacheSize) { - throw new IllegalStateException("cannot set cacheSize to a value less than the value " + - "set with previous setMaxSqlCacheSize() call."); + throw new IllegalStateException( + "expected value between 0 and " + MAX_SQL_CACHE_SIZE); + } else if (oldCompiledQueries != null && cacheSize < oldCompiledQueries.maxSize()) { + throw new IllegalStateException("cannot set cacheSize to a value less than the " + + "value set with previous setMaxSqlCacheSize() call."); + } + mCompiledQueries = new LruCache<String, SQLiteCompiledSql>(cacheSize) { + @Override + protected void entryEvicted(String key, SQLiteCompiledSql value) { + verifyLockOwner(); + value.releaseIfNotInUse(); + } + }; + if (oldCompiledQueries != null) { + for (Map.Entry<String, SQLiteCompiledSql> entry + : oldCompiledQueries.snapshot().entrySet()) { + mCompiledQueries.put(entry.getKey(), entry.getValue()); + } } - mMaxSqlCacheSize = cacheSize; } } - /* package */ boolean isInStatementCache(String sql) { - synchronized (mCompiledQueries) { - return mCompiledQueries.containsKey(sql); - } + /* package */ synchronized boolean isInStatementCache(String sql) { + return mCompiledQueries.get(sql) != null; } - /* package */ void releaseCompiledSqlObj(SQLiteCompiledSql compiledSql) { - synchronized (mCompiledQueries) { - if (mCompiledQueries.containsValue(compiledSql)) { - // it is in cache - reset its inUse flag - compiledSql.release(); - } else { - // it is NOT in cache. finalize it. - compiledSql.releaseSqlStatement(); - } + /* package */ synchronized void releaseCompiledSqlObj( + String sql, SQLiteCompiledSql compiledSql) { + if (mCompiledQueries.get(sql) == compiledSql) { + // it is in cache - reset its inUse flag + compiledSql.release(); + } else { + // it is NOT in cache. finalize it. + compiledSql.releaseSqlStatement(); } } - private int getCacheHitNum() { - synchronized(mCompiledQueries) { - return mNumCacheHits; - } + private synchronized int getCacheHitNum() { + return mCompiledQueries.hitCount(); } - private int getCacheMissNum() { - synchronized(mCompiledQueries) { - return mNumCacheMisses; - } + private synchronized int getCacheMissNum() { + return mCompiledQueries.missCount(); } - private int getCachesize() { - synchronized(mCompiledQueries) { - return mCompiledQueries.size(); - } + private synchronized int getCachesize() { + return mCompiledQueries.size(); } /* package */ void finalizeStatementLater(int id) { @@ -2274,6 +2273,17 @@ public class SQLiteDatabase extends SQLiteClosable { } } + /* package */ boolean isInQueueOfStatementsToBeFinalized(int id) { + if (!isOpen()) { + // database already closed. this statement will already have been finalized. + // return true so that the caller doesn't have to worry about finalizing this statement. + return true; + } + synchronized(mClosedStatementIds) { + return mClosedStatementIds.contains(id); + } + } + /* package */ void closePendingStatements() { if (!isOpen()) { // since this database is already closed, no need to finalize anything. diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java index 83621f2231e4..88246e8aee27 100644 --- a/core/java/android/database/sqlite/SQLiteProgram.java +++ b/core/java/android/database/sqlite/SQLiteProgram.java @@ -18,7 +18,6 @@ package android.database.sqlite; import android.database.DatabaseUtils; import android.database.Cursor; -import android.util.Log; import java.util.HashMap; @@ -184,7 +183,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { if (mCompiledSql == null) { return; } - mDatabase.releaseCompiledSqlObj(mCompiledSql); + mDatabase.releaseCompiledSqlObj(mSql, mCompiledSql); mCompiledSql = null; nStatement = 0; } diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 207785c87053..c2f3ae70b8b2 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -357,7 +357,7 @@ public class Camera { } } - private native final void setPreviewDisplay(Surface surface); + private native final void setPreviewDisplay(Surface surface) throws IOException; /** * Sets the {@link SurfaceTexture} to be used for live preview. @@ -380,7 +380,7 @@ public class Camera { * @throws IOException if the method fails (for example, if the surface * texture is unavailable or unsuitable). */ - public native final void setPreviewTexture(SurfaceTexture surfaceTexture); + public native final void setPreviewTexture(SurfaceTexture surfaceTexture) throws IOException; /** * Callback interface used to deliver copies of preview frames as diff --git a/drm/drmioserver/main_drmioserver.cpp b/core/java/android/hardware/IUsbManager.aidl index 7ed048d6738a..6c99ab36fb03 100644 --- a/drm/drmioserver/main_drmioserver.cpp +++ b/core/java/android/hardware/IUsbManager.aidl @@ -14,27 +14,18 @@ * limitations under the License. */ -#include <sys/types.h> -#include <unistd.h> -#include <grp.h> +package android.hardware; -#include <binder/IPCThreadState.h> -#include <binder/ProcessState.h> -#include <binder/IServiceManager.h> -#include <utils/Log.h> -#include <private/android_filesystem_config.h> +import android.hardware.UsbAccessory; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; -#include <DrmIOService.h> - -using namespace android; - -int main(int argc, char** argv) +/** @hide */ +interface IUsbManager { - sp<ProcessState> proc(ProcessState::self()); - sp<IServiceManager> sm = defaultServiceManager(); - LOGI("ServiceManager: %p", sm.get()); - DrmIOService::instantiate(); - ProcessState::self()->startThreadPool(); - IPCThreadState::self()->joinThreadPool(); + /* Returns a list of all currently attached USB devices */ + void getDeviceList(out Bundle devices); + ParcelFileDescriptor openDevice(String deviceName); + UsbAccessory getCurrentAccessory(); + ParcelFileDescriptor openAccessory(); } - diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index f079e42a9993..dd4096bf05e3 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -380,6 +380,58 @@ public class SensorManager /*-----------------------------------------------------------------------*/ + private class SensorEventPool { + private final int mPoolSize; + private final SensorEvent mPool[]; + private int mNumItemsInPool; + + private SensorEvent createSensorEvent() { + // maximal size for all legacy events is 3 + return new SensorEvent(3); + } + + SensorEventPool(int poolSize) { + mPoolSize = poolSize; + mNumItemsInPool = poolSize; + mPool = new SensorEvent[poolSize]; + } + + SensorEvent getFromPool() { + SensorEvent t = null; + synchronized (this) { + if (mNumItemsInPool > 0) { + // remove the "top" item from the pool + final int index = mPoolSize - mNumItemsInPool; + t = mPool[index]; + mPool[index] = null; + mNumItemsInPool--; + } + } + if (t == null) { + // the pool was empty or this item was removed from the pool for + // the first time. In any case, we need to create a new item. + t = createSensorEvent(); + } + return t; + } + + void returnToPool(SensorEvent t) { + synchronized (this) { + // is there space left in the pool? + if (mNumItemsInPool < mPoolSize) { + // if so, return the item to the pool + mNumItemsInPool++; + final int index = mPoolSize - mNumItemsInPool; + mPool[index] = t; + } + } + } + } + + private static SensorEventPool sPool; + + /*-----------------------------------------------------------------------*/ + static private class SensorThread { Thread mThread; @@ -485,10 +537,9 @@ public class SensorManager /*-----------------------------------------------------------------------*/ private class ListenerDelegate { - final SensorEventListener mSensorEventListener; + private final SensorEventListener mSensorEventListener; private final ArrayList<Sensor> mSensorList = new ArrayList<Sensor>(); private final Handler mHandler; - private SensorEvent mValuesPool; public SparseBooleanArray mSensors = new SparseBooleanArray(); public SparseBooleanArray mFirstEvent = new SparseBooleanArray(); public SparseIntArray mSensorAccuracies = new SparseIntArray(); @@ -527,40 +578,12 @@ public class SensorManager } mSensorEventListener.onSensorChanged(t); - returnToPool(t); + sPool.returnToPool(t); } }; addSensor(sensor); } - protected SensorEvent createSensorEvent() { - // maximal size for all legacy events is 3 - return new SensorEvent(3); - } - - protected SensorEvent getFromPool() { - SensorEvent t = null; - synchronized (this) { - // remove the array from the pool - t = mValuesPool; - mValuesPool = null; - } - if (t == null) { - // the pool was empty, we need a new one - t = createSensorEvent(); - } - return t; - } - - protected void returnToPool(SensorEvent t) { - synchronized (this) { - // put back the array into the pool - if (mValuesPool == null) { - mValuesPool = t; - } - } - } - Object getListener() { return mSensorEventListener; } @@ -582,7 +605,7 @@ public class SensorManager } void onSensorChangedLocked(Sensor sensor, float[] values, long[] timestamp, int accuracy) { - SensorEvent t = getFromPool(); + SensorEvent t = sPool.getFromPool(); final float[] v = t.values; v[0] = values[0]; v[1] = values[1]; @@ -644,6 +667,7 @@ public class SensorManager } } while (i>0); + sPool = new SensorEventPool( sFullSensorsList.size()*2 ); sSensorThread = new SensorThread(); } } @@ -1970,7 +1994,8 @@ public class SensorManager if (rotationVector.length == 4) { q0 = rotationVector[3]; } else { - q0 = (float)Math.sqrt(1 - q1*q1 - q2*q2 - q3*q3); + q0 = 1 - q1*q1 - q2*q2 - q3*q3; + q0 = (q0 > 0) ? (float)Math.sqrt(q0) : 0; } float sq_q1 = 2 * q1 * q1; diff --git a/core/java/android/hardware/UsbAccessory.aidl b/core/java/android/hardware/UsbAccessory.aidl new file mode 100644 index 000000000000..97a777bb0fd8 --- /dev/null +++ b/core/java/android/hardware/UsbAccessory.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +parcelable UsbAccessory; diff --git a/core/java/android/hardware/UsbAccessory.java b/core/java/android/hardware/UsbAccessory.java new file mode 100644 index 000000000000..71672fa5517c --- /dev/null +++ b/core/java/android/hardware/UsbAccessory.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +/** + * A class representing a USB accessory. + */ +public final class UsbAccessory implements Parcelable { + + private static final String TAG = "UsbAccessory"; + + private String mManufacturer; + private String mModel; + private String mType; + private String mVersion; + + private UsbAccessory() { + } + + /** + * UsbAccessory should only be instantiated by UsbService implementation + * @hide + */ + public UsbAccessory(String manufacturer, String model, String type, String version) { + mManufacturer = manufacturer; + mModel = model; + mType = type; + mVersion = version; + } + + /** + * UsbAccessory should only be instantiated by UsbService implementation + * @hide + */ + public UsbAccessory(String[] strings) { + mManufacturer = strings[0]; + mModel = strings[1]; + mType = strings[2]; + mVersion = strings[3]; + } + + /** + * Returns the manufacturer of the accessory. + * + * @return the accessory manufacturer + */ + public String getManufacturer() { + return mManufacturer; + } + + /** + * Returns the model name of the accessory. + * + * @return the accessory model + */ + public String getModel() { + return mModel; + } + + /** + * Returns the type of the accessory. + * + * @return the accessory type + */ + public String getType() { + return mType; + } + + /** + * Returns the version of the accessory. + * + * @return the accessory version + */ + public String getVersion() { + return mVersion; + } + + @Override + public String toString() { + return "UsbAccessory[mManufacturer=" + mManufacturer + + ", mModel=" + mModel + + ", mType=" + mType + + ", mVersion=" + mVersion + "]"; + } + + public static final Parcelable.Creator<UsbAccessory> CREATOR = + new Parcelable.Creator<UsbAccessory>() { + public UsbAccessory createFromParcel(Parcel in) { + String manufacturer = in.readString(); + String model = in.readString(); + String type = in.readString(); + String version = in.readString(); + return new UsbAccessory(manufacturer, model, type, version); + } + + public UsbAccessory[] newArray(int size) { + return new UsbAccessory[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeString(mManufacturer); + parcel.writeString(mModel); + parcel.writeString(mType); + parcel.writeString(mVersion); + } +} diff --git a/core/java/android/hardware/UsbConstants.java b/core/java/android/hardware/UsbConstants.java new file mode 100644 index 000000000000..4c8c4d4db9dc --- /dev/null +++ b/core/java/android/hardware/UsbConstants.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +/** + * Contains constants for the USB protocol. + * These constants correspond to definitions in linux/usb/ch9.h in the linux kernel. + */ +public final class UsbConstants { + + public static final int USB_ENDPOINT_DIR_MASK = 0x80; + public static final int USB_DIR_OUT = 0; + public static final int USB_DIR_IN = 0x80; + + public static final int USB_TYPE_MASK = (0x03 << 5); + public static final int USB_TYPE_STANDARD = (0x00 << 5); + public static final int USB_TYPE_CLASS = (0x01 << 5); + public static final int USB_TYPE_VENDOR = (0x02 << 5); + public static final int USB_TYPE_RESERVED = (0x03 << 5); + + public static final int USB_ENDPOINT_NUMBER_MASK = 0x0f; + + // flags for endpoint attributes + public static final int USB_ENDPOINT_XFERTYPE_MASK = 0x03; + public static final int USB_ENDPOINT_XFER_CONTROL = 0; + public static final int USB_ENDPOINT_XFER_ISOC = 1; + public static final int USB_ENDPOINT_XFER_BULK = 2; + public static final int USB_ENDPOINT_XFER_INT = 3; + + // USB classes + public static final int USB_CLASS_PER_INTERFACE = 0; + public static final int USB_CLASS_AUDIO = 1; + public static final int USB_CLASS_COMM = 2; + public static final int USB_CLASS_HID = 3; + public static final int USB_CLASS_PHYSICA = 5; + public static final int USB_CLASS_STILL_IMAGE = 6; + public static final int USB_CLASS_PRINTER = 7; + public static final int USB_CLASS_MASS_STORAGE = 8; + public static final int USB_CLASS_HUB = 9; + public static final int USB_CLASS_CDC_DATA = 0x0a; + public static final int USB_CLASS_CSCID = 0x0b; + public static final int USB_CLASS_CONTENT_SEC = 0x0d; + public static final int USB_CLASS_VIDEO = 0x0e; + public static final int USB_CLASS_WIRELESS_CONTROLLER = 0xe0; + public static final int USB_CLASS_MISC = 0xef; + public static final int USB_CLASS_APP_SPEC = 0xfe; + public static final int USB_CLASS_VENDOR_SPEC = 0xff; + + // USB subclasses + public static final int USB_INTERFACE_SUBCLASS_BOOT = 1; // for HID class + public static final int USB_SUBCLASS_VENDOR_SPEC = 0xff; +}
\ No newline at end of file diff --git a/core/java/android/hardware/UsbDevice.aidl b/core/java/android/hardware/UsbDevice.aidl new file mode 100644 index 000000000000..6dfd43fe1097 --- /dev/null +++ b/core/java/android/hardware/UsbDevice.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 android.hardware; + +parcelable UsbDevice; diff --git a/core/java/android/hardware/UsbDevice.java b/core/java/android/hardware/UsbDevice.java new file mode 100644 index 000000000000..ca7dae305845 --- /dev/null +++ b/core/java/android/hardware/UsbDevice.java @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.hardware; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.FileDescriptor; + + +/** + * A class representing a USB device. + */ +public final class UsbDevice implements Parcelable { + + private static final String TAG = "UsbDevice"; + + private String mName; + private int mVendorId; + private int mProductId; + private int mClass; + private int mSubclass; + private int mProtocol; + private Parcelable[] mInterfaces; + + // used by the JNI code + private int mNativeContext; + + private UsbDevice() { + } + + + /** + * UsbDevice should only be instantiated by UsbService implementation + * @hide + */ + public UsbDevice(String name, int vendorId, int productId, + int Class, int subClass, int protocol, Parcelable[] interfaces) { + mName = name; + mVendorId = vendorId; + mProductId = productId; + mClass = Class; + mSubclass = subClass; + mProtocol = protocol; + mInterfaces = interfaces; + } + + /** + * Returns the name of the device. + * In the standard implementation, this is the path of the device file + * for the device in the usbfs file system. + * + * @return the device name + */ + public String getDeviceName() { + return mName; + } + + /** + * Returns a unique integer ID for the device. + * This is a convenience for clients that want to use an integer to represent + * the device, rather than the device name. + * IDs are not persistent across USB disconnects. + * + * @return the device ID + */ + public int getDeviceId() { + return getDeviceId(mName); + } + + /** + * Returns a vendor ID for the device. + * + * @return the device vendor ID + */ + public int getVendorId() { + return mVendorId; + } + + /** + * Returns a product ID for the device. + * + * @return the device product ID + */ + public int getProductId() { + return mProductId; + } + + /** + * Returns the devices's class field. + * Some useful constants for USB device classes can be found in + * {@link android.hardware.UsbConstants} + * + * @return the devices's class + */ + public int getDeviceClass() { + return mClass; + } + + /** + * Returns the device's subclass field. + * + * @return the device's subclass + */ + public int getDeviceSubclass() { + return mSubclass; + } + + /** + * Returns the device's subclass field. + * + * @return the device's protocol + */ + public int getDeviceProtocol() { + return mProtocol; + } + + /** + * Returns the number of {@link android.hardware.UsbInterface}s this device contains. + * + * @return the number of interfaces + */ + public int getInterfaceCount() { + return mInterfaces.length; + } + + /** + * Returns the {@link android.hardware.UsbInterface} at the given index. + * + * @return the interface + */ + public UsbInterface getInterface(int index) { + return (UsbInterface)mInterfaces[index]; + } + + /* package */ boolean open(ParcelFileDescriptor pfd) { + return native_open(mName, pfd.getFileDescriptor()); + } + + /** + * Releases all system resources related to the device. + */ + public void close() { + native_close(); + } + + /** + * Returns an integer file descriptor for the device, or + * -1 if the device is not opened. + * This is intended for passing to native code to access the device + */ + public int getFileDescriptor() { + return native_get_fd(); + } + + /** + * Claims exclusive access to a {@link android.hardware.UsbInterface}. + * This must be done before sending or receiving data on any + * {@link android.hardware.UsbEndpoint}s belonging to the interface + * @param intf the interface to claim + * @param force true to disconnect kernel driver if necessary + * @return true if the interface was successfully claimed + */ + public boolean claimInterface(UsbInterface intf, boolean force) { + return native_claim_interface(intf.getId(), force); + } + + /** + * Releases exclusive access to a {@link android.hardware.UsbInterface}. + * + * @return true if the interface was successfully released + */ + public boolean releaseInterface(UsbInterface intf) { + return native_release_interface(intf.getId()); + } + + /** + * Performs a control transaction on endpoint zero for this device. + * The direction of the transfer is determined by the request type. + * If requestType & {@link UsbConstants#USB_ENDPOINT_DIR_MASK} is + * {@link UsbConstants#USB_DIR_OUT}, then the transfer is a write, + * and if it is {@link UsbConstants#USB_DIR_IN}, then the transfer + * is a read. + * + * @param requestType request type for this transaction + * @param request request ID for this transaction + * @param value value field for this transaction + * @param index index field for this transaction + * @param buffer buffer for data portion of transaction, + * or null if no data needs to be sent or received + * @param length the length of the data to send or receive + * @param timeout in milliseconds + * @return length of data transferred (or zero) for success, + * or negative value for failure + */ + public int controlTransfer(int requestType, int request, int value, + int index, byte[] buffer, int length, int timeout) { + return native_control_request(requestType, request, value, index, buffer, length, timeout); + } + + /** + * Performs a bulk transaction on the given endpoint. + * The direction of the transfer is determined by the direction of the endpoint + * + * @param endpoint the endpoint for this transaction + * @param buffer buffer for data to send or receive, + * @param length the length of the data to send or receive + * @param timeout in milliseconds + * @return length of data transferred (or zero) for success, + * or negative value for failure + */ + public int bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout) { + return native_bulk_request(endpoint.getAddress(), buffer, length, timeout); + } + + /** + * Waits for the result of a {@link android.hardware.UsbRequest#queue} operation + * Note that this may return requests queued on multiple {@link android.hardware.UsbEndpoint}s. + * When multiple endpoints are in use, {@link android.hardware.UsbRequest#getEndpoint} and + * {@link android.hardware.UsbRequest#getClientData} can be useful in determining how to process + * the result of this function. + * + * @return a completed USB request, or null if an error occurred + */ + public UsbRequest requestWait() { + UsbRequest request = native_request_wait(); + if (request != null) { + request.dequeue(); + } + return request; + } + + /** + * Returns the serial number for the device. + * This will return null if the device has not been opened. + * + * @return the device serial number + */ + public String getSerial() { + return native_get_serial(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof UsbDevice) { + return ((UsbDevice)o).mName.equals(mName); + } else if (o instanceof String) { + return ((String)o).equals(mName); + } else { + return false; + } + } + + @Override + public String toString() { + return "UsbDevice[mName=" + mName + ",mVendorId=" + mVendorId + + ",mProductId=" + mProductId + ",mClass=" + mClass + + ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol + + ",mInterfaces=" + mInterfaces + "]"; + } + + public static final Parcelable.Creator<UsbDevice> CREATOR = + new Parcelable.Creator<UsbDevice>() { + public UsbDevice createFromParcel(Parcel in) { + String name = in.readString(); + int vendorId = in.readInt(); + int productId = in.readInt(); + int clasz = in.readInt(); + int subClass = in.readInt(); + int protocol = in.readInt(); + Parcelable[] interfaces = in.readParcelableArray(UsbInterface.class.getClassLoader()); + UsbDevice result = new UsbDevice(name, vendorId, productId, clasz, subClass, protocol, interfaces); + for (int i = 0; i < interfaces.length; i++) { + ((UsbInterface)interfaces[i]).setDevice(result); + } + return result; + } + + public UsbDevice[] newArray(int size) { + return new UsbDevice[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeString(mName); + parcel.writeInt(mVendorId); + parcel.writeInt(mProductId); + parcel.writeInt(mClass); + parcel.writeInt(mSubclass); + parcel.writeInt(mProtocol); + parcel.writeParcelableArray(mInterfaces, 0); + } + + public static int getDeviceId(String name) { + return native_get_device_id(name); + } + + public static String getDeviceName(int id) { + return native_get_device_name(id); + } + + private native boolean native_open(String deviceName, FileDescriptor pfd); + private native void native_close(); + private native int native_get_fd(); + private native boolean native_claim_interface(int interfaceID, boolean force); + private native boolean native_release_interface(int interfaceID); + private native int native_control_request(int requestType, int request, int value, + int index, byte[] buffer, int length, int timeout); + private native int native_bulk_request(int endpoint, byte[] buffer, int length, int timeout); + private native UsbRequest native_request_wait(); + private native String native_get_serial(); + + private static native int native_get_device_id(String name); + private static native String native_get_device_name(int id); +} diff --git a/core/java/android/hardware/UsbEndpoint.aidl b/core/java/android/hardware/UsbEndpoint.aidl new file mode 100644 index 000000000000..51fc67bfb7f4 --- /dev/null +++ b/core/java/android/hardware/UsbEndpoint.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 android.hardware; + +parcelable UsbEndpoint; diff --git a/core/java/android/hardware/UsbEndpoint.java b/core/java/android/hardware/UsbEndpoint.java new file mode 100644 index 000000000000..8d4099dfd73e --- /dev/null +++ b/core/java/android/hardware/UsbEndpoint.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A class representing an endpoint on a {@link android.hardware.UsbInterface}. + */ +public final class UsbEndpoint implements Parcelable { + + private int mAddress; + private int mAttributes; + private int mMaxPacketSize; + private int mInterval; + private UsbInterface mInterface; + + private UsbEndpoint() { + } + + /** + * UsbEndpoint should only be instantiated by UsbService implementation + * @hide + */ + public UsbEndpoint(int address, int attributes, int maxPacketSize, int interval) { + mAddress = address; + mAttributes = attributes; + mMaxPacketSize = maxPacketSize; + mInterval = interval; + } + + /** + * Returns the endpoint's address field. + * + * @return the endpoint's address + */ + public int getAddress() { + return mAddress; + } + + /** + * Extracts the endpoint's endpoint number from its address + * + * @return the endpoint's endpoint number + */ + public int getEndpointNumber() { + return mAddress & UsbConstants.USB_ENDPOINT_NUMBER_MASK; + } + + /** + * Returns the endpoint's direction. + * Returns {@link android.hardware.UsbConstants#USB_DIR_OUT} + * if the direction is host to device, and + * {@link android.hardware.UsbConstants#USB_DIR_IN} if the + * direction is device to host. + * + * @return the endpoint's direction + */ + public int getDirection() { + return mAddress & UsbConstants.USB_ENDPOINT_DIR_MASK; + } + + /** + * Returns the endpoint's attributes field. + * + * @return the endpoint's attributes + */ + public int getAttributes() { + return mAttributes; + } + + /** + * Returns the endpoint's type. + * Possible results are: + * <ul> + * <li>{@link android.hardware.UsbConstants#USB_ENDPOINT_XFER_CONTROL} (endpoint zero) + * <li>{@link android.hardware.UsbConstants#USB_ENDPOINT_XFER_ISOC} (isochronous endpoint) + * <li>{@link android.hardware.UsbConstants#USB_ENDPOINT_XFER_BULK} (bulk endpoint) + * <li>{@link android.hardware.UsbConstants#USB_ENDPOINT_XFER_INT} (interrupt endpoint) + * </ul> + * + * @return the endpoint's type + */ + public int getType() { + return mAttributes & UsbConstants.USB_ENDPOINT_XFERTYPE_MASK; + } + + /** + * Returns the endpoint's maximum packet size. + * + * @return the endpoint's maximum packet size + */ + public int getMaxPacketSize() { + return mMaxPacketSize; + } + + /** + * Returns the endpoint's interval field. + * + * @return the endpoint's interval + */ + public int getInterval() { + return mInterval; + } + + /** + * Returns the {@link android.hardware.UsbInterface} this endpoint belongs to. + * + * @return the endpoint's interface + */ + public UsbInterface getInterface() { + return mInterface; + } + + /** + * Returns the {@link android.hardware.UsbDevice} this endpoint belongs to. + * + * @return the endpoint's device + */ + public UsbDevice getDevice() { + return mInterface.getDevice(); + } + + // only used for parcelling + /* package */ void setInterface(UsbInterface intf) { + mInterface = intf; + } + + @Override + public String toString() { + return "UsbEndpoint[mAddress=" + mAddress + ",mAttributes=" + mAttributes + + ",mMaxPacketSize=" + mMaxPacketSize + ",mInterval=" + mInterval +"]"; + } + + public static final Parcelable.Creator<UsbEndpoint> CREATOR = + new Parcelable.Creator<UsbEndpoint>() { + public UsbEndpoint createFromParcel(Parcel in) { + int address = in.readInt(); + int attributes = in.readInt(); + int maxPacketSize = in.readInt(); + int interval = in.readInt(); + return new UsbEndpoint(address, attributes, maxPacketSize, interval); + } + + public UsbEndpoint[] newArray(int size) { + return new UsbEndpoint[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mAddress); + parcel.writeInt(mAttributes); + parcel.writeInt(mMaxPacketSize); + parcel.writeInt(mInterval); + } +} diff --git a/core/java/android/hardware/UsbInterface.aidl b/core/java/android/hardware/UsbInterface.aidl new file mode 100644 index 000000000000..a715ccd0221d --- /dev/null +++ b/core/java/android/hardware/UsbInterface.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 android.hardware; + +parcelable UsbInterface; diff --git a/core/java/android/hardware/UsbInterface.java b/core/java/android/hardware/UsbInterface.java new file mode 100644 index 000000000000..deef81f1d438 --- /dev/null +++ b/core/java/android/hardware/UsbInterface.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A class representing an interface on a {@link android.hardware.UsbDevice}. + */ +public final class UsbInterface implements Parcelable { + + private int mId; + private int mClass; + private int mSubclass; + private int mProtocol; + private UsbDevice mDevice; + private Parcelable[] mEndpoints; + + private UsbInterface() { + } + + /** + * UsbInterface should only be instantiated by UsbService implementation + * @hide + */ + public UsbInterface(int id, int Class, int subClass, int protocol, + Parcelable[] endpoints) { + mId = id; + mClass = Class; + mSubclass = subClass; + mProtocol = protocol; + mEndpoints = endpoints; + } + + /** + * Returns the interface's ID field. + * + * @return the interface's ID + */ + public int getId() { + return mId; + } + + /** + * Returns the interface's class field. + * Some useful constants for USB classes can be found in + * {@link android.hardware.UsbConstants} + * + * @return the interface's class + */ + public int getInterfaceClass() { + return mClass; + } + + /** + * Returns the interface's subclass field. + * + * @return the interface's subclass + */ + public int getInterfaceSubclass() { + return mSubclass; + } + + /** + * Returns the interface's protocol field. + * + * @return the interface's protocol + */ + public int getInterfaceProtocol() { + return mProtocol; + } + + /** + * Returns the number of {@link android.hardware.UsbEndpoint}s this interface contains. + * + * @return the number of endpoints + */ + public int getEndpointCount() { + return mEndpoints.length; + } + + /** + * Returns the {@link android.hardware.UsbEndpoint} at the given index. + * + * @return the endpoint + */ + public UsbEndpoint getEndpoint(int index) { + return (UsbEndpoint)mEndpoints[index]; + } + + /** + * Returns the {@link android.hardware.UsbDevice} this interface belongs to. + * + * @return the interface's device + */ + public UsbDevice getDevice() { + return mDevice; + } + + // only used for parcelling + /* package */ void setDevice(UsbDevice device) { + mDevice = device; + } + + @Override + public String toString() { + return "UsbInterface[mId=" + mId + ",mClass=" + mClass + + ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol + + ",mEndpoints=" + mEndpoints + "]"; + } + + public static final Parcelable.Creator<UsbInterface> CREATOR = + new Parcelable.Creator<UsbInterface>() { + public UsbInterface createFromParcel(Parcel in) { + int id = in.readInt(); + int Class = in.readInt(); + int subClass = in.readInt(); + int protocol = in.readInt(); + Parcelable[] endpoints = in.readParcelableArray(UsbEndpoint.class.getClassLoader()); + UsbInterface result = new UsbInterface(id, Class, subClass, protocol, endpoints); + for (int i = 0; i < endpoints.length; i++) { + ((UsbEndpoint)endpoints[i]).setInterface(result); + } + return result; + } + + public UsbInterface[] newArray(int size) { + return new UsbInterface[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mId); + parcel.writeInt(mClass); + parcel.writeInt(mSubclass); + parcel.writeInt(mProtocol); + parcel.writeParcelableArray(mEndpoints, 0); + } +} diff --git a/core/java/android/hardware/UsbManager.java b/core/java/android/hardware/UsbManager.java index 18790d254e00..0f616ff97daf 100644 --- a/core/java/android/hardware/UsbManager.java +++ b/core/java/android/hardware/UsbManager.java @@ -17,28 +17,103 @@ package android.hardware; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.util.Log; + import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; +import java.util.HashMap; /** - * Class for accessing USB state information. - * @hide + * This class allows you to access the state of USB, both in host and device mode. + * + * <p>You can obtain an instance of this class by calling + * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. + * + * {@samplecode + * UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); + * } */ public class UsbManager { + private static final String TAG = "UsbManager"; + /** - * Broadcast Action: A sticky broadcast for USB state change events. + * Broadcast Action: A sticky broadcast for USB state change events when in device mode. * * This is a sticky broadcast for clients that includes USB connected/disconnected state, - * the USB configuration that is currently set and a bundle containing name/value pairs - * with the names of the functions and a value of either {@link #USB_FUNCTION_ENABLED} - * or {@link #USB_FUNCTION_DISABLED}. - * Possible USB function names include {@link #USB_FUNCTION_MASS_STORAGE}, - * {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS} and {@link #USB_FUNCTION_MTP}. + * <ul> + * <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected. + * <li> {@link #USB_CONFIGURATION} a Bundle containing name/value pairs where the name + * is the name of a USB function and the value is either {@link #USB_FUNCTION_ENABLED} + * or {@link #USB_FUNCTION_DISABLED}. The possible function names include + * {@link #USB_FUNCTION_MASS_STORAGE}, {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS}, + * {@link #USB_FUNCTION_MTP} and {@link #USB_FUNCTION_ACCESSORY}. + * </ul> */ public static final String ACTION_USB_STATE = "android.hardware.action.USB_STATE"; + /** + * Broadcast Action: A broadcast for USB device attached event. + * + * This intent is sent when a USB device is attached to the USB bus when in host mode. + * <ul> + * <li> {@link #EXTRA_DEVICE_NAME} containing the device's name (String) + * <li> {@link #EXTRA_VENDOR_ID} containing the device's vendor ID (Integer) + * <li> {@link #EXTRA_PRODUCT_ID} containing the device's product ID (Integer) + * <li> {@link #EXTRA_DEVICE_CLASS} } containing the device class (Integer) + * <li> {@link #EXTRA_DEVICE_SUBCLASS} containing the device subclass (Integer) + * <li> {@link #EXTRA_DEVICE_PROTOCOL} containing the device protocol (Integer) + * <li> {@link #EXTRA_DEVICE} containing the {@link android.hardware.UsbDevice} + * for the attached device + * </ul> + */ + public static final String ACTION_USB_DEVICE_ATTACHED = + "android.hardware.action.USB_DEVICE_ATTACHED"; + + /** + * Broadcast Action: A broadcast for USB device detached event. + * + * This intent is sent when a USB device is detached from the USB bus when in host mode. + * <ul> + * <li> {@link #EXTRA_DEVICE_NAME} containing the device's name (String) + * </ul> + */ + public static final String ACTION_USB_DEVICE_DETACHED = + "android.hardware.action.USB_DEVICE_DETACHED"; + + /** + * Broadcast Action: A broadcast for USB accessory attached event. + * + * This intent is sent when a USB accessory is attached. + * <ul> + * <li> {@link #EXTRA_ACCESSORY_MANUFACTURER} containing the accessory's manufacturer (String) + * <li> {@link #EXTRA_ACCESSORY_PRODUCT} containing the accessory's product name (String) + * <li> {@link #EXTRA_ACCESSORY_TYPE} containing the accessory's type (String) + * <li> {@link #EXTRA_ACCESSORY_VERSION} containing the accessory's version (String) + * <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.UsbAccessory} + * for the attached accessory + * </ul> + */ + public static final String ACTION_USB_ACCESSORY_ATTACHED = + "android.hardware.action.USB_ACCESSORY_ATTACHED"; + + /** + * Broadcast Action: A broadcast for USB accessory detached event. + * + * This intent is sent when a USB accessory is detached. + * <ul> + * <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.UsbAccessory} + * for the attached accessory that was detached + * </ul> + */ + public static final String ACTION_USB_ACCESSORY_DETACHED = + "android.hardware.action.USB_ACCESSORY_DETACHED"; + /** * Boolean extra indicating whether USB is connected or disconnected. * Used in extras for the {@link #ACTION_USB_STATE} broadcast. @@ -76,17 +151,188 @@ public class UsbManager { public static final String USB_FUNCTION_MTP = "mtp"; /** - * Value indicating that a USB function is enabled. + * Name of the Accessory USB function. * Used in extras for the {@link #ACTION_USB_STATE} broadcast */ + public static final String USB_FUNCTION_ACCESSORY = "accessory"; + + /** + * Value indicating that a USB function is enabled. + * Used in {@link #USB_CONFIGURATION} extras bundle for the + * {@link #ACTION_USB_STATE} broadcast + */ public static final String USB_FUNCTION_ENABLED = "enabled"; /** * Value indicating that a USB function is disabled. - * Used in extras for the {@link #ACTION_USB_STATE} broadcast + * Used in {@link #USB_CONFIGURATION} extras bundle for the + * {@link #ACTION_USB_STATE} broadcast */ public static final String USB_FUNCTION_DISABLED = "disabled"; + /** + * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} and + * {@link #ACTION_USB_DEVICE_DETACHED} broadcasts + * containing the device's ID (String). + */ + public static final String EXTRA_DEVICE_NAME = "device_name"; + + /** + * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast + * containing the device's vendor ID (int). + */ + public static final String EXTRA_VENDOR_ID = "vendor_id"; + + /** + * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast + * containing the device's product ID (int). + */ + public static final String EXTRA_PRODUCT_ID = "product_id"; + + /** + * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast + * containing the device's class (int). + */ + public static final String EXTRA_DEVICE_CLASS = "device_class"; + + /** + * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast + * containing the device's class (int). + */ + public static final String EXTRA_DEVICE_SUBCLASS = "device_subclass"; + + /** + * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast + * containing the device's class (int). + */ + public static final String EXTRA_DEVICE_PROTOCOL = "device_protocol"; + + /** + * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} broadcast + * containing the UsbDevice object for the device. + */ + + public static final String EXTRA_DEVICE = "device"; + + /** + * Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} broadcast + * containing the UsbAccessory object for the accessory. + */ + public static final String EXTRA_ACCESSORY = "accessory"; + + /** + * Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} broadcast + * containing the accessory's manufacturer name. + */ + public static final String EXTRA_ACCESSORY_MANUFACTURER = "accessory-manufacturer"; + + /** + * Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} broadcast + * containing the accessory's product name. + */ + public static final String EXTRA_ACCESSORY_PRODUCT = "accessory-product"; + + /** + * Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} broadcast + * containing the accessory's type. + */ + public static final String EXTRA_ACCESSORY_TYPE = "accessory-type"; + + /** + * Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} broadcast + * containing the accessory's version. + */ + public static final String EXTRA_ACCESSORY_VERSION = "accessory-version"; + + private IUsbManager mService; + + /** + * {@hide} + */ + public UsbManager(IUsbManager service) { + mService = service; + } + + /** + * Returns a HashMap containing all USB devices currently attached. + * USB device name is the key for the returned HashMap. + * The result will be empty if no devices are attached, or if + * USB host mode is inactive or unsupported. + * + * @return HashMap containing all connected USB devices. + */ + public HashMap<String,UsbDevice> getDeviceList() { + Bundle bundle = new Bundle(); + try { + mService.getDeviceList(bundle); + HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>(); + for (String name : bundle.keySet()) { + result.put(name, (UsbDevice)bundle.get(name)); + } + return result; + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getDeviceList", e); + return null; + } + } + + /** + * Opens the device so it can be used to send and receive + * data using {@link android.hardware.UsbRequest}. + * + * @param device the device to open + * @return true if we successfully opened the device + */ + public boolean openDevice(UsbDevice device) { + try { + ParcelFileDescriptor pfd = mService.openDevice(device.getDeviceName()); + if (pfd == null) { + return false; + } + boolean result = device.open(pfd); + pfd.close(); + return result; + } catch (Exception e) { + Log.e(TAG, "exception in UsbManager.openDevice", e); + return false; + } + } + + /** + * Returns a list of currently attached USB accessories. + * (in the current implementation there can be at most one) + * + * @return list of USB accessories, or null if none are attached. + */ + public UsbAccessory[] getAccessoryList() { + try { + UsbAccessory accessory = mService.getCurrentAccessory(); + if (accessory == null) { + return null; + } else { + return new UsbAccessory[] { accessory }; + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in openAccessory" , e); + return null; + } + } + + /** + * Opens a file descriptor for reading and writing data to the USB accessory. + * + * @param accessory the USB accessory to open + * @return file descriptor, or null if the accessor could not be opened. + */ + public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { + try { + return mService.openAccessory(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in openAccessory" , e); + return null; + } + } + private static File getFunctionEnableFile(String function) { return new File("/sys/class/usb_composite/" + function + "/enable"); } @@ -94,6 +340,9 @@ public class UsbManager { /** * Returns true if the specified USB function is supported by the kernel. * Note that a USB function maybe supported but disabled. + * + * @param function name of the USB function + * @return true if the USB function is supported. */ public static boolean isFunctionSupported(String function) { return getFunctionEnableFile(function).exists(); @@ -101,6 +350,9 @@ public class UsbManager { /** * Returns true if the specified USB function is currently enabled. + * + * @param function name of the USB function + * @return true if the USB function is enabled. */ public static boolean isFunctionEnabled(String function) { try { @@ -112,4 +364,20 @@ public class UsbManager { return false; } } + + /** + * Enables or disables a USB function. + * + * @hide + */ + public static boolean setFunctionEnabled(String function, boolean enable) { + try { + FileOutputStream stream = new FileOutputStream(getFunctionEnableFile(function)); + stream.write(enable ? '1' : '0'); + stream.close(); + return true; + } catch (IOException e) { + return false; + } + } } diff --git a/core/java/android/hardware/UsbRequest.java b/core/java/android/hardware/UsbRequest.java new file mode 100644 index 000000000000..ae3a289e11d5 --- /dev/null +++ b/core/java/android/hardware/UsbRequest.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +import android.util.Log; + +import java.nio.ByteBuffer; + +/** + * A class representing USB request packet. + * This can be used for both reading and writing data to or from a + * {@link android.hardware.UsbDevice}. + * UsbRequests are sent asynchronously via {@link #queue} and the results + * are read by {@link android.hardware.UsbDevice#requestWait}. + */ +public class UsbRequest { + + private static final String TAG = "UsbRequest"; + + // used by the JNI code + private int mNativeContext; + + private UsbEndpoint mEndpoint; + + // for temporarily saving current buffer across queue and dequeue + private ByteBuffer mBuffer; + private int mLength; + + // for client use + private Object mClientData; + + public UsbRequest() { + } + + /** + * Initializes the request so it can read or write data on the given endpoint. + * Whether the request allows reading or writing depends on the direction of the endpoint. + * + * @param endpoint the endpoint to be used for this request. + * @return true if the request was successfully opened. + */ + public boolean initialize(UsbEndpoint endpoint) { + mEndpoint = endpoint; + return native_init(endpoint.getDevice(), + endpoint.getAddress(), endpoint.getAttributes(), + endpoint.getMaxPacketSize(), endpoint.getInterval()); + } + + /** + * Releases all resources related to this request. + */ + public void close() { + mEndpoint = null; + native_close(); + } + + @Override + protected void finalize() throws Throwable { + try { + if (mEndpoint != null) { + Log.v(TAG, "endpoint still open in finalize(): " + this); + close(); + } + } finally { + super.finalize(); + } + } + + /** + * Returns the endpoint for the request, or null if the request is not opened. + * + * @return the request's endpoint + */ + public UsbEndpoint getEndpoint() { + return mEndpoint; + } + + /** + * Returns the client data for the request. + * This can be used in conjunction with {@link #setClientData} + * to associate another object with this request, which can be useful for + * maintaining state between calls to {@link #queue} and + * {@link android.hardware.UsbDevice#requestWait} + * + * @return the client data for the request + */ + public Object getClientData() { + return mClientData; + } + + /** + * Sets the client data for the request. + * This can be used in conjunction with {@link #getClientData} + * to associate another object with this request, which can be useful for + * maintaining state between calls to {@link #queue} and + * {@link android.hardware.UsbDevice#requestWait} + * + * @param data the client data for the request + */ + public void setClientData(Object data) { + mClientData = data; + } + + /** + * Queues the request to send or receive data on its endpoint. + * For OUT endpoints, the given buffer data will be sent on the endpoint. + * For IN endpoints, the endpoint will attempt to read the given number of bytes + * into the specified buffer. + * If the queueing operation is successful, we return true and the result will be + * returned via {@link android.hardware.UsbDevice#requestWait} + * + * @param buffer the buffer containing the bytes to write, or location to store + * the results of a read + * @param length number of bytes to read or write + * @return true if the queueing operation succeeded + */ + public boolean queue(ByteBuffer buffer, int length) { + boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); + boolean result; + if (buffer.isDirect()) { + result = native_queue_direct(buffer, length, out); + } else if (buffer.hasArray()) { + result = native_queue_array(buffer.array(), length, out); + } else { + throw new IllegalArgumentException("buffer is not direct and has no array"); + } + if (result) { + // save our buffer for when the request has completed + mBuffer = buffer; + mLength = length; + } + return result; + } + + /* package */ void dequeue() { + boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); + if (mBuffer.isDirect()) { + native_dequeue_direct(); + } else { + native_dequeue_array(mBuffer.array(), mLength, out); + } + mBuffer = null; + mLength = 0; + } + + /** + * Cancels a pending queue operation. + * + * @return true if cancelling succeeded + */ + public boolean cancel() { + return native_cancel(); + } + + private native boolean native_init(UsbDevice device, int ep_address, int ep_attributes, + int ep_max_packet_size, int ep_interval); + private native void native_close(); + private native boolean native_queue_array(byte[] buffer, int length, boolean out); + private native void native_dequeue_array(byte[] buffer, int length, boolean out); + private native boolean native_queue_direct(ByteBuffer buffer, int length, boolean out); + private native void native_dequeue_direct(); + private native boolean native_cancel(); +} diff --git a/core/java/android/net/DhcpInfo.java b/core/java/android/net/DhcpInfo.java index 9c81c193672c..e2660e40f57c 100644 --- a/core/java/android/net/DhcpInfo.java +++ b/core/java/android/net/DhcpInfo.java @@ -18,6 +18,7 @@ package android.net; import android.os.Parcelable; import android.os.Parcel; +import java.net.InetAddress; /** * A simple object for retrieving the results of a DHCP request. @@ -65,10 +66,7 @@ public class DhcpInfo implements Parcelable { } private static void putAddress(StringBuffer buf, int addr) { - buf.append(addr & 0xff).append('.'). - append((addr >>>= 8) & 0xff).append('.'). - append((addr >>>= 8) & 0xff).append('.'). - append((addr >>>= 8) & 0xff); + buf.append(NetworkUtils.intToInetAddress(addr).getHostAddress()); } /** Implement the Parcelable interface {@hide} */ diff --git a/core/java/android/net/DhcpInfoInternal.java b/core/java/android/net/DhcpInfoInternal.java new file mode 100644 index 000000000000..73966692d03f --- /dev/null +++ b/core/java/android/net/DhcpInfoInternal.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.net; + +import android.text.TextUtils; +import android.util.Log; + +import java.net.InetAddress; +import java.net.Inet4Address; +import java.net.UnknownHostException; + +/** + * A simple object for retrieving the results of a DHCP request. + * Replaces (internally) the IPv4-only DhcpInfo class. + * @hide + */ +public class DhcpInfoInternal { + private final static String TAG = "DhcpInfoInternal"; + public String ipAddress; + public String gateway; + public int prefixLength; + + public String dns1; + public String dns2; + + public String serverAddress; + public int leaseDuration; + + public DhcpInfoInternal() { + } + + private int convertToInt(String addr) { + if (addr != null) { + try { + InetAddress inetAddress = NetworkUtils.numericToInetAddress(addr); + if (inetAddress instanceof Inet4Address) { + return NetworkUtils.inetAddressToInt(inetAddress); + } + } catch (IllegalArgumentException e) {} + } + return 0; + } + + public DhcpInfo makeDhcpInfo() { + DhcpInfo info = new DhcpInfo(); + info.ipAddress = convertToInt(ipAddress); + info.gateway = convertToInt(gateway); + try { + InetAddress inetAddress = NetworkUtils.numericToInetAddress(ipAddress); + info.netmask = NetworkUtils.prefixLengthToNetmaskInt(prefixLength); + } catch (IllegalArgumentException e) {} + info.dns1 = convertToInt(dns1); + info.dns2 = convertToInt(dns2); + info.serverAddress = convertToInt(serverAddress); + info.leaseDuration = leaseDuration; + return info; + } + + public LinkAddress makeLinkAddress() { + if (TextUtils.isEmpty(ipAddress)) { + Log.e(TAG, "makeLinkAddress with empty ipAddress"); + return null; + } + return new LinkAddress(NetworkUtils.numericToInetAddress(ipAddress), prefixLength); + } + + public LinkProperties makeLinkProperties() { + LinkProperties p = new LinkProperties(); + p.addLinkAddress(makeLinkAddress()); + if (TextUtils.isEmpty(gateway) == false) { + p.addGateway(NetworkUtils.numericToInetAddress(gateway)); + } + if (TextUtils.isEmpty(dns1) == false) { + p.addDns(NetworkUtils.numericToInetAddress(dns1)); + } else { + Log.d(TAG, "makeLinkProperties with empty dns1!"); + } + if (TextUtils.isEmpty(dns2) == false) { + p.addDns(NetworkUtils.numericToInetAddress(dns2)); + } else { + Log.d(TAG, "makeLinkProperties with empty dns2!"); + } + return p; + } + + public String toString() { + return "addr: " + ipAddress + "/" + prefixLength + + " gateway: " + gateway + + " dns: " + dns1 + "," + dns2 + + " dhcpServer: " + serverAddress + + " leaseDuration: " + leaseDuration; + } +} diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java index d1bbaa49abbd..89b5915b2544 100644 --- a/core/java/android/net/InterfaceConfiguration.java +++ b/core/java/android/net/InterfaceConfiguration.java @@ -28,8 +28,7 @@ import java.net.UnknownHostException; */ public class InterfaceConfiguration implements Parcelable { public String hwAddr; - public InetAddress addr; - public InetAddress mask; + public LinkAddress addr; public String interfaceFlags; public InterfaceConfiguration() { @@ -41,8 +40,6 @@ public class InterfaceConfiguration implements Parcelable { str.append("ipddress "); str.append((addr != null) ? addr.toString() : "NULL"); - str.append(" netmask "); - str.append((mask != null) ? mask.toString() : "NULL"); str.append(" flags ").append(interfaceFlags); str.append(" hwaddr ").append(hwAddr); @@ -59,7 +56,7 @@ public class InterfaceConfiguration implements Parcelable { public boolean isActive() { try { if(interfaceFlags.contains("up")) { - for (byte b : addr.getAddress()) { + for (byte b : addr.getAddress().getAddress()) { if (b != 0) return true; } } @@ -79,13 +76,7 @@ public class InterfaceConfiguration implements Parcelable { dest.writeString(hwAddr); if (addr != null) { dest.writeByte((byte)1); - dest.writeByteArray(addr.getAddress()); - } else { - dest.writeByte((byte)0); - } - if (mask != null) { - dest.writeByte((byte)1); - dest.writeByteArray(mask.getAddress()); + dest.writeParcelable(addr, flags); } else { dest.writeByte((byte)0); } @@ -99,14 +90,7 @@ public class InterfaceConfiguration implements Parcelable { InterfaceConfiguration info = new InterfaceConfiguration(); info.hwAddr = in.readString(); if (in.readByte() == 1) { - try { - info.addr = InetAddress.getByAddress(in.createByteArray()); - } catch (UnknownHostException e) {} - } - if (in.readByte() == 1) { - try { - info.mask = InetAddress.getByAddress(in.createByteArray()); - } catch (UnknownHostException e) {} + info.addr = in.readParcelable(null); } info.interfaceFlags = in.readString(); return info; diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java index 3f03a2a875d5..9c36b1276526 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -19,6 +19,7 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; +import java.net.Inet4Address; import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.UnknownHostException; @@ -38,12 +39,13 @@ public class LinkAddress implements Parcelable { */ private final int prefixLength; - public LinkAddress(InetAddress address, InetAddress mask) { - this.address = address; - this.prefixLength = computeprefixLength(mask); - } - public LinkAddress(InetAddress address, int prefixLength) { + if (address == null || prefixLength < 0 || + ((address instanceof Inet4Address) && prefixLength > 32) || + (prefixLength > 128)) { + throw new IllegalArgumentException("Bad LinkAddress params " + address + + prefixLength); + } this.address = address; this.prefixLength = prefixLength; } @@ -53,18 +55,6 @@ public class LinkAddress implements Parcelable { this.prefixLength = interfaceAddress.getNetworkPrefixLength(); } - private static int computeprefixLength(InetAddress mask) { - int count = 0; - for (byte b : mask.getAddress()) { - for (int i = 0; i < 8; ++i) { - if ((b & (1 << i)) != 0) { - ++count; - } - } - } - return count; - } - @Override public String toString() { return (address == null ? "" : (address.getHostAddress() + "/" + prefixLength)); diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index f1545eab4686..b6e9751a520b 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -31,7 +31,24 @@ import java.util.Collections; /** * Describes the properties of a network link. - * TODO - consider adding optional fields like Apn and ApnType + * + * A link represents a connection to a network. + * It may have multiple addresses and multiple gateways, + * multiple dns servers but only one http proxy. + * + * Because it's a single network, the dns's + * are interchangeable and don't need associating with + * particular addresses. The gateways similarly don't + * need associating with particular addresses. + * + * A dual stack interface works fine in this model: + * each address has it's own prefix length to describe + * the local network. The dns servers all return + * both v4 addresses and v6 addresses regardless of the + * address family of the server itself (rfc4213) and we + * don't care which is used. The gateways will be + * selected based on the destination address and the + * source address has no relavence. * @hide */ public class LinkProperties implements Parcelable { @@ -39,7 +56,7 @@ public class LinkProperties implements Parcelable { String mIfaceName; private Collection<LinkAddress> mLinkAddresses; private Collection<InetAddress> mDnses; - private InetAddress mGateway; + private Collection<InetAddress> mGateways; private ProxyProperties mHttpProxy; public LinkProperties() { @@ -52,7 +69,7 @@ public class LinkProperties implements Parcelable { mIfaceName = source.getInterfaceName(); mLinkAddresses = source.getLinkAddresses(); mDnses = source.getDnses(); - mGateway = source.getGateway(); + mGateways = source.getGateways(); mHttpProxy = new ProxyProperties(source.getHttpProxy()); } } @@ -89,11 +106,11 @@ public class LinkProperties implements Parcelable { return Collections.unmodifiableCollection(mDnses); } - public void setGateway(InetAddress gateway) { - mGateway = gateway; + public void addGateway(InetAddress gateway) { + mGateways.add(gateway); } - public InetAddress getGateway() { - return mGateway; + public Collection<InetAddress> getGateways() { + return Collections.unmodifiableCollection(mGateways); } public void setHttpProxy(ProxyProperties proxy) { @@ -107,7 +124,7 @@ public class LinkProperties implements Parcelable { mIfaceName = null; mLinkAddresses = new ArrayList<LinkAddress>(); mDnses = new ArrayList<InetAddress>(); - mGateway = null; + mGateways = new ArrayList<InetAddress>(); mHttpProxy = null; } @@ -131,10 +148,12 @@ public class LinkProperties implements Parcelable { for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ","; dns += "] "; + String gateways = "Gateways: ["; + for (InetAddress gw : mGateways) gateways += gw.getHostAddress() + ","; + gateways += "] "; String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " "); - String gateway = (mGateway == null ? "" : "Gateway: " + mGateway.getHostAddress() + " "); - return ifaceName + linkAddresses + gateway + dns + proxy; + return ifaceName + linkAddresses + gateways + dns + proxy; } /** @@ -152,12 +171,12 @@ public class LinkProperties implements Parcelable { for(InetAddress d : mDnses) { dest.writeByteArray(d.getAddress()); } - if (mGateway != null) { - dest.writeByte((byte)1); - dest.writeByteArray(mGateway.getAddress()); - } else { - dest.writeByte((byte)0); + + dest.writeInt(mGateways.size()); + for(InetAddress gw : mGateways) { + dest.writeByteArray(gw.getAddress()); } + if (mHttpProxy != null) { dest.writeByte((byte)1); dest.writeParcelable(mHttpProxy, flags); @@ -192,10 +211,11 @@ public class LinkProperties implements Parcelable { netProp.addDns(InetAddress.getByAddress(in.createByteArray())); } catch (UnknownHostException e) { } } - if (in.readByte() == 1) { + addressCount = in.readInt(); + for (int i=0; i<addressCount; i++) { try { - netProp.setGateway(InetAddress.getByAddress(in.createByteArray())); - } catch (UnknownHostException e) {} + netProp.addGateway(InetAddress.getByAddress(in.createByteArray())); + } catch (UnknownHostException e) { } } if (in.readByte() == 1) { netProp.setHttpProxy((ProxyProperties)in.readParcelable(null)); diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index 8a653ddd55e6..b3f39885f943 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -77,7 +77,7 @@ public class NetworkUtils { * the IP address information. * @return {@code true} for success, {@code false} for failure */ - public native static boolean runDhcp(String interfaceName, DhcpInfo ipInfo); + public native static boolean runDhcp(String interfaceName, DhcpInfoInternal ipInfo); /** * Shut down the DHCP client daemon. @@ -104,45 +104,20 @@ public class NetworkUtils { public native static String getDhcpError(); /** - * When static IP configuration has been specified, configure the network - * interface according to the values supplied. - * @param interfaceName the name of the interface to configure - * @param ipInfo the IP address, default gateway, and DNS server addresses - * with which to configure the interface. - * @return {@code true} for success, {@code false} for failure - */ - public static boolean configureInterface(String interfaceName, DhcpInfo ipInfo) { - return configureNative(interfaceName, - ipInfo.ipAddress, - ipInfo.netmask, - ipInfo.gateway, - ipInfo.dns1, - ipInfo.dns2); - } - - private native static boolean configureNative( - String interfaceName, int ipAddress, int netmask, int gateway, int dns1, int dns2); - - /** * Convert a IPv4 address from an integer to an InetAddress. - * @param hostAddr is an Int corresponding to the IPv4 address in network byte order - * @return the IP address as an {@code InetAddress}, returns null if - * unable to convert or if the int is an invalid address. + * @param hostAddress an int corresponding to the IPv4 address in network byte order */ public static InetAddress intToInetAddress(int hostAddress) { - InetAddress inetAddress; byte[] addressBytes = { (byte)(0xff & hostAddress), (byte)(0xff & (hostAddress >> 8)), (byte)(0xff & (hostAddress >> 16)), (byte)(0xff & (hostAddress >> 24)) }; try { - inetAddress = InetAddress.getByAddress(addressBytes); - } catch(UnknownHostException e) { - return null; + return InetAddress.getByAddress(addressBytes); + } catch (UnknownHostException e) { + throw new AssertionError(); } - - return inetAddress; } /** @@ -175,6 +150,19 @@ public class NetworkUtils { } /** + * Create an InetAddress from a string where the string must be a standard + * representation of a V4 or V6 address. Avoids doing a DNS lookup on failure + * but it will throw an IllegalArgumentException in that case. + * @param addrString + * @return the InetAddress + * @hide + */ + public static InetAddress numericToInetAddress(String addrString) + throws IllegalArgumentException { + return InetAddress.parseNumericAddress(addrString); + } + + /** * Add a default route through the specified gateway. * @param interfaceName interface on which the route should be added * @param gw the IP address of the gateway to which the route is desired, diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 0d64dab179e9..eca06c50dd82 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -69,6 +69,43 @@ public class TrafficStats { public static native long getMobileRxBytes(); /** + * Get the total number of packets transmitted through the specified interface. + * + * @return number of packets. If the statistics are not supported by this interface, + * {@link #UNSUPPORTED} will be returned. + * @hide + */ + public static native long getTxPackets(String iface); + + /** + * Get the total number of packets received through the specified interface. + * + * @return number of packets. If the statistics are not supported by this interface, + * {@link #UNSUPPORTED} will be returned. + * @hide + */ + public static native long getRxPackets(String iface); + + /** + * Get the total number of bytes transmitted through the specified interface. + * + * @return number of bytes. If the statistics are not supported by this interface, + * {@link #UNSUPPORTED} will be returned. + * @hide + */ + public static native long getTxBytes(String iface); + + /** + * Get the total number of bytes received through the specified interface. + * + * @return number of bytes. If the statistics are not supported by this interface, + * {@link #UNSUPPORTED} will be returned. + * @hide + */ + public static native long getRxBytes(String iface); + + + /** * Get the total number of packets sent through all network interfaces. * * @return the number of packets. If the statistics are not supported by this device, @@ -122,4 +159,139 @@ public class TrafficStats { * @return number of bytes */ public static native long getUidRxBytes(int uid); + + /** + * Get the number of packets (TCP segments + UDP) sent through + * the network for this UID. + * The statistics are across all interfaces. + * + * {@see android.os.Process#myUid()}. + * + * @param uid The UID of the process to examine. + * @return number of packets. + * If the statistics are not supported by this device, + * {@link #UNSUPPORTED} will be returned. + */ + public static native long getUidTxPackets(int uid); + + /** + * Get the number of packets (TCP segments + UDP) received through + * the network for this UID. + * The statistics are across all interfaces. + * + * {@see android.os.Process#myUid()}. + * + * @param uid The UID of the process to examine. + * @return number of packets + */ + public static native long getUidRxPackets(int uid); + + /** + * Get the number of TCP payload bytes sent for this UID. + * This total does not include protocol and control overheads at + * the transport and the lower layers of the networking stack. + * The statistics are across all interfaces. + * + * {@see android.os.Process#myUid()}. + * + * @param uid The UID of the process to examine. + * @return number of bytes. If the statistics are not supported by this device, + * {@link #UNSUPPORTED} will be returned. + */ + public static native long getUidTcpTxBytes(int uid); + + /** + * Get the number of TCP payload bytes received for this UID. + * This total does not include protocol and control overheads at + * the transport and the lower layers of the networking stack. + * The statistics are across all interfaces. + * + * {@see android.os.Process#myUid()}. + * + * @param uid The UID of the process to examine. + * @return number of bytes. If the statistics are not supported by this device, + * {@link #UNSUPPORTED} will be returned. + */ + public static native long getUidTcpRxBytes(int uid); + + /** + * Get the number of UDP payload bytes sent for this UID. + * This total does not include protocol and control overheads at + * the transport and the lower layers of the networking stack. + * The statistics are across all interfaces. + * + * {@see android.os.Process#myUid()}. + * + * @param uid The UID of the process to examine. + * @return number of bytes. If the statistics are not supported by this device, + * {@link #UNSUPPORTED} will be returned. + */ + public static native long getUidUdpTxBytes(int uid); + + /** + * Get the number of UDP payload bytes received for this UID. + * This total does not include protocol and control overheads at + * the transport and the lower layers of the networking stack. + * The statistics are across all interfaces. + * + * {@see android.os.Process#myUid()}. + * + * @param uid The UID of the process to examine. + * @return number of bytes. If the statistics are not supported by this device, + * {@link #UNSUPPORTED} will be returned. + */ + public static native long getUidUdpRxBytes(int uid); + + /** + * Get the number of TCP segments sent for this UID. + * Does not include TCP control packets (SYN/ACKs/FIN/..). + * The statistics are across all interfaces. + * + * {@see android.os.Process#myUid()}. + * + * @param uid The UID of the process to examine. + * @return number of TCP segments. If the statistics are not supported by this device, + * {@link #UNSUPPORTED} will be returned. + */ + public static native long getUidTcpTxSegments(int uid); + + /** + * Get the number of TCP segments received for this UID. + * Does not include TCP control packets (SYN/ACKs/FIN/..). + * The statistics are across all interfaces. + * + * {@see android.os.Process#myUid()}. + * + * @param uid The UID of the process to examine. + * @return number of TCP segments. If the statistics are not supported by this device, + * {@link #UNSUPPORTED} will be returned. + */ + public static native long getUidTcpRxSegments(int uid); + + + /** + * Get the number of UDP packets sent for this UID. + * Includes DNS requests. + * The statistics are across all interfaces. + * + * {@see android.os.Process#myUid()}. + * + * @param uid The UID of the process to examine. + * @return number of packets. If the statistics are not supported by this device, + * {@link #UNSUPPORTED} will be returned. + */ + public static native long getUidUdpTxPackets(int uid); + + /** + * Get the number of UDP packets received for this UID. + * Includes DNS responses. + * The statistics are across all interfaces. + * + * {@see android.os.Process#myUid()}. + * + * @param uid The UID of the process to examine. + * @return number of packets. If the statistics are not supported by this device, + * {@link #UNSUPPORTED} will be returned. + */ + public static native long getUidUdpRxPackets(int uid); } diff --git a/core/java/android/net/dhcp/DhcpAckPacket.java b/core/java/android/net/dhcp/DhcpAckPacket.java index 900a0e6987c5..4eca5317ca78 100644 --- a/core/java/android/net/dhcp/DhcpAckPacket.java +++ b/core/java/android/net/dhcp/DhcpAckPacket.java @@ -33,7 +33,7 @@ class DhcpAckPacket extends DhcpPacket { DhcpAckPacket(int transId, boolean broadcast, InetAddress serverAddress, InetAddress clientIp, byte[] clientMac) { - super(transId, Inet4Address.ANY, clientIp, Inet4Address.ANY, + super(transId, Inet4Address.ANY, clientIp, serverAddress, Inet4Address.ANY, clientMac, broadcast); mBroadcast = broadcast; mSrcIp = serverAddress; diff --git a/core/java/android/net/dhcp/DhcpPacket.java b/core/java/android/net/dhcp/DhcpPacket.java index e009f70c339a..7d2bd694abf7 100644 --- a/core/java/android/net/dhcp/DhcpPacket.java +++ b/core/java/android/net/dhcp/DhcpPacket.java @@ -561,7 +561,7 @@ abstract class DhcpPacket { InetAddress nextIp; InetAddress relayIp; byte[] clientMac; - List<InetAddress> dnsServers = null; + List<InetAddress> dnsServers = new ArrayList<InetAddress>(); InetAddress gateway = null; // aka router Integer leaseTime = null; InetAddress serverIdentifier = null; @@ -684,7 +684,6 @@ abstract class DhcpPacket { expectedLen = 4; break; case DHCP_DNS_SERVER: - dnsServers = new ArrayList<InetAddress>(); expectedLen = 0; for (expectedLen = 0; expectedLen < optionLen; diff --git a/core/java/android/net/http/CertificateValidatorCache.java b/core/java/android/net/http/CertificateValidatorCache.java deleted file mode 100644 index 47661d5e7623..000000000000 --- a/core/java/android/net/http/CertificateValidatorCache.java +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.http; - -import android.os.SystemClock; - -import android.security.Sha1MessageDigest; - -import java.security.cert.Certificate; -import java.security.cert.CertificateFactory; -import java.security.cert.CertPath; -import java.security.GeneralSecurityException; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Random; - - -/** - * Validator cache used to speed-up certificate chain validation. The idea is - * to keep each secure domain name associated with a cryptographically secure - * hash of the certificate chain successfully used to validate the domain. If - * we establish connection with the domain more than once and each time receive - * the same list of certificates, we do not have to re-validate. - * - * {@hide} - */ -class CertificateValidatorCache { - - // TODO: debug only! - public static long mSave = 0; - public static long mCost = 0; - // TODO: debug only! - - /** - * The cache-entry lifetime in milliseconds (here, 10 minutes) - */ - private static final long CACHE_ENTRY_LIFETIME = 10 * 60 * 1000; - - /** - * The certificate factory - */ - private static CertificateFactory sCertificateFactory; - - /** - * The certificate validator cache map (domain to a cache entry) - */ - private HashMap<Integer, CacheEntry> mCacheMap; - - /** - * Random salt - */ - private int mBigScrew; - - /** - * @param certificate The array of server certificates to compute a - * secure hash from - * @return The secure hash computed from server certificates - */ - public static byte[] secureHash(Certificate[] certificates) { - byte[] secureHash = null; - - // TODO: debug only! - long beg = SystemClock.uptimeMillis(); - // TODO: debug only! - - if (certificates != null && certificates.length != 0) { - byte[] encodedCertPath = null; - try { - synchronized (CertificateValidatorCache.class) { - if (sCertificateFactory == null) { - try { - sCertificateFactory = - CertificateFactory.getInstance("X.509"); - } catch(GeneralSecurityException e) { - if (HttpLog.LOGV) { - HttpLog.v("CertificateValidatorCache:" + - " failed to create the certificate factory"); - } - } - } - } - - CertPath certPath = - sCertificateFactory.generateCertPath(Arrays.asList(certificates)); - if (certPath != null) { - encodedCertPath = certPath.getEncoded(); - if (encodedCertPath != null) { - Sha1MessageDigest messageDigest = - new Sha1MessageDigest(); - secureHash = messageDigest.digest(encodedCertPath); - } - } - } catch (GeneralSecurityException e) {} - } - - // TODO: debug only! - long end = SystemClock.uptimeMillis(); - mCost += (end - beg); - // TODO: debug only! - - return secureHash; - } - - /** - * Creates a new certificate-validator cache - */ - public CertificateValidatorCache() { - Random random = new Random(); - mBigScrew = random.nextInt(); - - mCacheMap = new HashMap<Integer, CacheEntry>(); - } - - /** - * @param domain The domain to check against - * @param secureHash The secure hash to check against - * @return True iff there is a valid (not expired) cache entry - * associated with the domain and the secure hash - */ - public boolean has(String domain, byte[] secureHash) { - boolean rval = false; - - if (domain != null && domain.length() != 0) { - if (secureHash != null && secureHash.length != 0) { - CacheEntry cacheEntry = (CacheEntry)mCacheMap.get( - new Integer(mBigScrew ^ domain.hashCode())); - if (cacheEntry != null) { - if (!cacheEntry.expired()) { - rval = cacheEntry.has(domain, secureHash); - // TODO: debug only! - if (rval) { - mSave += cacheEntry.mSave; - } - // TODO: debug only! - } else { - mCacheMap.remove(cacheEntry); - } - } - } - } - - return rval; - } - - /** - * Adds the (domain, secureHash) tuple to the cache - * @param domain The domain to be added to the cache - * @param secureHash The secure hash to be added to the cache - * @return True iff succeeds - */ - public boolean put(String domain, byte[] secureHash, long save) { - if (domain != null && domain.length() != 0) { - if (secureHash != null && secureHash.length != 0) { - mCacheMap.put( - new Integer(mBigScrew ^ domain.hashCode()), - new CacheEntry(domain, secureHash, save)); - - return true; - } - } - - return false; - } - - /** - * Certificate-validator cache entry. We have one per domain - */ - private class CacheEntry { - - /** - * The hash associated with this cache entry - */ - private byte[] mHash; - - /** - * The time associated with this cache entry - */ - private long mTime; - - // TODO: debug only! - public long mSave; - // TODO: debug only! - - /** - * The host associated with this cache entry - */ - private String mDomain; - - /** - * Creates a new certificate-validator cache entry - * @param domain The domain to be associated with this cache entry - * @param secureHash The secure hash to be associated with this cache - * entry - */ - public CacheEntry(String domain, byte[] secureHash, long save) { - mDomain = domain; - mHash = secureHash; - // TODO: debug only! - mSave = save; - // TODO: debug only! - mTime = SystemClock.uptimeMillis(); - } - - /** - * @return True iff the cache item has expired - */ - public boolean expired() { - return CACHE_ENTRY_LIFETIME < SystemClock.uptimeMillis() - mTime; - } - - /** - * @param domain The domain to check - * @param secureHash The secure hash to check - * @return True iff the given domain and hash match those associated - * with this entry - */ - public boolean has(String domain, byte[] secureHash) { - if (domain != null && 0 < domain.length()) { - if (!mDomain.equals(domain)) { - return false; - } - } - - if (secureHash != null) { - int hashLength = secureHash.length; - if (0 < hashLength) { - if (hashLength == mHash.length) { - for (int i = 0; i < hashLength; ++i) { - if (secureHash[i] != mHash[i]) { - return false; - } - } - return true; - } - } - } - - return false; - } - } -}; diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java index 2c48a04d3ec3..f23f69cd242d 100644 --- a/core/java/android/net/http/RequestHandle.java +++ b/core/java/android/net/http/RequestHandle.java @@ -18,7 +18,6 @@ package android.net.http; import android.net.ParseException; import android.net.WebAddress; -import android.security.Md5MessageDigest; import junit.framework.Assert; import android.webkit.CookieManager; @@ -26,6 +25,8 @@ import org.apache.commons.codec.binary.Base64; import java.io.InputStream; import java.lang.Math; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; import java.util.Random; @@ -377,11 +378,15 @@ public class RequestHandle { */ private String H(String param) { if (param != null) { - Md5MessageDigest md5 = new Md5MessageDigest(); + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); - byte[] d = md5.digest(param.getBytes()); - if (d != null) { - return bufferToHex(d); + byte[] d = md5.digest(param.getBytes()); + if (d != null) { + return bufferToHex(d); + } + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); } } diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java index bba11b004f3e..5079c23ceb64 100644 --- a/core/java/android/net/http/SslCertificate.java +++ b/core/java/android/net/http/SslCertificate.java @@ -110,7 +110,7 @@ public class SslCertificate { * @param issuedBy The entity that issued this certificate * @param validNotBefore The not-before date from the certificate validity period in ISO 8601 format * @param validNotAfter The not-after date from the certificate validity period in ISO 8601 format - * @deprecated Use {@link #SslCertificate(String, String, Date, Date)} + * @deprecated Use {@link #SslCertificate(X509Certificate)} */ @Deprecated public SslCertificate( @@ -124,7 +124,9 @@ public class SslCertificate { * @param issuedBy The entity that issued this certificate * @param validNotBefore The not-before date from the certificate validity period * @param validNotAfter The not-after date from the certificate validity period + * @deprecated Use {@link #SslCertificate(X509Certificate)} */ + @Deprecated public SslCertificate( String issuedTo, String issuedBy, Date validNotBefore, Date validNotAfter) { mIssuedTo = new DName(issuedTo); diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 6d19f4174183..4991914204d9 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -244,7 +244,7 @@ public class RecoverySystem { // algorithm is used by the signature (which should be // SHA1withRSA). - String da = sigInfo.getdigestAlgorithm(); + String da = sigInfo.getDigestAlgorithm(); String dea = sigInfo.getDigestEncryptionAlgorithm(); String alg = null; if (da == null || dea == null) { diff --git a/core/java/android/provider/Applications.java b/core/java/android/provider/Applications.java index 7aabc5080928..3686d173052b 100644 --- a/core/java/android/provider/Applications.java +++ b/core/java/android/provider/Applications.java @@ -18,6 +18,7 @@ package android.provider; import android.content.ComponentName; import android.content.ContentResolver; +import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; @@ -26,10 +27,12 @@ import java.util.List; /** * The Applications provider gives information about installed applications. * - * @hide Only used by ApplicationsProvider so far. + * @hide Only used by ApplicationsProvider and Launchers so far. */ public class Applications { + private static final String TAG = "ApplicationsProvider"; + /** * The content authority for this provider. */ @@ -65,6 +68,26 @@ public class Applications { ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + APPLICATION_SUB_TYPE; /** + * The path that should be used when an application is launched. The aim is + * to help ApplicationsProvider keep track of which applications the user + * uses the most, and improve app ranking based on this. + */ + public static final String INCREASE_LAUNCH_COUNT_PATH = "increase_launch_count"; + + public static final Uri INCREASE_LAUNCH_COUNT_URI = CONTENT_URI.buildUpon() + .appendPath(INCREASE_LAUNCH_COUNT_PATH).build(); + + /** + * The package name parameter for the "increase launch count" call. + */ + public static final String INCREASE_LAUNCH_COUNT_PACKAGE = "packageName"; + + /** + * The classname parameter for the "increase launch count" call. + */ + public static final String INCREASE_LAUNCH_COUNT_CLASS = "className"; + + /** * no public constructor since this is a utility class */ private Applications() {} @@ -79,6 +102,20 @@ public class Applications { } /** + * Increases the launch count of an application. Launch counts are used + * by the ApplicationsProvider to improve ranking. + */ + public static void increaseLaunchCount( + final ContentResolver resolver, final ComponentName componentName) { + + ContentValues parameters = new ContentValues(); + parameters.put(INCREASE_LAUNCH_COUNT_PACKAGE, componentName.getPackageName()); + parameters.put(INCREASE_LAUNCH_COUNT_CLASS, componentName.getClassName()); + + resolver.insert(INCREASE_LAUNCH_COUNT_URI, parameters); + } + + /** * Gets the application component name from an application URI. * * @param appUri A URI of the form diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java index c0076054e7ba..de7176369c0c 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -59,6 +59,10 @@ public final class Calendar { public static final String EVENT_BEGIN_TIME = "beginTime"; public static final String EVENT_END_TIME = "endTime"; + /** + * This must not be changed or horrible, unspeakable things could happen. + * For instance, the Calendar app might break. Also, the db might not work. + */ public static final String AUTHORITY = "com.android.calendar"; /** diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index ee091f04b330..b05b078a27d0 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -3807,58 +3807,42 @@ public final class ContactsContract { } /** - * Additional columns returned by the {@link Contacts#CONTENT_FILTER_URI} providing the - * explanation of why the filter matched the contact. Specifically, they contain the - * data type and element that was used for matching. - * <p> - * This is temporary API, it will need to change when we move to FTS. + * Additional column returned by the {@link Contacts#CONTENT_FILTER_URI} providing the + * explanation of why the filter matched the contact. Specifically, it contains the + * data elements that matched the query. The overall number of words in the snippet + * can be capped. * * @hide */ public static class SearchSnippetColumns { /** - * The ID of the data row that was matched by the filter. - * - * @hide - */ - public static final String SNIPPET_DATA_ID = "snippet_data_id"; - - /** - * The type of data that was matched by the filter. - * - * @hide - */ - public static final String SNIPPET_MIMETYPE = "snippet_mimetype"; - - /** - * The {@link Data#DATA1} field of the data row that was matched by the filter. - * - * @hide - */ - public static final String SNIPPET_DATA1 = "snippet_data1"; - - /** - * The {@link Data#DATA2} field of the data row that was matched by the filter. + * The search snippet constructed according to the SQLite rules, see + * http://www.sqlite.org/fts3.html#snippet + * <p> + * The snippet may contain (parts of) several data elements comprising + * the contact. * * @hide */ - public static final String SNIPPET_DATA2 = "snippet_data2"; + public static final String SNIPPET = "snippet"; - /** - * The {@link Data#DATA3} field of the data row that was matched by the filter. - * - * @hide - */ - public static final String SNIPPET_DATA3 = "snippet_data3"; /** - * The {@link Data#DATA4} field of the data row that was matched by the filter. + * Comma-separated parameters for the generation of the snippet: + * <ul> + * <li>The "start match" text. Default is <b></li> + * <li>The "end match" text. Default is </b></li> + * <li>The "ellipsis" text. Default is <b>...</b></li> + * <li>Maximum number of tokens to include in the snippet. Can be either + * a positive or a negative number: A positive number indicates how many + * tokens can be returned in total. A negative number indicates how many + * tokens can be returned per occurrence of the search terms.</li> + * </ul> * * @hide */ - public static final String SNIPPET_DATA4 = "snippet_data4"; - + public static final String SNIPPET_ARGS_PARAM_KEY = "snippet_args"; } /** diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index 013edd395ae7..3c4bb793b926 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -466,6 +466,12 @@ public final class Downloads { public static final int DESTINATION_SYSTEMCACHE_PARTITION = 5; /** + * This download was completed by the caller (i.e., NOT downloadmanager) + * and caller wants to have this download displayed in Downloads App. + */ + public static final int DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD = 6; + + /** * This download is allowed to run. */ public static final int CONTROL_RUN = 0; @@ -522,6 +528,17 @@ public final class Downloads { } /** + * this method determines if a notification should be displayed for a + * given {@link #COLUMN_VISIBILITY} value + * @param visibility the value of {@link #COLUMN_VISIBILITY}. + * @return true if the notification should be displayed. false otherwise. + */ + public static boolean isNotificationToBeDisplayed(int visibility) { + return visibility == DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED || + visibility == DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION; + } + + /** * Returns whether the download has completed (either with success or * error). */ @@ -561,6 +578,19 @@ public final class Downloads { public static final int STATUS_QUEUED_FOR_WIFI = 196; /** + * This download couldn't be completed due to insufficient storage + * space. Typically, this is because the SD card is full. + */ + public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 198; + + /** + * This download couldn't be completed because no external storage + * device was found. Typically, this is because the SD card is not + * mounted. + */ + public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 199; + + /** * This download has successfully completed. * Warning: there might be other status values that indicate success * in the future. @@ -665,19 +695,6 @@ public final class Downloads { public static final int STATUS_TOO_MANY_REDIRECTS = 497; /** - * This download couldn't be completed due to insufficient storage - * space. Typically, this is because the SD card is full. - */ - public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498; - - /** - * This download couldn't be completed because no external storage - * device was found. Typically, this is because the SD card is not - * mounted. - */ - public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499; - - /** * This download is visible but only shows in the notifications * while it's in progress. */ diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 4c1fb5b7cb2d..b59421e28858 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -21,6 +21,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.content.ContentResolver; import android.content.ContentValues; import android.content.ContentUris; +import android.content.Context; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteException; @@ -2020,4 +2021,29 @@ public final class MediaStore { * the Music app. */ public static final String MEDIA_IGNORE_FILENAME = ".nomedia"; + + /** + * Get the media provider's version. + * Applications that import data from the media provider into their own caches + * can use this to detect that the media provider changed, and reimport data + * as needed. No other assumptions should be made about the meaning of the version. + * @param context Context to use for performing the query. + * @return A version string, or null if the version could not be determined. + */ + public static String getVersion(Context context) { + Cursor c = context.getContentResolver().query( + Uri.parse(CONTENT_AUTHORITY_SLASH + "none/version"), + null, null, null, null); + if (c != null) { + try { + if (c.moveToFirst()) { + return c.getString(0); + } + } finally { + c.close(); + } + } + return null; + } + } diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 1a351122b05e..62f66b6ce799 100755..100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -1723,6 +1723,21 @@ public final class Telephony { public static final String TYPE = "type"; + /** + * The protocol to be used to connect to this APN. + * + * One of the PDP_type values in TS 27.007 section 10.1.1. + * For example, "IP", "IPV6", "IPV4V6", or "PPP". + */ + public static final String PROTOCOL = "protocol"; + + /** + * The protocol to be used to connect to this APN when roaming. + * + * The syntax is the same as protocol. + */ + public static final String ROAMING_PROTOCOL = "roaming_protocol"; + public static final String CURRENT = "current"; } diff --git a/core/java/android/security/MessageDigest.java b/core/java/android/security/MessageDigest.java deleted file mode 100644 index cf2d0fe6e225..000000000000 --- a/core/java/android/security/MessageDigest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.security; - -import java.security.NoSuchAlgorithmException; - -/** - * Base class for producing a message digest from different hash encryptions. - */ -public abstract class MessageDigest -{ - /** - * Returns a digest object of the specified type. - * - * @param algorithm The type of hash function to use. Valid values are - * <em>SHA-1</em> and <em>MD5</em>. - * @return The respective MessageDigest object. Either a - * {@link android.security.Sha1MessageDigest} or - * {@link android.security.Md5MessageDigest} object. - * @throws NoSuchAlgorithmException If an invalid <var>algorithm</var> - * is given. - */ - public static MessageDigest getInstance(String algorithm) - throws NoSuchAlgorithmException - { - if (algorithm == null) { - return null; - } - - if (algorithm.equals("SHA-1")) { - return new Sha1MessageDigest(); - } - else if (algorithm.equals("MD5")) { - return new Md5MessageDigest(); - } - - throw new NoSuchAlgorithmException(); - } - - public abstract void update(byte[] input); - public abstract byte[] digest(); - - /** - * Produces a message digest for the given input. - * - * @param input The message to encrypt. - * @return The digest (hash sum). - */ - public abstract byte[] digest(byte[] input); -} diff --git a/core/java/android/security/Sha1MessageDigest.java b/core/java/android/security/Sha1MessageDigest.java deleted file mode 100644 index aa01fa615199..000000000000 --- a/core/java/android/security/Sha1MessageDigest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.security; - -/** - * Provides the SHA-1 hash encyption. - */ -public class Sha1MessageDigest extends MessageDigest -{ - // ptr to native context - private int mNativeSha1Context; - - public Sha1MessageDigest() - { - init(); - } - - public byte[] digest(byte[] input) - { - update(input); - return digest(); - } - - private native void init(); - public native void update(byte[] input); - public native byte[] digest(); - native public void reset(); -} diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 7456acd33360..3316ea51f701 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -306,6 +306,9 @@ class BluetoothEventLoop { value = str.toString(); } mBluetoothService.setProperty(name, value); + if (name.equals("UUIDs")) { + mBluetoothService.updateBluetoothState(value); + } } else if (name.equals("Powered")) { // bluetoothd has restarted, re-read all our properties. // Note: bluez only sends this property change when it restarts. @@ -648,8 +651,7 @@ class BluetoothEventLoop { } else { Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address); } - } else if (BluetoothUuid.isInputDevice(uuid) && !isOtherInputDeviceConnected(address) && - isKeyboard(address)) { + } else if (BluetoothUuid.isInputDevice(uuid) && !isOtherInputDeviceConnected(address)) { BluetoothInputDevice inputDevice = new BluetoothInputDevice(mContext); authorized = inputDevice.getInputDevicePriority(device) > BluetoothInputDevice.PRIORITY_OFF; @@ -658,8 +660,7 @@ class BluetoothEventLoop { } else { Log.i(TAG, "Rejecting incoming HID connection from " + address); } - } else if (BluetoothUuid.isBnep(uuid) || BluetoothUuid.isNap(uuid) && - mBluetoothService.allowIncomingTethering()){ + } else if (BluetoothUuid.isBnep(uuid) && mBluetoothService.allowIncomingTethering()){ authorized = true; } else { Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address); @@ -668,17 +669,6 @@ class BluetoothEventLoop { return authorized; } - private boolean isKeyboard(String address) { - BluetoothClass btClass = new BluetoothClass(mBluetoothService.getRemoteClass(address)); - int btDeviceClass = btClass.getDeviceClass(); - if (btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD || - btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING) { - return true; - } - log("Incoming Connect: Input device class: " + btDeviceClass + " Not a keyboard"); - return false; - } - private boolean isOtherInputDeviceConnected(String address) { List<BluetoothDevice> devices = mBluetoothService.lookupInputDevicesMatchingStates(new int[] { diff --git a/core/java/android/server/BluetoothInputProfileHandler.java b/core/java/android/server/BluetoothInputProfileHandler.java new file mode 100644 index 000000000000..7ffa5ae2c727 --- /dev/null +++ b/core/java/android/server/BluetoothInputProfileHandler.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.server; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothDeviceProfileState; +import android.bluetooth.BluetoothInputDevice; +import android.bluetooth.BluetoothProfileState; +import android.content.Context; +import android.content.Intent; +import android.os.Message; +import android.provider.Settings; +import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * This handles all the operations on the HID profile. + * All functions are called by BluetoothService, as Bluetooth Service + * is the Service handler for the HID profile. + */ +final class BluetoothInputProfileHandler { + private static final String TAG = "BluetoothInputProfileHandler"; + private static final boolean DBG = true; + + public static BluetoothInputProfileHandler sInstance; + private Context mContext; + private BluetoothService mBluetoothService; + private final HashMap<BluetoothDevice, Integer> mInputDevices; + private final BluetoothProfileState mHidProfileState; + + private BluetoothInputProfileHandler(Context context, BluetoothService service) { + mContext = context; + mBluetoothService = service; + mInputDevices = new HashMap<BluetoothDevice, Integer>(); + mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID); + mHidProfileState.start(); + } + + static synchronized BluetoothInputProfileHandler getInstance(Context context, + BluetoothService service) { + if (sInstance == null) sInstance = new BluetoothInputProfileHandler(context, service); + return sInstance; + } + + synchronized boolean connectInputDevice(BluetoothDevice device, + BluetoothDeviceProfileState state) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + if (objectPath == null || + getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED || + getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) { + return false; + } + if (state != null) { + Message msg = new Message(); + msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING; + msg.obj = state; + mHidProfileState.sendMessage(msg); + return true; + } + return false; + } + + synchronized boolean connectInputDeviceInternal(BluetoothDevice device) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING); + if (!mBluetoothService.connectInputDeviceNative(objectPath)) { + handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED); + return false; + } + return true; + } + + synchronized boolean disconnectInputDevice(BluetoothDevice device, + BluetoothDeviceProfileState state) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + if (objectPath == null || + getInputDeviceState(device) == BluetoothInputDevice.STATE_DISCONNECTED) { + return false; + } + if (state != null) { + Message msg = new Message(); + msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING; + msg.obj = state; + mHidProfileState.sendMessage(msg); + return true; + } + return false; + } + + synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING); + if (!mBluetoothService.disconnectInputDeviceNative(objectPath)) { + handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED); + return false; + } + return true; + } + + synchronized int getInputDeviceState(BluetoothDevice device) { + if (mInputDevices.get(device) == null) { + return BluetoothInputDevice.STATE_DISCONNECTED; + } + return mInputDevices.get(device); + } + + synchronized List<BluetoothDevice> getConnectedInputDevices() { + List<BluetoothDevice> devices = lookupInputDevicesMatchingStates( + new int[] {BluetoothInputDevice.STATE_CONNECTED}); + return devices; + } + + synchronized int getInputDevicePriority(BluetoothDevice device) { + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), + BluetoothInputDevice.PRIORITY_UNDEFINED); + } + + synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) { + if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) { + return false; + } + return Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), + priority); + } + + synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) { + List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>(); + + for (BluetoothDevice device: mInputDevices.keySet()) { + int inputDeviceState = getInputDeviceState(device); + for (int state : states) { + if (state == inputDeviceState) { + inputDevices.add(device); + break; + } + } + } + return inputDevices; + } + + private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) { + int prevState; + if (mInputDevices.get(device) == null) { + prevState = BluetoothInputDevice.STATE_DISCONNECTED; + } else { + prevState = mInputDevices.get(device); + } + if (prevState == state) return; + + mInputDevices.put(device, state); + + if (getInputDevicePriority(device) > + BluetoothInputDevice.PRIORITY_OFF && + state == BluetoothInputDevice.STATE_CONNECTING || + state == BluetoothInputDevice.STATE_CONNECTED) { + // We have connected or attempting to connect. + // Bump priority + setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT); + } + + Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState); + intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state); + mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); + + debugLog("InputDevice state : device: " + device + " State:" + prevState + "->" + state); + mBluetoothService.sendConnectionStateChange(device, state, prevState); + } + + synchronized void handleInputDevicePropertyChange(String address, boolean connected) { + int state = connected ? BluetoothInputDevice.STATE_CONNECTED : + BluetoothInputDevice.STATE_DISCONNECTED; + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(address); + handleInputDeviceStateChange(device, state); + } + + synchronized void setInitialInputDevicePriority(BluetoothDevice device, int state) { + switch (state) { + case BluetoothDevice.BOND_BONDED: + if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) { + setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON); + } + break; + case BluetoothDevice.BOND_NONE: + setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED); + break; + } + } + + private static void debugLog(String msg) { + if (DBG) Log.d(TAG, msg); + } + + private static void errorLog(String msg) { + Log.e(TAG, msg); + } +} diff --git a/core/java/android/server/BluetoothPanProfileHandler.java b/core/java/android/server/BluetoothPanProfileHandler.java new file mode 100644 index 000000000000..fb964396e4fc --- /dev/null +++ b/core/java/android/server/BluetoothPanProfileHandler.java @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.server; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothPan; +import android.bluetooth.BluetoothTetheringDataTracker; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources.NotFoundException; +import android.net.ConnectivityManager; +import android.net.InterfaceConfiguration; +import android.net.LinkAddress; +import android.os.IBinder; +import android.os.INetworkManagementService; +import android.os.ServiceManager; +import android.util.Log; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * This handles the PAN profile. All calls into this are made + * from Bluetooth Service. + */ +final class BluetoothPanProfileHandler { + private static final String TAG = "BluetoothPanProfileHandler"; + private static final boolean DBG = true; + + private ArrayList<String> mBluetoothIfaceAddresses; + private int mMaxPanDevices; + + private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1"; + private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5; + private static final int BLUETOOTH_PREFIX_LENGTH = 24; + public static BluetoothPanProfileHandler sInstance; + private final HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices; + private boolean mTetheringOn; + private Context mContext; + private BluetoothService mBluetoothService; + + private BluetoothPanProfileHandler(Context context, BluetoothService service) { + mContext = context; + mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>(); + mBluetoothService = service; + mTetheringOn = false; + mBluetoothIfaceAddresses = new ArrayList<String>(); + try { + mMaxPanDevices = context.getResources().getInteger( + com.android.internal.R.integer.config_max_pan_devices); + } catch (NotFoundException e) { + mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS; + } + } + + static synchronized BluetoothPanProfileHandler getInstance(Context context, + BluetoothService service) { + if (sInstance == null) sInstance = new BluetoothPanProfileHandler(context, service); + return sInstance; + } + + synchronized boolean isTetheringOn() { + return mTetheringOn; + } + + synchronized boolean allowIncomingTethering() { + if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices) + return true; + return false; + } + + private BroadcastReceiver mTetheringReceiver = null; + + synchronized void setBluetoothTethering(boolean value) { + if (!value) { + disconnectPanServerDevices(); + } + + if (mBluetoothService.getBluetoothState() != BluetoothAdapter.STATE_ON && value) { + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + mTetheringReceiver = new BroadcastReceiver() { + @Override + public synchronized void onReceive(Context context, Intent intent) { + if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF) + == BluetoothAdapter.STATE_ON) { + mTetheringOn = true; + mContext.unregisterReceiver(mTetheringReceiver); + } + } + }; + mContext.registerReceiver(mTetheringReceiver, filter); + } else { + mTetheringOn = value; + } + } + + synchronized int getPanDeviceState(BluetoothDevice device) { + BluetoothPanDevice panDevice = mPanDevices.get(device); + if (panDevice == null) { + return BluetoothPan.STATE_DISCONNECTED; + } + return panDevice.mState; + } + + synchronized boolean connectPanDevice(BluetoothDevice device) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + if (DBG) Log.d(TAG, "connect PAN(" + objectPath + ")"); + if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) { + errorLog(device + " already connected to PAN"); + } + + int connectedCount = 0; + for (BluetoothDevice panDevice: mPanDevices.keySet()) { + if (getPanDeviceState(panDevice) == BluetoothPan.STATE_CONNECTED) { + connectedCount ++; + } + } + if (connectedCount > 8) { + debugLog(device + " could not connect to PAN because 8 other devices are" + + "already connected"); + return false; + } + + handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING, + BluetoothPan.LOCAL_PANU_ROLE); + if (mBluetoothService.connectPanDeviceNative(objectPath, "nap")) { + debugLog("connecting to PAN"); + return true; + } else { + handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED, + BluetoothPan.LOCAL_PANU_ROLE); + errorLog("could not connect to PAN"); + return false; + } + } + + private synchronized boolean disconnectPanServerDevices() { + debugLog("disconnect all PAN devices"); + + for (BluetoothDevice device: mPanDevices.keySet()) { + BluetoothPanDevice panDevice = mPanDevices.get(device); + int state = panDevice.mState; + if (state == BluetoothPan.STATE_CONNECTED && + panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + + handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING, + panDevice.mLocalRole); + + if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath, + device.getAddress(), + panDevice.mIfaceAddr)) { + errorLog("could not disconnect Pan Server Device "+device.getAddress()); + + // Restore prev state + handlePanDeviceStateChange(device, state, + panDevice.mLocalRole); + + return false; + } + } + } + return true; + } + + synchronized List<BluetoothDevice> getConnectedPanDevices() { + List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); + + for (BluetoothDevice device: mPanDevices.keySet()) { + if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) { + devices.add(device); + } + } + return devices; + } + + synchronized boolean disconnectPanDevice(BluetoothDevice device) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + debugLog("disconnect PAN(" + objectPath + ")"); + + int state = getPanDeviceState(device); + if (state != BluetoothPan.STATE_CONNECTED) { + debugLog(device + " already disconnected from PAN"); + return false; + } + + BluetoothPanDevice panDevice = mPanDevices.get(device); + + if (panDevice == null) { + errorLog("No record for this Pan device:" + device); + return false; + } + + handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING, + panDevice.mLocalRole); + if (panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) { + if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath, device.getAddress(), + panDevice.mIface)) { + // Restore prev state, this shouldn't happen + handlePanDeviceStateChange(device, state, panDevice.mLocalRole); + return false; + } + } else { + if (!mBluetoothService.disconnectPanDeviceNative(objectPath)) { + // Restore prev state, this shouldn't happen + handlePanDeviceStateChange(device, state, panDevice.mLocalRole); + return false; + } + } + return true; + } + + synchronized void handlePanDeviceStateChange(BluetoothDevice device, + String iface, int state, int role) { + int prevState; + String ifaceAddr = null; + BluetoothPanDevice panDevice = mPanDevices.get(device); + + if (panDevice == null) { + prevState = BluetoothPan.STATE_DISCONNECTED; + } else { + prevState = panDevice.mState; + ifaceAddr = panDevice.mIfaceAddr; + } + if (prevState == state) return; + + if (role == BluetoothPan.LOCAL_NAP_ROLE) { + if (state == BluetoothPan.STATE_CONNECTED) { + ifaceAddr = enableTethering(iface); + if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface"); + } else if (state == BluetoothPan.STATE_DISCONNECTED) { + if (ifaceAddr != null) { + mBluetoothIfaceAddresses.remove(ifaceAddr); + ifaceAddr = null; + } + } + } else { + // PANU Role = reverse Tether + if (state == BluetoothPan.STATE_CONNECTED) { + BluetoothTetheringDataTracker.getInstance().startReverseTether(iface, device); + } else if (state == BluetoothPan.STATE_DISCONNECTED && + (prevState == BluetoothPan.STATE_CONNECTED || + prevState == BluetoothPan.STATE_DISCONNECTING)) { + BluetoothTetheringDataTracker.getInstance().stopReverseTether(panDevice.mIface); + } + } + + if (panDevice == null) { + panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, role); + mPanDevices.put(device, panDevice); + } else { + panDevice.mState = state; + panDevice.mIfaceAddr = ifaceAddr; + panDevice.mLocalRole = role; + } + + if (state == BluetoothPan.STATE_DISCONNECTED) { + mPanDevices.remove(device); + } + + Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState); + intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state); + intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, role); + mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); + + debugLog("Pan Device state : device: " + device + " State:" + prevState + "->" + state); + mBluetoothService.sendConnectionStateChange(device, state, prevState); + } + + synchronized void handlePanDeviceStateChange(BluetoothDevice device, + int state, int role) { + handlePanDeviceStateChange(device, null, state, role); + } + + private class BluetoothPanDevice { + private int mState; + private String mIfaceAddr; + private String mIface; + private int mLocalRole; // Which local role is this PAN device bound to + + BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) { + mState = state; + mIfaceAddr = ifaceAddr; + mIface = iface; + mLocalRole = localRole; + } + } + + private String createNewTetheringAddressLocked() { + if (getConnectedPanDevices().size() == mMaxPanDevices) { + debugLog ("Max PAN device connections reached"); + return null; + } + String address = BLUETOOTH_IFACE_ADDR_START; + while (true) { + if (mBluetoothIfaceAddresses.contains(address)) { + String[] addr = address.split("\\."); + Integer newIp = Integer.parseInt(addr[2]) + 1; + address = address.replace(addr[2], newIp.toString()); + } else { + break; + } + } + mBluetoothIfaceAddresses.add(address); + return address; + } + + // configured when we start tethering + private synchronized String enableTethering(String iface) { + debugLog("updateTetherState:" + iface); + + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + ConnectivityManager cm = + (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs(); + + // bring toggle the interfaces + String[] currentIfaces = new String[0]; + try { + currentIfaces = service.listInterfaces(); + } catch (Exception e) { + Log.e(TAG, "Error listing Interfaces :" + e); + return null; + } + + boolean found = false; + for (String currIface: currentIfaces) { + if (currIface.equals(iface)) { + found = true; + break; + } + } + + if (!found) return null; + + String address = createNewTetheringAddressLocked(); + if (address == null) return null; + + InterfaceConfiguration ifcg = null; + try { + ifcg = service.getInterfaceConfig(iface); + if (ifcg != null) { + InetAddress addr = null; + if (ifcg.addr == null || (addr = ifcg.addr.getAddress()) == null || + addr.equals(InetAddress.getByName("0.0.0.0")) || + addr.equals(InetAddress.getByName("::0"))) { + addr = InetAddress.getByName(address); + } + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); + ifcg.addr = new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH); + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", ""); + ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," "); + service.setInterfaceConfig(iface, ifcg); + if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { + Log.e(TAG, "Error tethering "+iface); + } + } + } catch (Exception e) { + Log.e(TAG, "Error configuring interface " + iface + ", :" + e); + return null; + } + return address; + } + + private static void debugLog(String msg) { + if (DBG) Log.d(TAG, msg); + } + + private static void errorLog(String msg) { + Log.e(TAG, msg); + } +} diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 115370b2f0cb..a295de5660f6 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -24,17 +24,17 @@ package android.server; +import com.android.internal.app.IBatteryStats; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDeviceProfileState; import android.bluetooth.BluetoothHeadset; -import android.bluetooth.BluetoothInputDevice; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfileState; import android.bluetooth.BluetoothSocket; -import android.bluetooth.BluetoothTetheringDataTracker; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothCallback; @@ -44,13 +44,9 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.res.Resources.NotFoundException; -import android.net.ConnectivityManager; -import android.net.InterfaceConfiguration; import android.os.Binder; import android.os.Handler; import android.os.IBinder; -import android.os.INetworkManagementService; import android.os.Message; import android.os.ParcelUuid; import android.os.RemoteException; @@ -59,8 +55,6 @@ import android.provider.Settings; import android.util.Log; import android.util.Pair; -import com.android.internal.app.IBatteryStats; - import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -75,7 +69,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -95,9 +88,8 @@ public class BluetoothService extends IBluetooth.Stub { private int mBluetoothState; private boolean mRestart = false; // need to call enable() after disable() private boolean mIsDiscovering; - private boolean mTetheringOn; - private int[] mAdapterSdpUuids; private int[] mAdapterSdpHandles; + private ParcelUuid[] mAdapterUuids; private BluetoothAdapter mAdapter; // constant after init() private final BondState mBondState = new BondState(); // local cache of bondings @@ -105,7 +97,7 @@ public class BluetoothService extends IBluetooth.Stub { private final Context mContext; private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; - private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; + static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr"; private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin"; @@ -115,8 +107,7 @@ public class BluetoothService extends IBluetooth.Stub { private static final int MESSAGE_FINISH_DISABLE = 1; private static final int MESSAGE_UUID_INTENT = 2; - private static final int MESSAGE_DISCOVERABLE_TIMEOUT = 3; - private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 4; + private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3; // The time (in millisecs) to delay the pairing attempt after the first // auto pairing attempt fails. We use an exponential delay with @@ -125,13 +116,6 @@ public class BluetoothService extends IBluetooth.Stub { private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000; private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000; - private ArrayList<String> mBluetoothIfaceAddresses; - private int mMaxPanDevices; - - private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1"; - private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5; - private static final String BLUETOOTH_NETMASK = "255.255.255.0"; - // The timeout used to sent the UUIDs Intent // This timeout should be greater than the page timeout private static final int UUID_INTENT_DELAY = 6000; @@ -155,11 +139,8 @@ public class BluetoothService extends IBluetooth.Stub { private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState; private final BluetoothProfileState mA2dpProfileState; private final BluetoothProfileState mHfpProfileState; - private final BluetoothProfileState mHidProfileState; private BluetoothA2dpService mA2dpService; - private final HashMap<BluetoothDevice, Integer> mInputDevices; - private final HashMap<BluetoothDevice, Pair<Integer, String>> mPanDevices; private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData; private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0; @@ -167,9 +148,9 @@ public class BluetoothService extends IBluetooth.Stub { private static String mDockAddress; private String mDockPin; - private String mIface; - private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED; + private BluetoothPanProfileHandler mBluetoothPanProfileHandler; + private BluetoothInputProfileHandler mBluetoothInputProfileHandler; private static class RemoteService { public String address; @@ -218,7 +199,7 @@ public class BluetoothService extends IBluetooth.Stub { mBluetoothState = BluetoothAdapter.STATE_OFF; mIsDiscovering = false; - mTetheringOn = false; + mAdapterProperties = new HashMap<String, String>(); mDeviceProperties = new HashMap<String, Map<String,String>>(); @@ -230,27 +211,17 @@ public class BluetoothService extends IBluetooth.Stub { mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>(); mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP); mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP); - mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID); - - mBluetoothIfaceAddresses = new ArrayList<String>(); - try { - mMaxPanDevices = context.getResources().getInteger( - com.android.internal.R.integer.config_max_pan_devices); - } catch (NotFoundException e) { - mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS; - } mHfpProfileState.start(); mA2dpProfileState.start(); - mHidProfileState.start(); IntentFilter filter = new IntentFilter(); registerForAirplaneMode(filter); filter.addAction(Intent.ACTION_DOCK_EVENT); mContext.registerReceiver(mReceiver, filter); - mInputDevices = new HashMap<BluetoothDevice, Integer>(); - mPanDevices = new HashMap<BluetoothDevice, Pair<Integer, String>>(); + mBluetoothInputProfileHandler = BluetoothInputProfileHandler.getInstance(mContext, this); + mBluetoothPanProfileHandler = BluetoothPanProfileHandler.getInstance(mContext, this); } public static synchronized String readDockBluetoothAddress() { @@ -439,6 +410,8 @@ public class BluetoothService extends IBluetooth.Stub { mProfilesConnecting = 0; mProfilesDisconnecting = 0; mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED; + mAdapterUuids = null; + mAdapterSdpHandles = null; if (saveSetting) { persistBluetoothOnSetting(false); @@ -535,15 +508,6 @@ public class BluetoothService extends IBluetooth.Stub { makeServiceChannelCallbacks(address); } break; - case MESSAGE_DISCOVERABLE_TIMEOUT: - int mode = msg.arg1; - if (isEnabledInternal()) { - // TODO: Switch back to the previous scan mode - // This is ok for now, because we only use - // CONNECTABLE and CONNECTABLE_DISCOVERABLE - setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, -1); - } - break; case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY: address = (String)msg.obj; if (address != null) { @@ -589,7 +553,6 @@ public class BluetoothService extends IBluetooth.Stub { } } - if (res) { if (!setupNativeDataNative()) { return; @@ -597,39 +560,79 @@ public class BluetoothService extends IBluetooth.Stub { if (mSaveSetting) { persistBluetoothOnSetting(true); } - //Register SDP records. - if (mContext.getResources(). - getBoolean(com.android.internal.R.bool.config_voice_capable)) { - int[] uuids = { - BluetoothUuid.getServiceIdentifierFromParcelUuid( - BluetoothUuid.Handsfree_AG), - BluetoothUuid.getServiceIdentifierFromParcelUuid( - BluetoothUuid.HSP_AG), - BluetoothUuid.getServiceIdentifierFromParcelUuid( - BluetoothUuid.PBAP_PSE), - BluetoothUuid.getServiceIdentifierFromParcelUuid( - BluetoothUuid.ObexObjectPush), - }; - mAdapterSdpUuids = uuids; - - } else { - int[] uuids = { - BluetoothUuid.getServiceIdentifierFromParcelUuid( - BluetoothUuid.HSP_AG), - BluetoothUuid.getServiceIdentifierFromParcelUuid( - BluetoothUuid.ObexObjectPush), - }; - mAdapterSdpUuids = uuids; - } - - mAdapterSdpHandles = addReservedServiceRecordsNative(mAdapterSdpUuids); - setBluetoothTetheringNative(true, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE); mIsDiscovering = false; mBondState.readAutoPairingData(); mBondState.loadBondState(); initProfileState(); + // This should be the last step of the the enable thread. + // Because this adds SDP records which asynchronously + // broadcasts the Bluetooth On State in updateBluetoothState. + // So we want all internal state setup before this. + updateSdpRecords(); + } else { + setBluetoothState(BluetoothAdapter.STATE_OFF); + } + mEnableThread = null; + } + } + + private synchronized void addReservedSdpRecords(final ArrayList<ParcelUuid> uuids) { + //Register SDP records. + int[] svcIdentifiers = new int[uuids.size()]; + for (int i = 0; i < uuids.size(); i++) { + svcIdentifiers[i] = BluetoothUuid.getServiceIdentifierFromParcelUuid(uuids.get(i)); + } + mAdapterSdpHandles = addReservedServiceRecordsNative(svcIdentifiers); + } + + private synchronized void updateSdpRecords() { + ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>(); + + // Add the default records + uuids.add(BluetoothUuid.HSP_AG); + uuids.add(BluetoothUuid.ObexObjectPush); + + if (mContext.getResources(). + getBoolean(com.android.internal.R.bool.config_voice_capable)) { + uuids.add(BluetoothUuid.Handsfree_AG); + uuids.add(BluetoothUuid.PBAP_PSE); + } + + // Add SDP records for profiles maintained by Android userspace + addReservedSdpRecords(uuids); + + // Enable profiles maintained by Bluez userspace. + setBluetoothTetheringNative(true, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE); + + // Add SDP records for profiles maintained by Bluez userspace + uuids.add(BluetoothUuid.AudioSource); + uuids.add(BluetoothUuid.AvrcpTarget); + uuids.add(BluetoothUuid.NAP); + + // Cannot cast uuids.toArray directly since ParcelUuid is parcelable + mAdapterUuids = new ParcelUuid[uuids.size()]; + for (int i = 0; i < uuids.size(); i++) { + mAdapterUuids[i] = uuids.get(i); + } + } + + /** + * This function is called from Bluetooth Event Loop when onPropertyChanged + * for adapter comes in with UUID property. + * @param uuidsThe uuids of adapter as reported by Bluez. + */ + synchronized void updateBluetoothState(String uuids) { + if (mBluetoothState == BluetoothAdapter.STATE_TURNING_ON) { + ParcelUuid[] adapterUuids = convertStringToParcelUuid(uuids); + + if (mAdapterUuids != null && + BluetoothUuid.containsAllUuids(adapterUuids, mAdapterUuids)) { + setBluetoothState(BluetoothAdapter.STATE_ON); + String[] propVal = {"Pairable", getProperty("Pairable")}; + mEventLoop.onPropertyChanged(propVal); + // Log bluetooth on to battery stats. long ident = Binder.clearCallingIdentity(); try { @@ -638,24 +641,11 @@ public class BluetoothService extends IBluetooth.Stub { } finally { Binder.restoreCallingIdentity(ident); } - } - mEnableThread = null; - - setBluetoothState(res ? - BluetoothAdapter.STATE_ON : - BluetoothAdapter.STATE_OFF); - - if (res) { - // Update mode - String[] propVal = {"Pairable", getProperty("Pairable")}; - mEventLoop.onPropertyChanged(propVal); - } - - if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) { - disable(false); + if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) { + disable(false); + } } - } } @@ -815,7 +805,8 @@ public class BluetoothService extends IBluetooth.Stub { // HID is handled by BluetoothService, other profiles // will be handled by their respective services. - setInitialInputDevicePriority(mAdapter.getRemoteDevice(address), state); + mBluetoothInputProfileHandler.setInitialInputDevicePriority( + mAdapter.getRemoteDevice(address), state); if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" + reason + ")"); @@ -1163,21 +1154,16 @@ public class BluetoothService extends IBluetooth.Stub { switch (mode) { case BluetoothAdapter.SCAN_MODE_NONE: - mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT); pairable = false; discoverable = false; break; case BluetoothAdapter.SCAN_MODE_CONNECTABLE: - mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT); pairable = true; discoverable = false; break; case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: - mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT); pairable = true; discoverable = true; - Message msg = mHandler.obtainMessage(MESSAGE_DISCOVERABLE_TIMEOUT); - mHandler.sendMessageDelayed(msg, duration * 1000); if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds"); break; default: @@ -1216,7 +1202,10 @@ public class BluetoothService extends IBluetooth.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); String value = getProperty("UUIDs"); if (value == null) return null; + return convertStringToParcelUuid(value); + } + private synchronized ParcelUuid[] convertStringToParcelUuid(String value) { String[] uuidStrings = null; // The UUIDs are stored as a "," separated string. uuidStrings = value.split(","); @@ -1226,7 +1215,6 @@ public class BluetoothService extends IBluetooth.Stub { uuids[i] = ParcelUuid.fromString(uuidStrings[i]); } return uuids; - } @@ -1465,431 +1453,6 @@ public class BluetoothService extends IBluetooth.Stub { return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address); } - public synchronized boolean isTetheringOn() { - return mTetheringOn; - } - - /*package*/ synchronized boolean allowIncomingTethering() { - if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices) - return true; - return false; - } - - private BroadcastReceiver mTetheringReceiver = null; - - public synchronized void setBluetoothTethering(boolean value) { - if (!value) { - disconnectPan(); - } - - if (getBluetoothState() != BluetoothAdapter.STATE_ON && value) { - IntentFilter filter = new IntentFilter(); - filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); - mTetheringReceiver = new BroadcastReceiver() { - @Override - public synchronized void onReceive(Context context, Intent intent) { - if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF) - == BluetoothAdapter.STATE_ON) { - mTetheringOn = true; - mContext.unregisterReceiver(mTetheringReceiver); - } - } - }; - mContext.registerReceiver(mTetheringReceiver, filter); - } else { - mTetheringOn = value; - } - } - - public synchronized int getPanDeviceState(BluetoothDevice device) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - - Pair<Integer, String> panDevice = mPanDevices.get(device); - if (panDevice == null) { - return BluetoothPan.STATE_DISCONNECTED; - } - return panDevice.first; - } - - public synchronized boolean connectPanDevice(BluetoothDevice device) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - - String objectPath = getObjectPathFromAddress(device.getAddress()); - if (DBG) log("connect PAN(" + objectPath + ")"); - if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) { - log (device + " already connected to PAN"); - } - - int connectedCount = 0; - for (BluetoothDevice panDevice: mPanDevices.keySet()) { - if (getPanDeviceState(panDevice) == BluetoothPan.STATE_CONNECTED) { - connectedCount ++; - } - } - if (connectedCount > 8) { - log (device + " could not connect to PAN because 8 other devices are already connected"); - return false; - } - - handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING, - BluetoothPan.LOCAL_PANU_ROLE); - if (connectPanDeviceNative(objectPath, "nap")) { - log ("connecting to PAN"); - return true; - } else { - handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED, - BluetoothPan.LOCAL_PANU_ROLE); - log ("could not connect to PAN"); - return false; - } - } - - private synchronized boolean disconnectPan() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (DBG) log("disconnect all PAN devices"); - - for (BluetoothDevice device: mPanDevices.keySet()) { - if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) { - if (!disconnectPanDevice(device)) { - log ("could not disconnect Pan Device "+device.getAddress()); - return false; - } - } - } - return true; - } - - public synchronized List<BluetoothDevice> getConnectedPanDevices() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - - List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); - - for (BluetoothDevice device: mPanDevices.keySet()) { - if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) { - devices.add(device); - } - } - return devices; - } - - public synchronized boolean disconnectPanDevice(BluetoothDevice device) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - String objectPath = getObjectPathFromAddress(device.getAddress()); - if (DBG) log("disconnect PAN(" + objectPath + ")"); - if (getPanDeviceState(device) != BluetoothPan.STATE_CONNECTED) { - log (device + " already disconnected from PAN"); - } - handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING, - BluetoothPan.LOCAL_PANU_ROLE); - return disconnectPanDeviceNative(objectPath); - } - - /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device, - String iface, - int state, - int role) { - int prevState; - String ifaceAddr = null; - - if (mPanDevices.get(device) == null) { - prevState = BluetoothPan.STATE_DISCONNECTED; - } else { - prevState = mPanDevices.get(device).first; - ifaceAddr = mPanDevices.get(device).second; - } - if (prevState == state) return; - - if (role == BluetoothPan.LOCAL_NAP_ROLE) { - if (state == BluetoothPan.STATE_CONNECTED) { - ifaceAddr = enableTethering(iface); - if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface"); - } else if (state == BluetoothPan.STATE_DISCONNECTED) { - if (ifaceAddr != null) { - mBluetoothIfaceAddresses.remove(ifaceAddr); - ifaceAddr = null; - } - } - } else { - // PANU Role = reverse Tether - if (state == BluetoothPan.STATE_CONNECTED) { - mIface = iface; - BluetoothTetheringDataTracker.getInstance().startReverseTether(iface, device); - } else if (state == BluetoothPan.STATE_DISCONNECTED && - (prevState == BluetoothPan.STATE_CONNECTED || - prevState == BluetoothPan.STATE_DISCONNECTING)) { - BluetoothTetheringDataTracker.getInstance().stopReverseTether(mIface); - } - } - - Pair<Integer, String> value = new Pair<Integer, String>(state, ifaceAddr); - mPanDevices.put(device, value); - - Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED); - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); - intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState); - intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state); - intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, role); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - - if (DBG) log("Pan Device state : device: " + device + " State:" + prevState + "->" + state); - sendConnectionStateChange(device, state, prevState); - } - - /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device, - int state, int role) { - handlePanDeviceStateChange(device, null, state, role); - } - - private String createNewTetheringAddressLocked() { - if (getConnectedPanDevices().size() == mMaxPanDevices) { - log("Max PAN device connections reached"); - return null; - } - String address = BLUETOOTH_IFACE_ADDR_START; - while (true) { - if (mBluetoothIfaceAddresses.contains(address)) { - String[] addr = address.split("\\."); - Integer newIp = Integer.parseInt(addr[2]) + 1; - address = address.replace(addr[2], newIp.toString()); - } else { - break; - } - } - mBluetoothIfaceAddresses.add(address); - return address; - } - - // configured when we start tethering - private synchronized String enableTethering(String iface) { - log("updateTetherState:" + iface); - - IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); - INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); - ConnectivityManager cm = - (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs(); - - // bring toggle the interfaces - String[] currentIfaces = new String[0]; - try { - currentIfaces = service.listInterfaces(); - } catch (Exception e) { - Log.e(TAG, "Error listing Interfaces :" + e); - return null; - } - - boolean found = false; - for (String currIface: currentIfaces) { - if (currIface.equals(iface)) { - found = true; - break; - } - } - - if (!found) return null; - - String address = createNewTetheringAddressLocked(); - if (address == null) return null; - - InterfaceConfiguration ifcg = null; - try { - ifcg = service.getInterfaceConfig(iface); - if (ifcg != null) { - ifcg.mask = InetAddress.getByName(BLUETOOTH_NETMASK); - - if (ifcg.addr == null || ifcg.addr.equals(InetAddress.getByName("0.0.0.0"))) { - ifcg.addr = InetAddress.getByName(address); - ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); - } - ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", ""); - ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," "); - service.setInterfaceConfig(iface, ifcg); - if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { - Log.e(TAG, "Error tethering "+iface); - } - } - } catch (Exception e) { - Log.e(TAG, "Error configuring interface " + iface + ", :" + e); - return null; - } - return address; - } - - public synchronized boolean connectInputDevice(BluetoothDevice device) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - - String objectPath = getObjectPathFromAddress(device.getAddress()); - if (objectPath == null || - getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED || - getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) { - return false; - } - - BluetoothClass btClass = new BluetoothClass(getRemoteClass(device.getAddress())); - int btDeviceClass = btClass.getDeviceClass(); - if (btDeviceClass != BluetoothClass.Device.PERIPHERAL_KEYBOARD && - btDeviceClass != BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING) { - log("Input device btDeviceClass: " + btDeviceClass + " Not a keyboard"); - return false; - } - - BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); - if (state != null) { - Message msg = new Message(); - msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING; - msg.obj = state; - mHidProfileState.sendMessage(msg); - return true; - } - return false; - } - - public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) { - String objectPath = getObjectPathFromAddress(device.getAddress()); - handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING); - if (!connectInputDeviceNative(objectPath)) { - handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED); - return false; - } - return true; - } - - public synchronized boolean disconnectInputDevice(BluetoothDevice device) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - - String objectPath = getObjectPathFromAddress(device.getAddress()); - if (objectPath == null || - getInputDeviceState(device) == BluetoothInputDevice.STATE_DISCONNECTED) { - return false; - } - BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); - if (state != null) { - Message msg = new Message(); - msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING; - msg.obj = state; - mHidProfileState.sendMessage(msg); - return true; - } - return false; - } - - public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) { - String objectPath = getObjectPathFromAddress(device.getAddress()); - handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING); - if (!disconnectInputDeviceNative(objectPath)) { - handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED); - return false; - } - return true; - } - - public synchronized int getInputDeviceState(BluetoothDevice device) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - - if (mInputDevices.get(device) == null) { - return BluetoothInputDevice.STATE_DISCONNECTED; - } - return mInputDevices.get(device); - } - - public synchronized List<BluetoothDevice> getConnectedInputDevices() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - List<BluetoothDevice> devices = lookupInputDevicesMatchingStates( - new int[] {BluetoothInputDevice.STATE_CONNECTED}); - return devices; - } - - public synchronized int getInputDevicePriority(BluetoothDevice device) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), - BluetoothInputDevice.PRIORITY_UNDEFINED); - } - - public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) { - return false; - } - return Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), - priority); - } - - /*package*/synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) { - List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>(); - - for (BluetoothDevice device: mInputDevices.keySet()) { - int inputDeviceState = getInputDeviceState(device); - for (int state : states) { - if (state == inputDeviceState) { - inputDevices.add(device); - break; - } - } - } - return inputDevices; - } - - private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) { - int prevState; - if (mInputDevices.get(device) == null) { - prevState = BluetoothInputDevice.STATE_DISCONNECTED; - } else { - prevState = mInputDevices.get(device); - } - if (prevState == state) return; - - mInputDevices.put(device, state); - - if (getInputDevicePriority(device) > - BluetoothInputDevice.PRIORITY_OFF && - state == BluetoothInputDevice.STATE_CONNECTING || - state == BluetoothInputDevice.STATE_CONNECTED) { - // We have connected or attempting to connect. - // Bump priority - setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT); - } - - Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); - intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState); - intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - - if (DBG) log("InputDevice state : device: " + device + " State:" + prevState + "->" + state); - sendConnectionStateChange(device, state, prevState); - } - - /*package*/ void handleInputDevicePropertyChange(String address, boolean connected) { - int state = connected ? BluetoothInputDevice.STATE_CONNECTED : - BluetoothInputDevice.STATE_DISCONNECTED; - BluetoothDevice device = mAdapter.getRemoteDevice(address); - handleInputDeviceStateChange(device, state); - } - - private void setInitialInputDevicePriority(BluetoothDevice device, int state) { - switch (state) { - case BluetoothDevice.BOND_BONDED: - if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) { - setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON); - } - break; - case BluetoothDevice.BOND_NONE: - setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED); - break; - } - } - - /*package*/ boolean isRemoteDeviceInCache(String address) { - return (mDeviceProperties.get(address) != null); - } - /*package*/ String[] getRemoteDeviceProperties(String address) { if (!isEnabledInternal()) return null; @@ -2674,6 +2237,114 @@ public class BluetoothService extends IBluetooth.Stub { if (!result) log("Set Link Timeout to:" + num_slots + " slots failed"); } + /**** Handlers for PAN Profile ****/ + + public synchronized boolean isTetheringOn() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothPanProfileHandler.isTetheringOn(); + } + + /*package*/ synchronized boolean allowIncomingTethering() { + return mBluetoothPanProfileHandler.allowIncomingTethering(); + } + + public synchronized void setBluetoothTethering(boolean value) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + mBluetoothPanProfileHandler.setBluetoothTethering(value); + } + + public synchronized int getPanDeviceState(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothPanProfileHandler.getPanDeviceState(device); + } + + public synchronized boolean connectPanDevice(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + return mBluetoothPanProfileHandler.connectPanDevice(device); + } + + public synchronized List<BluetoothDevice> getConnectedPanDevices() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothPanProfileHandler.getConnectedPanDevices(); + } + + public synchronized boolean disconnectPanDevice(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + return mBluetoothPanProfileHandler.disconnectPanDevice(device); + } + + /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device, + String iface, + int state, + int role) { + mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role); + } + + /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device, + int state, int role) { + mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role); + } + + /**** Handlers for Input Device Profile ****/ + + public synchronized boolean connectInputDevice(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); + return mBluetoothInputProfileHandler.connectInputDevice(device, state); + } + + public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) { + return mBluetoothInputProfileHandler.connectInputDeviceInternal(device); + } + + public synchronized boolean disconnectInputDevice(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); + return mBluetoothInputProfileHandler.disconnectInputDevice(device, state); + } + + public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) { + return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device); + } + + public synchronized int getInputDeviceState(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothInputProfileHandler.getInputDeviceState(device); + + } + + public synchronized List<BluetoothDevice> getConnectedInputDevices() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothInputProfileHandler.getConnectedInputDevices(); + } + + public synchronized int getInputDevicePriority(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothInputProfileHandler.getInputDevicePriority(device); + } + + public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority); + } + + /*package*/synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) { + return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states); + } + + /*package*/ synchronized void handleInputDevicePropertyChange(String address, boolean connected) { + mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected); + } + + /*package*/ boolean isRemoteDeviceInCache(String address) { + return (mDeviceProperties.get(address) != null); + } + public boolean connectHeadset(String address) { if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false; @@ -2927,12 +2598,14 @@ public class BluetoothService extends IBluetooth.Stub { short channel); private native boolean removeServiceRecordNative(int handle); private native boolean setLinkTimeoutNative(String path, int num_slots); - private native boolean connectInputDeviceNative(String path); - private native boolean disconnectInputDeviceNative(String path); - - private native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge); - private native boolean connectPanDeviceNative(String path, String dstRole); - private native boolean disconnectPanDeviceNative(String path); + native boolean connectInputDeviceNative(String path); + native boolean disconnectInputDeviceNative(String path); + + native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge); + native boolean connectPanDeviceNative(String path, String dstRole); + native boolean disconnectPanDeviceNative(String path); + native boolean disconnectPanServerDeviceNative(String path, + String address, String iface); private native int[] addReservedServiceRecordsNative(int[] uuuids); private native boolean removeReservedServiceRecordsNative(int[] handles); diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 8700af802ae6..97a216a8415d 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -600,8 +600,9 @@ public abstract class Layout { * are at different run levels (and thus there's a split caret). * @param offset the offset * @return true if at a level boundary + * @hide */ - private boolean isLevelBoundary(int offset) { + public boolean isLevelBoundary(int offset) { int line = getLineForOffset(offset); Directions dirs = getLineDirections(line); if (dirs == DIRS_ALL_LEFT_TO_RIGHT || dirs == DIRS_ALL_RIGHT_TO_LEFT) { @@ -1148,8 +1149,7 @@ public abstract class Layout { int bottom = getLineTop(line+1); float h1 = getPrimaryHorizontal(point) - 0.5f; - float h2 = isLevelBoundary(point) ? - getSecondaryHorizontal(point) - 0.5f : h1; + float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point) - 0.5f : h1; int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) | TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING); diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java index 0412285bc3b0..0825bf3b5665 100644 --- a/core/java/android/text/SpannableStringInternal.java +++ b/core/java/android/text/SpannableStringInternal.java @@ -212,6 +212,10 @@ import java.lang.reflect.Array; Object ret1 = null; for (int i = 0; i < spanCount; i++) { + if (kind != null && !kind.isInstance(spans[i])) { + continue; + } + int spanStart = data[i * COLUMNS + START]; int spanEnd = data[i * COLUMNS + END]; @@ -231,10 +235,6 @@ import java.lang.reflect.Array; } } - if (kind != null && !kind.isInstance(spans[i])) { - continue; - } - if (count == 0) { ret1 = spans[i]; count++; diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index ac3df795c760..a4546f0682b3 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -36,8 +36,8 @@ import android.text.style.TabStopSpan; * float, float, android.graphics.Paint) * Canvas.drawText()} directly.</p> */ -public class StaticLayout extends Layout -{ +public class StaticLayout extends Layout { + public StaticLayout(CharSequence source, TextPaint paint, int width, Alignment align, float spacingmult, float spacingadd, @@ -114,8 +114,8 @@ public class StaticLayout extends Layout mMeasured = MeasuredText.obtain(); } - /* package */ void generate(CharSequence source, int bufstart, int bufend, - TextPaint paint, int outerwidth, + /* package */ void generate(CharSequence source, int bufStart, int bufEnd, + TextPaint paint, int outerWidth, Alignment align, float spacingmult, float spacingadd, boolean includepad, boolean trackpad, @@ -126,7 +126,7 @@ public class StaticLayout extends Layout boolean needMultiply = (spacingmult != 1 || spacingadd != 0); Paint.FontMetricsInt fm = mFontMetricsInt; - int[] choosehtv = null; + int[] chooseHtv = null; MeasuredText measured = mMeasured; @@ -137,27 +137,26 @@ public class StaticLayout extends Layout int DEFAULT_DIR = DIR_LEFT_TO_RIGHT; // XXX int paraEnd; - for (int paraStart = bufstart; paraStart <= bufend; paraStart = paraEnd) { - paraEnd = TextUtils.indexOf(source, '\n', paraStart, bufend); + for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) { + paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd); if (paraEnd < 0) - paraEnd = bufend; + paraEnd = bufEnd; else paraEnd++; - int paraLen = paraEnd - paraStart; int firstWidthLineLimit = mLineCount + 1; - int firstwidth = outerwidth; - int restwidth = outerwidth; + int firstWidth = outerWidth; + int restWidth = outerWidth; - LineHeightSpan[] chooseht = null; + LineHeightSpan[] chooseHt = null; if (spanned != null) { LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, LeadingMarginSpan.class); for (int i = 0; i < sp.length; i++) { LeadingMarginSpan lms = sp[i]; - firstwidth -= sp[i].getLeadingMargin(true); - restwidth -= sp[i].getLeadingMargin(false); + firstWidth -= sp[i].getLeadingMargin(true); + restWidth -= sp[i].getLeadingMargin(false); // LeadingMarginSpan2 is odd. The count affects all // leading margin spans, not just this particular one, @@ -166,32 +165,31 @@ public class StaticLayout extends Layout if (lms instanceof LeadingMarginSpan2) { LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms; int lmsFirstLine = getLineForOffset(spanned.getSpanStart(lms2)); - firstWidthLineLimit = lmsFirstLine + - lms2.getLeadingMarginLineCount(); + firstWidthLineLimit = lmsFirstLine + lms2.getLeadingMarginLineCount(); } } - chooseht = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class); + chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class); - if (chooseht.length != 0) { - if (choosehtv == null || - choosehtv.length < chooseht.length) { - choosehtv = new int[ArrayUtils.idealIntArraySize( - chooseht.length)]; + if (chooseHt.length != 0) { + if (chooseHtv == null || + chooseHtv.length < chooseHt.length) { + chooseHtv = new int[ArrayUtils.idealIntArraySize( + chooseHt.length)]; } - for (int i = 0; i < chooseht.length; i++) { - int o = spanned.getSpanStart(chooseht[i]); + for (int i = 0; i < chooseHt.length; i++) { + int o = spanned.getSpanStart(chooseHt[i]); if (o < paraStart) { // starts in this layout, before the // current paragraph - choosehtv[i] = getLineTop(getLineForOffset(o)); + chooseHtv[i] = getLineTop(getLineForOffset(o)); } else { // starts in this paragraph - choosehtv[i] = v; + chooseHtv[i] = v; } } } @@ -204,20 +202,18 @@ public class StaticLayout extends Layout int dir = measured.mDir; boolean easy = measured.mEasy; - CharSequence sub = source; - - int width = firstwidth; + int width = firstWidth; float w = 0; int here = paraStart; int ok = paraStart; - float okwidth = w; - int okascent = 0, okdescent = 0, oktop = 0, okbottom = 0; + float okWidth = w; + int okAscent = 0, okDescent = 0, okTop = 0, okBottom = 0; int fit = paraStart; - float fitwidth = w; - int fitascent = 0, fitdescent = 0, fittop = 0, fitbottom = 0; + float fitWidth = w; + int fitAscent = 0, fitDescent = 0, fitTop = 0, fitBottom = 0; boolean hasTabOrEmoji = false; boolean hasTab = false; @@ -244,21 +240,18 @@ public class StaticLayout extends Layout } nextSpanStart = spanEnd; - int startInPara = spanStart - paraStart; - int endInPara = spanEnd - paraStart; - int fmtop = fm.top; - int fmbottom = fm.bottom; - int fmascent = fm.ascent; - int fmdescent = fm.descent; + int fmTop = fm.top; + int fmBottom = fm.bottom; + int fmAscent = fm.ascent; + int fmDescent = fm.descent; for (int j = spanStart; j < spanEnd; j++) { char c = chs[j - paraStart]; - float before = w; - if (c == '\n') { + if (c == CHAR_NEW_LINE) { // intentionally left empty - } else if (c == '\t') { + } else if (c == CHAR_TAB) { if (hasTab == false) { hasTab = true; hasTabOrEmoji = true; @@ -276,7 +269,8 @@ public class StaticLayout extends Layout } else { w = TabStops.nextDefaultStop(w, TAB_INCREMENT); } - } else if (c >= 0xD800 && c <= 0xDFFF && j + 1 < spanEnd) { + } else if (c >= CHAR_FIRST_HIGH_SURROGATE && c <= CHAR_LAST_LOW_SURROGATE + && j + 1 < spanEnd) { int emoji = Character.codePointAt(chs, j - paraStart); if (emoji >= MIN_EMOJI && emoji <= MAX_EMOJI) { @@ -311,17 +305,17 @@ public class StaticLayout extends Layout // Log.e("text", "was " + before + " now " + w + " after " + c + " within " + width); if (w <= width) { - fitwidth = w; + fitWidth = w; fit = j + 1; - if (fmtop < fittop) - fittop = fmtop; - if (fmascent < fitascent) - fitascent = fmascent; - if (fmdescent > fitdescent) - fitdescent = fmdescent; - if (fmbottom > fitbottom) - fitbottom = fmbottom; + if (fmTop < fitTop) + fitTop = fmTop; + if (fmAscent < fitAscent) + fitAscent = fmAscent; + if (fmDescent > fitDescent) + fitDescent = fmDescent; + if (fmBottom > fitBottom) + fitBottom = fmBottom; /* * From the Unicode Line Breaking Algorithm: @@ -339,25 +333,26 @@ public class StaticLayout extends Layout * after but not before. */ - if (c == ' ' || c == '\t' || - ((c == '.' || c == ',' || c == ':' || c == ';') && + if (c == CHAR_SPACE || c == CHAR_TAB || + ((c == CHAR_DOT || c == CHAR_COMMA || + c == CHAR_COLON || c == CHAR_SEMICOLON) && (j - 1 < here || !Character.isDigit(chs[j - 1 - paraStart])) && (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) || - ((c == '/' || c == '-') && + ((c == CHAR_SLASH || c == CHAR_HYPHEN) && (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) || - (c >= FIRST_CJK && isIdeographic(c, true) && + (c >= CHAR_FIRST_CJK && isIdeographic(c, true) && j + 1 < spanEnd && isIdeographic(chs[j + 1 - paraStart], false))) { - okwidth = w; + okWidth = w; ok = j + 1; - if (fittop < oktop) - oktop = fittop; - if (fitascent < okascent) - okascent = fitascent; - if (fitdescent > okdescent) - okdescent = fitdescent; - if (fitbottom > okbottom) - okbottom = fitbottom; + if (fitTop < okTop) + okTop = fitTop; + if (fitAscent < okAscent) + okAscent = fitAscent; + if (fitDescent > okDescent) + okDescent = fitDescent; + if (fitBottom > okBottom) + okBottom = fitBottom; } } else { if (ellipsize != null) { @@ -365,56 +360,56 @@ public class StaticLayout extends Layout if (ok != here) { // Log.e("text", "output ok " + here + " to " +ok); - while (ok < spanEnd && chs[ok - paraStart] == ' ') { + while (ok < spanEnd && chs[ok - paraStart] == CHAR_SPACE) { ok++; } v = out(source, here, ok, - okascent, okdescent, oktop, okbottom, + okAscent, okDescent, okTop, okBottom, v, - spacingmult, spacingadd, chooseht, - choosehtv, fm, hasTabOrEmoji, + spacingmult, spacingadd, chooseHt, + chooseHtv, fm, hasTabOrEmoji, needMultiply, paraStart, chdirs, dir, easy, - ok == bufend, includepad, trackpad, + ok == bufEnd, includepad, trackpad, chs, widths, here - paraStart, - ellipsize, ellipsizedWidth, okwidth, + ellipsize, ellipsizedWidth, okWidth, paint); here = ok; } else { // Act like it fit even though it didn't. - fitwidth = w; + fitWidth = w; here = fit = j + 1; - if (fmtop < fittop) - fittop = fmtop; - if (fmascent < fitascent) - fitascent = fmascent; - if (fmdescent > fitdescent) - fitdescent = fmdescent; - if (fmbottom > fitbottom) - fitbottom = fmbottom; + if (fmTop < fitTop) + fitTop = fmTop; + if (fmAscent < fitAscent) + fitAscent = fmAscent; + if (fmDescent > fitDescent) + fitDescent = fmDescent; + if (fmBottom > fitBottom) + fitBottom = fmBottom; } } else { if (ok != here) { // Log.e("text", "output ok " + here + " to " +ok); - while (ok < spanEnd && chs[ok - paraStart] == ' ') { + while (ok < spanEnd && chs[ok - paraStart] == CHAR_SPACE) { ok++; } v = out(source, here, ok, - okascent, okdescent, oktop, okbottom, + okAscent, okDescent, okTop, okBottom, v, - spacingmult, spacingadd, chooseht, - choosehtv, fm, hasTabOrEmoji, + spacingmult, spacingadd, chooseHt, + chooseHtv, fm, hasTabOrEmoji, needMultiply, paraStart, chdirs, dir, easy, - ok == bufend, includepad, trackpad, + ok == bufEnd, includepad, trackpad, chs, widths, here - paraStart, - ellipsize, ellipsizedWidth, okwidth, + ellipsize, ellipsizedWidth, okWidth, paint); here = ok; @@ -422,15 +417,15 @@ public class StaticLayout extends Layout // Log.e("text", "output fit " + here + " to " +fit); v = out(source, here, fit, - fitascent, fitdescent, - fittop, fitbottom, + fitAscent, fitDescent, + fitTop, fitBottom, v, - spacingmult, spacingadd, chooseht, - choosehtv, fm, hasTabOrEmoji, + spacingmult, spacingadd, chooseHt, + chooseHtv, fm, hasTabOrEmoji, needMultiply, paraStart, chdirs, dir, easy, - fit == bufend, includepad, trackpad, + fit == bufEnd, includepad, trackpad, chs, widths, here - paraStart, - ellipsize, ellipsizedWidth, fitwidth, + ellipsize, ellipsizedWidth, fitWidth, paint); here = fit; @@ -446,10 +441,10 @@ public class StaticLayout extends Layout fm.ascent, fm.descent, fm.top, fm.bottom, v, - spacingmult, spacingadd, chooseht, - choosehtv, fm, hasTabOrEmoji, + spacingmult, spacingadd, chooseHt, + chooseHtv, fm, hasTabOrEmoji, needMultiply, paraStart, chdirs, dir, easy, - here + 1 == bufend, includepad, + here + 1 == bufEnd, includepad, trackpad, chs, widths, here - paraStart, ellipsize, ellipsizedWidth, @@ -470,65 +465,64 @@ public class StaticLayout extends Layout ok = fit = here; w = 0; - fitascent = fitdescent = fittop = fitbottom = 0; - okascent = okdescent = oktop = okbottom = 0; + fitAscent = fitDescent = fitTop = fitBottom = 0; + okAscent = okDescent = okTop = okBottom = 0; if (--firstWidthLineLimit <= 0) { - width = restwidth; + width = restWidth; } } } } if (paraEnd != here) { - if ((fittop | fitbottom | fitdescent | fitascent) == 0) { + if ((fitTop | fitBottom | fitDescent | fitAscent) == 0) { paint.getFontMetricsInt(fm); - fittop = fm.top; - fitbottom = fm.bottom; - fitascent = fm.ascent; - fitdescent = fm.descent; + fitTop = fm.top; + fitBottom = fm.bottom; + fitAscent = fm.ascent; + fitDescent = fm.descent; } // Log.e("text", "output rest " + here + " to " + end); v = out(source, - here, paraEnd, fitascent, fitdescent, - fittop, fitbottom, + here, paraEnd, fitAscent, fitDescent, + fitTop, fitBottom, v, - spacingmult, spacingadd, chooseht, - choosehtv, fm, hasTabOrEmoji, + spacingmult, spacingadd, chooseHt, + chooseHtv, fm, hasTabOrEmoji, needMultiply, paraStart, chdirs, dir, easy, - paraEnd == bufend, includepad, trackpad, + paraEnd == bufEnd, includepad, trackpad, chs, widths, here - paraStart, ellipsize, ellipsizedWidth, w, paint); } paraStart = paraEnd; - if (paraEnd == bufend) + if (paraEnd == bufEnd) break; } - if (bufend == bufstart || source.charAt(bufend - 1) == '\n') { - // Log.e("text", "output last " + bufend); + if (bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) { + // Log.e("text", "output last " + bufEnd); paint.getFontMetricsInt(fm); v = out(source, - bufend, bufend, fm.ascent, fm.descent, + bufEnd, bufEnd, fm.ascent, fm.descent, fm.top, fm.bottom, v, spacingmult, spacingadd, null, null, fm, false, - needMultiply, bufend, null, DEFAULT_DIR, true, + needMultiply, bufEnd, null, DEFAULT_DIR, true, true, includepad, trackpad, - null, null, bufstart, + null, null, bufStart, ellipsize, ellipsizedWidth, 0, paint); } } - private static final char FIRST_CJK = '\u2E80'; /** * Returns true if the specified character is one of those specified * as being Ideographic (class ID) by the Unicode Line Breaking Algorithm @@ -636,14 +630,14 @@ public class StaticLayout extends Layout private int out(CharSequence text, int start, int end, int above, int below, int top, int bottom, int v, float spacingmult, float spacingadd, - LineHeightSpan[] chooseht, int[] choosehtv, + LineHeightSpan[] chooseHt, int[] chooseHtv, Paint.FontMetricsInt fm, boolean hasTabOrEmoji, boolean needMultiply, int pstart, byte[] chdirs, int dir, boolean easy, boolean last, - boolean includepad, boolean trackpad, - char[] chs, float[] widths, int widstart, - TextUtils.TruncateAt ellipsize, float ellipsiswidth, - float textwidth, TextPaint paint) { + boolean includePad, boolean trackPad, + char[] chs, float[] widths, int widthStart, + TextUtils.TruncateAt ellipsize, float ellipsisWidth, + float textWidth, TextPaint paint) { int j = mLineCount; int off = j * mColumns; int want = off + mColumns + TOP; @@ -662,19 +656,19 @@ public class StaticLayout extends Layout mLineDirections = grow2; } - if (chooseht != null) { + if (chooseHt != null) { fm.ascent = above; fm.descent = below; fm.top = top; fm.bottom = bottom; - for (int i = 0; i < chooseht.length; i++) { - if (chooseht[i] instanceof LineHeightSpan.WithDensity) { - ((LineHeightSpan.WithDensity) chooseht[i]). - chooseHeight(text, start, end, choosehtv[i], v, fm, paint); + for (int i = 0; i < chooseHt.length; i++) { + if (chooseHt[i] instanceof LineHeightSpan.WithDensity) { + ((LineHeightSpan.WithDensity) chooseHt[i]). + chooseHeight(text, start, end, chooseHtv[i], v, fm, paint); } else { - chooseht[i].chooseHeight(text, start, end, choosehtv[i], v, fm); + chooseHt[i].chooseHeight(text, start, end, chooseHtv[i], v, fm); } } @@ -685,20 +679,20 @@ public class StaticLayout extends Layout } if (j == 0) { - if (trackpad) { + if (trackPad) { mTopPadding = top - above; } - if (includepad) { + if (includePad) { above = top; } } if (last) { - if (trackpad) { + if (trackPad) { mBottomPadding = bottom - below; } - if (includepad) { + if (includePad) { below = bottom; } } @@ -708,9 +702,9 @@ public class StaticLayout extends Layout if (needMultiply) { double ex = (below - above) * (spacingmult - 1) + spacingadd; if (ex >= 0) { - extra = (int)(ex + 0.5); + extra = (int)(ex + EXTRA_ROUNDING); } else { - extra = -(int)(-ex + 0.5); + extra = -(int)(-ex + EXTRA_ROUNDING); } } else { extra = 0; @@ -735,45 +729,45 @@ public class StaticLayout extends Layout if (easy) { mLineDirections[j] = linedirs; } else { - mLineDirections[j] = AndroidBidi.directions(dir, chdirs, widstart, chs, - widstart, end - start); + mLineDirections[j] = AndroidBidi.directions(dir, chdirs, widthStart, chs, + widthStart, end - start); } // If ellipsize is in marquee mode, do not apply ellipsis on the first line if (ellipsize != null && (ellipsize != TextUtils.TruncateAt.MARQUEE || j != 0)) { - calculateEllipsis(start, end, widths, widstart, - ellipsiswidth, ellipsize, j, - textwidth, paint); + calculateEllipsis(start, end, widths, widthStart, + ellipsisWidth, ellipsize, j, + textWidth, paint); } mLineCount++; return v; } - private void calculateEllipsis(int linestart, int lineend, - float[] widths, int widstart, + private void calculateEllipsis(int lineStart, int lineEnd, + float[] widths, int widthStart, float avail, TextUtils.TruncateAt where, - int line, float textwidth, TextPaint paint) { + int line, float textWidth, TextPaint paint) { - if (textwidth <= avail) { + if (textWidth <= avail) { // Everything fits! mLines[mColumns * line + ELLIPSIS_START] = 0; mLines[mColumns * line + ELLIPSIS_COUNT] = 0; return; } - float ellipsiswid = paint.measureText("\u2026"); + float ellipsisWidth = paint.measureText(HORIZONTAL_ELLIPSIS); int ellipsisStart, ellipsisCount; - int len = lineend - linestart; + int len = lineEnd - lineStart; if (where == TextUtils.TruncateAt.START) { float sum = 0; int i; for (i = len; i >= 0; i--) { - float w = widths[i - 1 + linestart - widstart]; + float w = widths[i - 1 + lineStart - widthStart]; - if (w + sum + ellipsiswid > avail) { + if (w + sum + ellipsisWidth > avail) { break; } @@ -787,9 +781,9 @@ public class StaticLayout extends Layout int i; for (i = 0; i < len; i++) { - float w = widths[i + linestart - widstart]; + float w = widths[i + lineStart - widthStart]; - if (w + sum + ellipsiswid > avail) { + if (w + sum + ellipsisWidth > avail) { break; } @@ -802,9 +796,9 @@ public class StaticLayout extends Layout float lsum = 0, rsum = 0; int left = 0, right = len; - float ravail = (avail - ellipsiswid) / 2; + float ravail = (avail - ellipsisWidth) / 2; for (right = len; right >= 0; right--) { - float w = widths[right - 1 + linestart - widstart]; + float w = widths[right - 1 + lineStart - widthStart]; if (w + rsum > ravail) { break; @@ -813,9 +807,9 @@ public class StaticLayout extends Layout rsum += w; } - float lavail = avail - ellipsiswid - rsum; + float lavail = avail - ellipsisWidth - rsum; for (left = 0; left < right; left++) { - float w = widths[left + linestart - widstart]; + float w = widths[left + lineStart - widthStart]; if (w + lsum > lavail) { break; @@ -968,6 +962,24 @@ public class StaticLayout extends Layout private static final int TAB_INCREMENT = 20; // same as Layout, but that's private + private static final char CHAR_FIRST_CJK = '\u2E80'; + + private static final char CHAR_NEW_LINE = '\n'; + private static final char CHAR_TAB = '\t'; + private static final char CHAR_SPACE = ' '; + private static final char CHAR_DOT = '.'; + private static final char CHAR_COMMA = ','; + private static final char CHAR_COLON = ':'; + private static final char CHAR_SEMICOLON = ';'; + private static final char CHAR_SLASH = '/'; + private static final char CHAR_HYPHEN = '-'; + + private static final double EXTRA_ROUNDING = 0.5; + private static final String HORIZONTAL_ELLIPSIS = "\u2026"; // this is "..." + + private static final int CHAR_FIRST_HIGH_SURROGATE = 0xD800; + private static final int CHAR_LAST_LOW_SURROGATE = 0xDFFF; + /* * This is reused across calls to generate() */ diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index baaa3ceae2a3..5ae65df9826a 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -17,32 +17,33 @@ package android.text.format; import android.content.Context; +import android.net.NetworkUtils; /** * Utility class to aid in formatting common values that are not covered - * by the standard java.util.Formatter. + * by {@link java.util.Formatter} */ public final class Formatter { /** * Formats a content size to be in the form of bytes, kilobytes, megabytes, etc - * + * * @param context Context to use to load the localized units - * @param number size value to be formated - * @return formated string with the number + * @param number size value to be formatted + * @return formatted string with the number */ public static String formatFileSize(Context context, long number) { return formatFileSize(context, number, false); } - + /** * Like {@link #formatFileSize}, but trying to generate shorter numbers - * (showing fewer digits of precisin). + * (showing fewer digits of precision). */ public static String formatShortFileSize(Context context, long number) { return formatFileSize(context, number, true); } - + private static String formatFileSize(Context context, long number, boolean shorter) { if (context == null) { return ""; @@ -92,21 +93,21 @@ public final class Formatter { getString(com.android.internal.R.string.fileSizeSuffix, value, context.getString(suffix)); } - + /** * Returns a string in the canonical IP format ###.###.###.### from a packed integer containing * the IP address. The IP address is expected to be in little-endian format (LSB first). That * is, 0x01020304 will return "4.3.2.1". - * - * @param addr the IP address as a packed integer with LSB first. + * + * @param ipv4Address the IP address as a packed integer with LSB first. * @return string with canonical IP address format. + * + * @deprecated this method doesn't support IPv6 addresses. Prefer {@link + * java.net.InetAddress#getHostAddress()}, which supports both IPv4 and + * IPv6 addresses. */ - public static String formatIpAddress(int addr) { - StringBuffer buf = new StringBuffer(); - buf.append(addr & 0xff).append('.'). - append((addr >>>= 8) & 0xff).append('.'). - append((addr >>>= 8) & 0xff).append('.'). - append((addr >>>= 8) & 0xff); - return buf.toString(); + @Deprecated + public static String formatIpAddress(int ipv4Address) { + return NetworkUtils.intToInetAddress(ipv4Address).getHostAddress(); } } diff --git a/core/java/android/util/FastImmutableArraySet.java b/core/java/android/util/FastImmutableArraySet.java new file mode 100644 index 000000000000..4175c6057ecf --- /dev/null +++ b/core/java/android/util/FastImmutableArraySet.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import java.util.AbstractSet; +import java.util.Iterator; + +/** + * A fast immutable set wrapper for an array that is optimized for non-concurrent iteration. + * The same iterator instance is reused each time to avoid creating lots of garbage. + * Iterating over an array in this fashion is 2.5x faster than iterating over a {@link HashSet} + * so it is worth copying the contents of the set to an array when iterating over it + * hundreds of times. + * @hide + */ +public final class FastImmutableArraySet<T> extends AbstractSet<T> { + FastIterator<T> mIterator; + T[] mContents; + + public FastImmutableArraySet(T[] contents) { + mContents = contents; + } + + @Override + public Iterator<T> iterator() { + FastIterator<T> it = mIterator; + if (it == null) { + it = new FastIterator<T>(mContents); + mIterator = it; + } else { + it.mIndex = 0; + } + return it; + } + + @Override + public int size() { + return mContents.length; + } + + private static final class FastIterator<T> implements Iterator<T> { + private final T[] mContents; + int mIndex; + + public FastIterator(T[] contents) { + mContents = contents; + } + + @Override + public boolean hasNext() { + return mIndex != mContents.length; + } + + @Override + public T next() { + return mContents[mIndex++]; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/core/java/android/util/LruCache.java b/core/java/android/util/LruCache.java new file mode 100644 index 000000000000..5578e6ab372c --- /dev/null +++ b/core/java/android/util/LruCache.java @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A cache that holds strong references to a limited number of values. Each time + * a value is accessed, it is moved to the head of a queue. When a value is + * added to a full cache, the value at the end of that queue is evicted and may + * become eligible for garbage collection. + * + * <p>If your cached values hold resources that need to be explicitly released, + * override {@link #entryEvicted}. This method is only invoked when values are + * evicted. Values replaced by calls to {@link #put} must be released manually. + * + * <p>If a cache miss should be computed on demand for the corresponding keys, + * override {@link #create}. This simplifies the calling code, allowing it to + * assume a value will always be returned, even when there's a cache miss. + * + * <p>By default, the cache size is measured in the number of entries. Override + * {@link #sizeOf} to size the cache in different units. For example, this cache + * is limited to 4MiB of bitmaps: + * <pre> {@code + * int cacheSize = 4 * 1024 * 1024; // 4MiB + * LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) { + * protected int sizeOf(String key, Bitmap value) { + * return value.getByteCount(); + * } + * }}</pre> + * + * <p>This class is thread-safe. Perform multiple cache operations atomically by + * synchronizing on the cache: <pre> {@code + * synchronized (cache) { + * if (cache.get(key) == null) { + * cache.put(key, value); + * } + * }}</pre> + * + * <p>This class does not allow null to be used as a key or value. A return + * value of null from {@link #get}, {@link #put} or {@link #remove} is + * unambiguous: the key was not in the cache. + */ +public class LruCache<K, V> { + private final LinkedHashMap<K, V> map; + + /** Size of this cache in units. Not necessarily the number of elements. */ + private int size; + private int maxSize; + + private int putCount; + private int createCount; + private int evictionCount; + private int hitCount; + private int missCount; + + /** + * @param maxSize for caches that do not override {@link #sizeOf}, this is + * the maximum number of entries in the cache. For all other caches, + * this is the maximum sum of the sizes of the entries in this cache. + */ + public LruCache(int maxSize) { + if (maxSize <= 0) { + throw new IllegalArgumentException("maxSize <= 0"); + } + this.maxSize = maxSize; + this.map = new LinkedHashMap<K, V>(0, 0.75f, true); + } + + /** + * Returns the value for {@code key} if it exists in the cache or can be + * created by {@code #create}. If a value was returned, it is moved to the + * head of the queue. This returns null if a value is not cached and cannot + * be created. + */ + public synchronized final V get(K key) { + if (key == null) { + throw new NullPointerException("key == null"); + } + + V result = map.get(key); + if (result != null) { + hitCount++; + return result; + } + + missCount++; + + // TODO: release the lock while calling this potentially slow user code + result = create(key); + + if (result != null) { + createCount++; + size += safeSizeOf(key, result); + map.put(key, result); + trimToSize(maxSize); + } + return result; + } + + /** + * Caches {@code value} for {@code key}. The value is moved to the head of + * the queue. + * + * @return the previous value mapped by {@code key}. Although that entry is + * no longer cached, it has not been passed to {@link #entryEvicted}. + */ + public synchronized final V put(K key, V value) { + if (key == null || value == null) { + throw new NullPointerException("key == null || value == null"); + } + + putCount++; + size += safeSizeOf(key, value); + V previous = map.put(key, value); + if (previous != null) { + size -= safeSizeOf(key, previous); + } + trimToSize(maxSize); + return previous; + } + + private void trimToSize(int maxSize) { + while (size > maxSize) { + Map.Entry<K, V> toEvict = map.eldest(); // equal to map.entrySet().iterator().next(); + if (toEvict == null) { + break; // map is empty; if size is not 0 then throw an error below + } + + K key = toEvict.getKey(); + V value = toEvict.getValue(); + map.remove(key); + size -= safeSizeOf(key, value); + evictionCount++; + + // TODO: release the lock while calling this potentially slow user code + entryEvicted(key, value); + } + + if (size < 0 || (map.isEmpty() && size != 0)) { + throw new IllegalStateException(getClass().getName() + + ".sizeOf() is reporting inconsistent results!"); + } + } + + /** + * Removes the entry for {@code key} if it exists. + * + * @return the previous value mapped by {@code key}. Although that entry is + * no longer cached, it has not been passed to {@link #entryEvicted}. + */ + public synchronized final V remove(K key) { + if (key == null) { + throw new NullPointerException("key == null"); + } + + V previous = map.remove(key); + if (previous != null) { + size -= safeSizeOf(key, previous); + } + return previous; + } + + /** + * Called for entries that have reached the tail of the least recently used + * queue and are be removed. The default implementation does nothing. + */ + protected void entryEvicted(K key, V value) {} + + /** + * Called after a cache miss to compute a value for the corresponding key. + * Returns the computed value or null if no value can be computed. The + * default implementation returns null. + */ + protected V create(K key) { + return null; + } + + private int safeSizeOf(K key, V value) { + int result = sizeOf(key, value); + if (result < 0) { + throw new IllegalStateException("Negative size: " + key + "=" + value); + } + return result; + } + + /** + * Returns the size of the entry for {@code key} and {@code value} in + * user-defined units. The default implementation returns 1 so that size + * is the number of entries and max size is the maximum number of entries. + * + * <p>An entry's size must not change while it is in the cache. + */ + protected int sizeOf(K key, V value) { + return 1; + } + + /** + * Clear the cache, calling {@link #entryEvicted} on each removed entry. + */ + public synchronized final void evictAll() { + trimToSize(-1); // -1 will evict 0-sized elements + } + + /** + * For caches that do not override {@link #sizeOf}, this returns the number + * of entries in the cache. For all other caches, this returns the sum of + * the sizes of the entries in this cache. + */ + public synchronized final int size() { + return size; + } + + /** + * For caches that do not override {@link #sizeOf}, this returns the maximum + * number of entries in the cache. For all other caches, this returns the + * maximum sum of the sizes of the entries in this cache. + */ + public synchronized final int maxSize() { + return maxSize; + } + + /** + * Returns the number of times {@link #get} returned a value. + */ + public synchronized final int hitCount() { + return hitCount; + } + + /** + * Returns the number of times {@link #get} returned null or required a new + * value to be created. + */ + public synchronized final int missCount() { + return missCount; + } + + /** + * Returns the number of times {@link #create(Object)} returned a value. + */ + public synchronized final int createCount() { + return createCount; + } + + /** + * Returns the number of times {@link #put} was called. + */ + public synchronized final int putCount() { + return putCount; + } + + /** + * Returns the number of values that have been evicted. + */ + public synchronized final int evictionCount() { + return evictionCount; + } + + /** + * Returns a copy of the current contents of the cache, ordered from least + * recently accessed to most recently accessed. + */ + public synchronized final Map<K, V> snapshot() { + return new LinkedHashMap<K, V>(map); + } + + @Override public synchronized final String toString() { + int accesses = hitCount + missCount; + int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0; + return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]", + maxSize, hitCount, missCount, hitPercent); + } +} diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 71d55f7a476d..d46dacc2e55d 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -42,10 +42,23 @@ public abstract class HardwareRenderer { /** * Turn on to only refresh the parts of the screen that need updating. + * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY} + * must also have the value "true". */ public static final boolean RENDER_DIRTY_REGIONS = true; /** + * System property used to enable or disable dirty regions invalidation. + * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true. + * The default value of this property is assumed to be true. + * + * Possible values: + * "true", to enable partial invalidates + * "false", to disable partial invalidates + */ + static final String RENDER_DIRTY_REGIONS_PROPERTY = "hwui.render_dirty_regions"; + + /** * Turn on to draw dirty regions every other frame. */ private static final boolean DEBUG_DIRTY_REGION = false; @@ -113,8 +126,23 @@ public abstract class HardwareRenderer { */ abstract void setup(int width, int height); + /** + * Interface used to receive callbacks whenever a view is drawn by + * a hardware renderer instance. + */ interface HardwareDrawCallbacks { + /** + * Invoked before a view is drawn by a hardware renderer. + * + * @param canvas The Canvas used to render the view. + */ void onHardwarePreDraw(Canvas canvas); + + /** + * Invoked after a view is drawn by a hardware renderer. + * + * @param canvas The Canvas used to render the view. + */ void onHardwarePostDraw(Canvas canvas); } @@ -250,6 +278,7 @@ public abstract class HardwareRenderer { int mFrameCount; Paint mDebugPaint; + boolean mDirtyRegions; final int mGlVersion; final boolean mTranslucent; @@ -259,6 +288,9 @@ public abstract class HardwareRenderer { GlRenderer(int glVersion, boolean translucent) { mGlVersion = glVersion; mTranslucent = translucent; + final String dirtyProperty = System.getProperty(RENDER_DIRTY_REGIONS_PROPERTY, "true"); + //noinspection PointlessBooleanExpression,ConstantConditions + mDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty); } /** @@ -388,10 +420,24 @@ public abstract class HardwareRenderer { // We can now initialize EGL for that display int[] version = new int[2]; if (!sEgl.eglInitialize(sEglDisplay, version)) { - throw new RuntimeException("eglInitialize failed " - + getEGLErrorString(sEgl.eglGetError())); + throw new RuntimeException("eglInitialize failed " + + getEGLErrorString(sEgl.eglGetError())); } + sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay); + if (sEglConfig == null) { + // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without + if (mDirtyRegions) { + mDirtyRegions = false; + + sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay); + if (sEglConfig == null) { + throw new RuntimeException("eglConfig not initialized"); + } + } else { + throw new RuntimeException("eglConfig not initialized"); + } + } /* * Create an EGL context. We want to do this as rarely as we can, because an @@ -409,7 +455,7 @@ public abstract class HardwareRenderer { throw new RuntimeException("eglDisplay not initialized"); } if (sEglConfig == null) { - throw new RuntimeException("mEglConfig not initialized"); + throw new RuntimeException("eglConfig not initialized"); } if (Thread.currentThread() != sEglThread) { throw new IllegalStateException("HardwareRenderer cannot be used " @@ -452,7 +498,7 @@ public abstract class HardwareRenderer { + getEGLErrorString(sEgl.eglGetError())); } - if (RENDER_DIRTY_REGIONS) { + if (mDirtyRegions) { if (!GLES20Canvas.preserveBackBuffer()) { Log.w(LOG_TAG, "Backbuffer cannot be preserved"); } @@ -518,15 +564,14 @@ public abstract class HardwareRenderer { * @return An {@link android.view.HardwareRenderer.GlRenderer.EglConfigChooser}. */ EglConfigChooser getConfigChooser(int glVersion) { - return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0); + return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0, mDirtyRegions); } @Override void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { if (canDraw()) { - //noinspection PointlessBooleanExpression,ConstantConditions - if (!HardwareRenderer.RENDER_DIRTY_REGIONS) { + if (!mDirtyRegions) { dirty = null; } @@ -676,16 +721,16 @@ public abstract class HardwareRenderer { static class ComponentSizeChooser extends EglConfigChooser { private int[] mValue; - private int mRedSize; - private int mGreenSize; - private int mBlueSize; - private int mAlphaSize; - private int mDepthSize; - private int mStencilSize; + private final int mRedSize; + private final int mGreenSize; + private final int mBlueSize; + private final int mAlphaSize; + private final int mDepthSize; + private final int mStencilSize; + private final boolean mDirtyRegions; ComponentSizeChooser(int glVersion, int redSize, int greenSize, int blueSize, - int alphaSize, int depthSize, int stencilSize) { - //noinspection PointlessBitwiseExpression + int alphaSize, int depthSize, int stencilSize, boolean dirtyRegions) { super(glVersion, new int[] { EGL10.EGL_RED_SIZE, redSize, EGL10.EGL_GREEN_SIZE, greenSize, @@ -694,7 +739,7 @@ public abstract class HardwareRenderer { EGL10.EGL_DEPTH_SIZE, depthSize, EGL10.EGL_STENCIL_SIZE, stencilSize, EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT | - (RENDER_DIRTY_REGIONS ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0), + (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0), EGL10.EGL_NONE }); mValue = new int[1]; mRedSize = redSize; @@ -703,7 +748,8 @@ public abstract class HardwareRenderer { mAlphaSize = alphaSize; mDepthSize = depthSize; mStencilSize = stencilSize; - } + mDirtyRegions = dirtyRegions; + } @Override EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { @@ -716,7 +762,7 @@ public abstract class HardwareRenderer { int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0); int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0); boolean backBuffer; - if (RENDER_DIRTY_REGIONS) { + if (mDirtyRegions) { int surfaceType = findConfigAttrib(egl, display, config, EGL_SURFACE_TYPE, 0); backBuffer = (surfaceType & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) != 0; diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index dd04975c11d6..7abbce6269c5 100755 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -20,6 +20,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.SparseArray; /** * Describes the capabilities of a particular input device. @@ -41,9 +42,9 @@ public final class InputDevice implements Parcelable { private String mName; private int mSources; private int mKeyboardType; - - private MotionRange[] mMotionRanges; - + + private final SparseArray<MotionRange> mMotionRanges = new SparseArray<MotionRange>(); + /** * A mask for input source classes. * @@ -98,7 +99,16 @@ public final class InputDevice implements Parcelable { * Use {@link #getMotionRange} to query the range of positions. */ public static final int SOURCE_CLASS_POSITION = 0x00000008; - + + /** + * The input source is a joystick. + * + * A {@link MotionEvent} should be interpreted as absolute joystick movements. + * + * Use {@link #getMotionRange} to query the range of positions. + */ + public static final int SOURCE_CLASS_JOYSTICK = 0x00000010; + /** * The input source is unknown. */ @@ -117,7 +127,15 @@ public final class InputDevice implements Parcelable { * @see #SOURCE_CLASS_BUTTON */ public static final int SOURCE_DPAD = 0x00000200 | SOURCE_CLASS_BUTTON; - + + /** + * The input source is a game pad. + * (It may also be a {@link #SOURCE_JOYSTICK}). + * + * @see #SOURCE_CLASS_BUTTON + */ + public static final int SOURCE_GAMEPAD = 0x00000400 | SOURCE_CLASS_BUTTON; + /** * The input source is a touch screen pointing device. * @@ -148,7 +166,15 @@ public final class InputDevice implements Parcelable { * @see #SOURCE_CLASS_POSITION */ public static final int SOURCE_TOUCHPAD = 0x00100000 | SOURCE_CLASS_POSITION; - + + /** + * The input source is a joystick. + * (It may also be a {@link #SOURCE_GAMEPAD}). + * + * @see #SOURCE_CLASS_JOYSTICK + */ + public static final int SOURCE_JOYSTICK = 0x01000000 | SOURCE_CLASS_JOYSTICK; + /** * A special input source constant that is used when filtering input devices * to match devices that provide any type of input source. @@ -156,70 +182,85 @@ public final class InputDevice implements Parcelable { public static final int SOURCE_ANY = 0xffffff00; /** - * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#x}. + * Constant for retrieving the range of values for {@link MotionEvent#AXIS_X}. * * @see #getMotionRange + * @deprecated Use {@link MotionEvent#AXIS_X} instead. */ - public static final int MOTION_RANGE_X = 0; - + @Deprecated + public static final int MOTION_RANGE_X = MotionEvent.AXIS_X; + /** - * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#y}. + * Constant for retrieving the range of values for {@link MotionEvent#AXIS_Y}. * * @see #getMotionRange + * @deprecated Use {@link MotionEvent#AXIS_Y} instead. */ - public static final int MOTION_RANGE_Y = 1; - + @Deprecated + public static final int MOTION_RANGE_Y = MotionEvent.AXIS_Y; + /** - * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#pressure}. + * Constant for retrieving the range of values for {@link MotionEvent#AXIS_PRESSURE}. * * @see #getMotionRange + * @deprecated Use {@link MotionEvent#AXIS_PRESSURE} instead. */ - public static final int MOTION_RANGE_PRESSURE = 2; - + @Deprecated + public static final int MOTION_RANGE_PRESSURE = MotionEvent.AXIS_PRESSURE; + /** - * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#size}. + * Constant for retrieving the range of values for {@link MotionEvent#AXIS_SIZE}. * * @see #getMotionRange + * @deprecated Use {@link MotionEvent#AXIS_SIZE} instead. */ - public static final int MOTION_RANGE_SIZE = 3; - + @Deprecated + public static final int MOTION_RANGE_SIZE = MotionEvent.AXIS_SIZE; + /** - * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#touchMajor}. + * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOUCH_MAJOR}. * * @see #getMotionRange + * @deprecated Use {@link MotionEvent#AXIS_TOUCH_MAJOR} instead. */ - public static final int MOTION_RANGE_TOUCH_MAJOR = 4; - + @Deprecated + public static final int MOTION_RANGE_TOUCH_MAJOR = MotionEvent.AXIS_TOUCH_MAJOR; + /** - * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#touchMinor}. + * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOUCH_MINOR}. * * @see #getMotionRange + * @deprecated Use {@link MotionEvent#AXIS_TOUCH_MINOR} instead. */ - public static final int MOTION_RANGE_TOUCH_MINOR = 5; - + @Deprecated + public static final int MOTION_RANGE_TOUCH_MINOR = MotionEvent.AXIS_TOUCH_MINOR; + /** - * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#toolMajor}. + * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOOL_MAJOR}. * * @see #getMotionRange + * @deprecated Use {@link MotionEvent#AXIS_TOOL_MAJOR} instead. */ - public static final int MOTION_RANGE_TOOL_MAJOR = 6; - + @Deprecated + public static final int MOTION_RANGE_TOOL_MAJOR = MotionEvent.AXIS_TOOL_MAJOR; + /** - * Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#toolMinor}. + * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOOL_MINOR}. * * @see #getMotionRange + * @deprecated Use {@link MotionEvent#AXIS_TOOL_MINOR} instead. */ - public static final int MOTION_RANGE_TOOL_MINOR = 7; - + @Deprecated + public static final int MOTION_RANGE_TOOL_MINOR = MotionEvent.AXIS_TOOL_MINOR; + /** - * Constant for retrieving the range of values for - * {@link MotionEvent.PointerCoords#orientation}. + * Constant for retrieving the range of values for {@link MotionEvent#AXIS_ORIENTATION}. * * @see #getMotionRange + * @deprecated Use {@link MotionEvent#AXIS_ORIENTATION} instead. */ - public static final int MOTION_RANGE_ORIENTATION = 8; - - private static final int MOTION_RANGE_LAST = MOTION_RANGE_ORIENTATION; + @Deprecated + public static final int MOTION_RANGE_ORIENTATION = MotionEvent.AXIS_ORIENTATION; /** * There is no keyboard. @@ -236,10 +277,9 @@ public final class InputDevice implements Parcelable { * The keyboard supports a complement of alphabetic keys. */ public static final int KEYBOARD_TYPE_ALPHABETIC = 2; - + // Called by native code. private InputDevice() { - mMotionRanges = new MotionRange[MOTION_RANGE_LAST + 1]; } /** @@ -310,72 +350,69 @@ public final class InputDevice implements Parcelable { public KeyCharacterMap getKeyCharacterMap() { return KeyCharacterMap.load(mId); } - + /** - * Gets information about the range of values for a particular {@link MotionEvent} - * coordinate. - * @param rangeType The motion range constant. - * @return The range of values, or null if the requested coordinate is not + * Gets information about the range of values for a particular {@link MotionEvent} axis. + * @param axis The axis constant. + * @return The range of values, or null if the requested axis is not * supported by the device. + * + * @see MotionEvent#AXIS_X + * @see MotionEvent#AXIS_Y */ - public MotionRange getMotionRange(int rangeType) { - if (rangeType < 0 || rangeType > MOTION_RANGE_LAST) { - throw new IllegalArgumentException("Requested range is out of bounds."); - } - - return mMotionRanges[rangeType]; + public MotionRange getMotionRange(int axis) { + return mMotionRanges.get(axis); } - - private void addMotionRange(int rangeType, float min, float max, float flat, float fuzz) { - if (rangeType >= 0 && rangeType <= MOTION_RANGE_LAST) { - MotionRange range = new MotionRange(min, max, flat, fuzz); - mMotionRanges[rangeType] = range; - } + + // Called by native code. + private void addMotionRange(int axis, float min, float max, float flat, float fuzz) { + mMotionRanges.append(axis, new MotionRange(min, max, flat, fuzz)); } - + /** - * Provides information about the range of values for a particular {@link MotionEvent} - * coordinate. + * Provides information about the range of values for a particular {@link MotionEvent} axis. + * + * @see InputDevice#getMotionRange(int) */ public static final class MotionRange { private float mMin; private float mMax; private float mFlat; private float mFuzz; - + private MotionRange(float min, float max, float flat, float fuzz) { mMin = min; mMax = max; mFlat = flat; mFuzz = fuzz; } - + /** - * Gets the minimum value for the coordinate. - * @return The minimum value. + * Gets the minimum value for the axis. + * @return The (inclusive) minimum value. */ public float getMin() { return mMin; } - + /** - * Gets the maximum value for the coordinate. - * @return The minimum value. + * Gets the maximum value for the axis. + * @return The (inclusive) maximum value. */ public float getMax() { return mMax; } - + /** - * Gets the range of the coordinate (difference between maximum and minimum). + * Gets the range of the axis (difference between maximum and minimum plus one). * @return The range of values. */ public float getRange() { - return mMax - mMin; + return mMax - mMin + 1; } - + /** - * Gets the extent of the center flat position with respect to this coordinate. + * Gets the extent of the center flat position with respect to this axis. * For example, a flat value of 8 means that the center position is between -8 and +8. * This value is mainly useful for calibrating self-centering devices. * @return The extent of the center flat position. @@ -383,9 +420,9 @@ public final class InputDevice implements Parcelable { public float getFlat() { return mFlat; } - + /** - * Gets the error tolerance for input device measurements with respect to this coordinate. + * Gets the error tolerance for input device measurements with respect to this axis. * For example, a value of 2 indicates that the measured value may be up to +/- 2 units * away from the actual value due to noise and device sensitivity limitations. * @return The error tolerance. @@ -394,7 +431,7 @@ public final class InputDevice implements Parcelable { return mFuzz; } } - + public static final Parcelable.Creator<InputDevice> CREATOR = new Parcelable.Creator<InputDevice>() { public InputDevice createFromParcel(Parcel in) { @@ -413,15 +450,13 @@ public final class InputDevice implements Parcelable { mName = in.readString(); mSources = in.readInt(); mKeyboardType = in.readInt(); - + for (;;) { - int rangeType = in.readInt(); - if (rangeType < 0) { + int axis = in.readInt(); + if (axis < 0) { break; } - - addMotionRange(rangeType, - in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat()); + addMotionRange(axis, in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat()); } } @@ -431,25 +466,25 @@ public final class InputDevice implements Parcelable { out.writeString(mName); out.writeInt(mSources); out.writeInt(mKeyboardType); - - for (int i = 0; i <= MOTION_RANGE_LAST; i++) { - MotionRange range = mMotionRanges[i]; - if (range != null) { - out.writeInt(i); - out.writeFloat(range.mMin); - out.writeFloat(range.mMax); - out.writeFloat(range.mFlat); - out.writeFloat(range.mFuzz); - } + + final int numAxes = mMotionRanges.size(); + for (int i = 0; i < numAxes; i++) { + int axis = mMotionRanges.keyAt(i); + MotionRange range = mMotionRanges.valueAt(i); + out.writeInt(axis); + out.writeFloat(range.mMin); + out.writeFloat(range.mMax); + out.writeFloat(range.mFlat); + out.writeFloat(range.mFuzz); } out.writeInt(-1); } - + @Override public int describeContents() { return 0; } - + @Override public String toString() { StringBuilder description = new StringBuilder(); @@ -468,29 +503,32 @@ public final class InputDevice implements Parcelable { break; } description.append("\n"); - - description.append(" Sources:"); + + description.append(" Sources: ").append(Integer.toHexString(mSources)).append(" ("); appendSourceDescriptionIfApplicable(description, SOURCE_KEYBOARD, "keyboard"); appendSourceDescriptionIfApplicable(description, SOURCE_DPAD, "dpad"); appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHSCREEN, "touchscreen"); appendSourceDescriptionIfApplicable(description, SOURCE_MOUSE, "mouse"); appendSourceDescriptionIfApplicable(description, SOURCE_TRACKBALL, "trackball"); appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHPAD, "touchpad"); - description.append("\n"); - - appendRangeDescriptionIfApplicable(description, MOTION_RANGE_X, "x"); - appendRangeDescriptionIfApplicable(description, MOTION_RANGE_Y, "y"); - appendRangeDescriptionIfApplicable(description, MOTION_RANGE_PRESSURE, "pressure"); - appendRangeDescriptionIfApplicable(description, MOTION_RANGE_SIZE, "size"); - appendRangeDescriptionIfApplicable(description, MOTION_RANGE_TOUCH_MAJOR, "touchMajor"); - appendRangeDescriptionIfApplicable(description, MOTION_RANGE_TOUCH_MINOR, "touchMinor"); - appendRangeDescriptionIfApplicable(description, MOTION_RANGE_TOOL_MAJOR, "toolMajor"); - appendRangeDescriptionIfApplicable(description, MOTION_RANGE_TOOL_MINOR, "toolMinor"); - appendRangeDescriptionIfApplicable(description, MOTION_RANGE_ORIENTATION, "orientation"); - + appendSourceDescriptionIfApplicable(description, SOURCE_JOYSTICK, "joystick"); + appendSourceDescriptionIfApplicable(description, SOURCE_GAMEPAD, "gamepad"); + description.append(" )\n"); + + final int numAxes = mMotionRanges.size(); + for (int i = 0; i < numAxes; i++) { + int axis = mMotionRanges.keyAt(i); + MotionRange range = mMotionRanges.valueAt(i); + description.append(" ").append(MotionEvent.axisToString(axis)); + description.append(": min=").append(range.mMin); + description.append(" max=").append(range.mMax); + description.append(" flat=").append(range.mFlat); + description.append(" fuzz=").append(range.mFuzz); + description.append("\n"); + } return description.toString(); } - + private void appendSourceDescriptionIfApplicable(StringBuilder description, int source, String sourceName) { if ((mSources & source) == source) { @@ -498,17 +536,4 @@ public final class InputDevice implements Parcelable { description.append(sourceName); } } - - private void appendRangeDescriptionIfApplicable(StringBuilder description, - int rangeType, String rangeName) { - MotionRange range = mMotionRanges[rangeType]; - if (range != null) { - description.append(" Range[").append(rangeName); - description.append("]: min=").append(range.mMin); - description.append(" max=").append(range.mMax); - description.append(" flat=").append(range.mFlat); - description.append(" fuzz=").append(range.mFuzz); - description.append("\n"); - } - } } diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java index 184e0fc8daba..f6aeb398fa13 100755 --- a/core/java/android/view/InputEvent.java +++ b/core/java/android/view/InputEvent.java @@ -24,11 +24,6 @@ import android.os.Parcelable; */ public abstract class InputEvent implements Parcelable { /** @hide */ - protected int mDeviceId; - /** @hide */ - protected int mSource; - - /** @hide */ protected static final int PARCEL_TOKEN_MOTION_EVENT = 1; /** @hide */ protected static final int PARCEL_TOKEN_KEY_EVENT = 2; @@ -45,55 +40,37 @@ public abstract class InputEvent implements Parcelable { * @return The device id. * @see InputDevice#getDevice */ - public final int getDeviceId() { - return mDeviceId; - } - + public abstract int getDeviceId(); + /** * Gets the device that this event came from. * * @return The device, or null if unknown. */ public final InputDevice getDevice() { - return InputDevice.getDevice(mDeviceId); + return InputDevice.getDevice(getDeviceId()); } - + /** * Gets the source of the event. * * @return The event source or {@link InputDevice#SOURCE_UNKNOWN} if unknown. * @see InputDevice#getSourceInfo */ - public final int getSource() { - return mSource; - } - + public abstract int getSource(); + /** * Modifies the source of the event. - * @param source The source. - * + * + * @param source The new source. * @hide */ - public final void setSource(int source) { - mSource = source; - } - + public abstract void setSource(int source); + public int describeContents() { return 0; } - - /** @hide */ - protected final void readBaseFromParcel(Parcel in) { - mDeviceId = in.readInt(); - mSource = in.readInt(); - } - - /** @hide */ - protected final void writeBaseToParcel(Parcel out) { - out.writeInt(mDeviceId); - out.writeInt(mSource); - } - + public static final Parcelable.Creator<InputEvent> CREATOR = new Parcelable.Creator<InputEvent>() { public InputEvent createFromParcel(Parcel in) { diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 941331a224dc..3f6a04b09baf 100755 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -533,8 +533,40 @@ public class KeyEvent extends InputEvent implements Parcelable { /** Key code constant: App switch key. * Should bring up the application switcher dialog. */ public static final int KEYCODE_APP_SWITCH = 187; - - private static final int LAST_KEYCODE = KEYCODE_APP_SWITCH; + /** Key code constant: Generic Game Pad Button #1.*/ + public static final int KEYCODE_BUTTON_1 = 188; + /** Key code constant: Generic Game Pad Button #2.*/ + public static final int KEYCODE_BUTTON_2 = 189; + /** Key code constant: Generic Game Pad Button #3.*/ + public static final int KEYCODE_BUTTON_3 = 190; + /** Key code constant: Generic Game Pad Button #4.*/ + public static final int KEYCODE_BUTTON_4 = 191; + /** Key code constant: Generic Game Pad Button #5.*/ + public static final int KEYCODE_BUTTON_5 = 192; + /** Key code constant: Generic Game Pad Button #6.*/ + public static final int KEYCODE_BUTTON_6 = 193; + /** Key code constant: Generic Game Pad Button #7.*/ + public static final int KEYCODE_BUTTON_7 = 194; + /** Key code constant: Generic Game Pad Button #8.*/ + public static final int KEYCODE_BUTTON_8 = 195; + /** Key code constant: Generic Game Pad Button #9.*/ + public static final int KEYCODE_BUTTON_9 = 196; + /** Key code constant: Generic Game Pad Button #10.*/ + public static final int KEYCODE_BUTTON_10 = 197; + /** Key code constant: Generic Game Pad Button #11.*/ + public static final int KEYCODE_BUTTON_11 = 198; + /** Key code constant: Generic Game Pad Button #12.*/ + public static final int KEYCODE_BUTTON_12 = 199; + /** Key code constant: Generic Game Pad Button #13.*/ + public static final int KEYCODE_BUTTON_13 = 200; + /** Key code constant: Generic Game Pad Button #14.*/ + public static final int KEYCODE_BUTTON_14 = 201; + /** Key code constant: Generic Game Pad Button #15.*/ + public static final int KEYCODE_BUTTON_15 = 202; + /** Key code constant: Generic Game Pad Button #16.*/ + public static final int KEYCODE_BUTTON_16 = 203; + + private static final int LAST_KEYCODE = KEYCODE_BUTTON_16; // NOTE: If you add a new keycode here you must also add it to: // isSystem() @@ -741,6 +773,22 @@ public class KeyEvent extends InputEvent implements Parcelable { "KEYCODE_PROG_YELLOW", "KEYCODE_PROG_BLUE", "KEYCODE_APP_SWITCH", + "KEYCODE_BUTTON_1", + "KEYCODE_BUTTON_2", + "KEYCODE_BUTTON_3", + "KEYCODE_BUTTON_4", + "KEYCODE_BUTTON_5", + "KEYCODE_BUTTON_6", + "KEYCODE_BUTTON_7", + "KEYCODE_BUTTON_8", + "KEYCODE_BUTTON_9", + "KEYCODE_BUTTON_10", + "KEYCODE_BUTTON_11", + "KEYCODE_BUTTON_12", + "KEYCODE_BUTTON_13", + "KEYCODE_BUTTON_14", + "KEYCODE_BUTTON_15", + "KEYCODE_BUTTON_16", }; // Symbolic names of all metakeys in bit order from least significant to most significant. @@ -1132,6 +1180,8 @@ public class KeyEvent extends InputEvent implements Parcelable { private KeyEvent mNext; private boolean mRecycled; + private int mDeviceId; + private int mSource; private int mMetaState; private int mAction; private int mKeyCode; @@ -1603,6 +1653,23 @@ public class KeyEvent extends InputEvent implements Parcelable { return native_hasDefaultAction(mKeyCode); } + /** {@inheritDoc} */ + @Override + public final int getDeviceId() { + return mDeviceId; + } + + /** {@inheritDoc} */ + @Override + public final int getSource() { + return mSource; + } + + /** {@inheritDoc} */ + @Override + public final void setSource(int source) { + mSource = source; + } /** * <p>Returns the state of the meta keys.</p> @@ -2603,8 +2670,8 @@ public class KeyEvent extends InputEvent implements Parcelable { } private KeyEvent(Parcel in) { - readBaseFromParcel(in); - + mDeviceId = in.readInt(); + mSource = in.readInt(); mAction = in.readInt(); mKeyCode = in.readInt(); mRepeatCount = in.readInt(); @@ -2617,9 +2684,9 @@ public class KeyEvent extends InputEvent implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeInt(PARCEL_TOKEN_KEY_EVENT); - - writeBaseToParcel(out); - + + out.writeInt(mDeviceId); + out.writeInt(mSource); out.writeInt(mAction); out.writeInt(mKeyCode); out.writeInt(mRepeatCount); diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index b95de64aa4df..6673be2bbd3c 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -26,7 +26,8 @@ import android.os.SystemClock; * class may hold either absolute or relative movements, depending on what * it is being used for. * <p> - * On pointing devices such as touch screens, pointer coordinates specify absolute + * On pointing devices with source class {@link InputDevice#SOURCE_CLASS_POINTER} + * such as touch screens, the pointer coordinates specify absolute * positions such as view X/Y coordinates. Each complete gesture is represented * by a sequence of motion events with actions that describe pointer state transitions * and movements. A gesture starts with a motion event with {@link #ACTION_DOWN} @@ -38,11 +39,18 @@ import android.os.SystemClock; * by a motion event with {@link #ACTION_UP} or when gesture is canceled * with {@link #ACTION_CANCEL}. * </p><p> - * On trackballs, the pointer coordinates specify relative movements as X/Y deltas. + * On trackball devices with source class {@link InputDevice#SOURCE_CLASS_TRACKBALL}, + * the pointer coordinates specify relative movements as X/Y deltas. * A trackball gesture consists of a sequence of movements described by motion * events with {@link #ACTION_MOVE} interspersed with occasional {@link #ACTION_DOWN} * or {@link #ACTION_UP} motion events when the trackball button is pressed or released. * </p><p> + * On joystick devices with source class {@link InputDevice#SOURCE_CLASS_JOYSTICK}, + * the pointer coordinates specify the absolute position of the joystick axes. + * The joystick axis values are normalized to a range of -1.0 to 1.0 where 0.0 corresponds + * to the center position. More information about the set of available axes and the + * range of motion can be obtained using {@link InputDevice#getMotionRange}. + * </p><p> * Motion events always report movements for all pointers at once. The number * of pointers only ever changes by one as individual pointers go up and down, * except when the gesture is canceled. @@ -94,7 +102,7 @@ import android.os.SystemClock; * </p> */ public final class MotionEvent extends InputEvent implements Parcelable { - private static final long MS_PER_NS = 1000000; + private static final long NS_PER_MS = 1000000; private static final boolean TRACK_RECYCLED_LOCATION = false; /** @@ -253,123 +261,241 @@ public final class MotionEvent extends InputEvent implements Parcelable { */ public static final int EDGE_RIGHT = 0x00000008; - /* - * Offset for the sample's X coordinate. - */ - static private final int SAMPLE_X = 0; - - /* - * Offset for the sample's Y coordinate. - */ - static private final int SAMPLE_Y = 1; - - /* - * Offset for the sample's pressure. - */ - static private final int SAMPLE_PRESSURE = 2; - - /* - * Offset for the sample's size + /** + * Constant used to identify the X axis of a motion event. + * + * The interpretation of the X axis varies by input source. + * It may represent the X position of the center of the touch contact area, + * a relative horizontal displacement of a trackball or joystick, or something else. + * + * @see #getX(int) + * @see #getHistoricalX(int, int) + * @see MotionEvent.PointerCoords#x + * @see InputDevice#getMotionRange */ - static private final int SAMPLE_SIZE = 3; - - /* - * Offset for the sample's touch major axis length. + public static final int AXIS_X = 0; + + /** + * Constant used to identify the Y axis of a motion event. + * + * The interpretation of the Y axis varies by input source. + * It may represent the Y position of the center of the touch contact area, + * a relative vertical displacement of a trackball or joystick, or something else. + * + * @see #getY(int) + * @see #getHistoricalY(int, int) + * @see MotionEvent.PointerCoords#y + * @see InputDevice#getMotionRange */ - static private final int SAMPLE_TOUCH_MAJOR = 4; + public static final int AXIS_Y = 1; - /* - * Offset for the sample's touch minor axis length. + /** + * Constant used to identify the Pressure axis of a motion event. + * + * The pressure axis specifies a normalized value that describes the approximate + * pressure applied to the device by a finger or other tool. + * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure), + * although values higher than 1 may be generated depending on the calibration of + * the input device. + * + * @see #getPressure(int) + * @see #getHistoricalPressure(int, int) + * @see MotionEvent.PointerCoords#pressure + * @see InputDevice#getMotionRange */ - static private final int SAMPLE_TOUCH_MINOR = 5; - - /* - * Offset for the sample's tool major axis length. + public static final int AXIS_PRESSURE = 2; + + /** + * Constant used to identify the Size axis of a motion event. + * + * The size axis specifies a normalized value that describes the approximate size + * of the pointer touch area in relation to the maximum detectable size for the device. + * It represents some approximation of the area of the screen being + * pressed; the actual value in pixels corresponding to the + * touch is normalized with the device specific range of values + * and scaled to a value between 0 and 1. The value of size can be used to + * determine fat touch events. + * + * To obtain calibrated size information in terms of pixels, use + * {@link #AXIS_TOUCH_MAJOR} or {@link #AXIS_TOOL_MAJOR} instead. + * + * @see #getSize(int) + * @see #getHistoricalSize(int, int) + * @see MotionEvent.PointerCoords#size + * @see InputDevice#getMotionRange */ - static private final int SAMPLE_TOOL_MAJOR = 6; + public static final int AXIS_SIZE = 3; - /* - * Offset for the sample's tool minor axis length. + /** + * Constant used to identify the TouchMajor axis of a motion event. + * + * The touch major axis specifies the length of the major axis of an ellipse that + * describes the touch area at the point of contact. + * If the device is a touch screen, the length is reported in pixels, otherwise it is + * reported in device-specific units. + * + * @see #getTouchMajor(int) + * @see #getHistoricalTouchMajor(int, int) + * @see MotionEvent.PointerCoords#touchMajor + * @see InputDevice#getMotionRange */ - static private final int SAMPLE_TOOL_MINOR = 7; - - /* - * Offset for the sample's orientation. + public static final int AXIS_TOUCH_MAJOR = 4; + + /** + * Constant used to identify the TouchMinor axis of a motion event. + * + * The touch major axis specifies the length of the minor axis of an ellipse that + * describes the touch area at the point of contact. + * If the device is a touch screen, the length is reported in pixels, otherwise it is + * reported in device-specific units. + * + * @see #getTouchMinor(int) + * @see #getHistoricalTouchMinor(int, int) + * @see MotionEvent.PointerCoords#touchMinor + * @see InputDevice#getMotionRange */ - static private final int SAMPLE_ORIENTATION = 8; + public static final int AXIS_TOUCH_MINOR = 5; - /* - * Number of data items for each sample. + /** + * Constant used to identify the ToolMajor axis of a motion event. + * + * The tool major axis specifies the length of the major axis of an ellipse that + * describes the size of the approaching tool. + * The tool area represents the estimated size of the finger or pen that is + * touching the device independent of its actual touch area at the point of contact. + * If the device is a touch screen, the length is reported in pixels, otherwise it is + * reported in device-specific units. + * + * @see #getToolMajor(int) + * @see #getHistoricalToolMajor(int, int) + * @see MotionEvent.PointerCoords#toolMajor + * @see InputDevice#getMotionRange */ - static private final int NUM_SAMPLE_DATA = 9; - - /* - * Minimum number of pointers for which to reserve space when allocating new - * motion events. This is explicitly not a bound on the maximum number of pointers. + public static final int AXIS_TOOL_MAJOR = 6; + + /** + * Constant used to identify the ToolMinor axis of a motion event. + * + * The tool minor axis specifies the length of the major axis of an ellipse that + * describes the size of the approaching tool. + * The tool area represents the estimated size of the finger or pen that is + * touching the device independent of its actual touch area at the point of contact. + * If the device is a touch screen, the length is reported in pixels, otherwise it is + * reported in device-specific units. + * + * @see #getToolMinor(int) + * @see #getHistoricalToolMinor(int, int) + * @see MotionEvent.PointerCoords#toolMinor + * @see InputDevice#getMotionRange */ - static private final int BASE_AVAIL_POINTERS = 5; - - /* - * Minimum number of samples for which to reserve space when allocating new motion events. + public static final int AXIS_TOOL_MINOR = 7; + + /** + * Constant used to identify the Orientation axis of a motion event. + * + * The orientation axis specifies the orientation of the touch area and tool area in + * radians clockwise from vertical relative to the vertical plane of the device. + * An angle of 0 degrees indicates that the major axis of contact is oriented + * upwards, is perfectly circular or is of unknown orientation. A positive angle + * indicates that the major axis of contact is oriented to the right. A negative angle + * indicates that the major axis of contact is oriented to the left. + * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians + * (finger pointing fully right). + * + * @see #getOrientation(int) + * @see #getHistoricalOrientation(int, int) + * @see MotionEvent.PointerCoords#orientation + * @see InputDevice#getMotionRange */ - static private final int BASE_AVAIL_SAMPLES = 8; - + public static final int AXIS_ORIENTATION = 8; + + // Private value for history pos that obtains the current sample. + private static final int HISTORY_CURRENT = -0x80000000; + private static final int MAX_RECYCLED = 10; private static final Object gRecyclerLock = new Object(); private static int gRecyclerUsed; private static MotionEvent gRecyclerTop; - private long mDownTimeNano; - private int mAction; - private float mXOffset; - private float mYOffset; - private float mXPrecision; - private float mYPrecision; - private int mEdgeFlags; - private int mMetaState; - private int mFlags; - - private int mNumPointers; - private int mNumSamples; - - private int mLastDataSampleIndex; - private int mLastEventTimeNanoSampleIndex; - - // Array of mNumPointers size of identifiers for each pointer of data. - private int[] mPointerIdentifiers; - - // Array of (mNumSamples * mNumPointers * NUM_SAMPLE_DATA) size of event data. - // Samples are ordered from oldest to newest. - private float[] mDataSamples; - - // Array of mNumSamples size of event time stamps in nanoseconds. - // Samples are ordered from oldest to newest. - private long[] mEventTimeNanoSamples; + // Shared temporary objects used when translating coordinates supplied by + // the caller into single element PointerCoords and pointer id arrays. + // Must lock gTmpPointerCoords prior to use. + private static final PointerCoords[] gTmpPointerCoords = + new PointerCoords[] { new PointerCoords() }; + private static final int[] gTmpPointerIds = new int[] { 0 /*always 0*/ }; + + // Pointer to the native MotionEvent object that contains the actual data. + private int mNativePtr; private MotionEvent mNext; private RuntimeException mRecycledLocation; private boolean mRecycled; - private native void nativeTransform(Matrix matrix); + private static native int nativeInitialize(int nativePtr, + int deviceId, int source, int action, int flags, int edgeFlags, int metaState, + float xOffset, float yOffset, float xPrecision, float yPrecision, + long downTimeNanos, long eventTimeNanos, + int pointerCount, int[] pointerIds, PointerCoords[] pointerCoords); + private static native int nativeCopy(int destNativePtr, int sourceNativePtr, + boolean keepHistory); + private static native void nativeDispose(int nativePtr); + private static native void nativeAddBatch(int nativePtr, long eventTimeNanos, + PointerCoords[] pointerCoords, int metaState); + + private static native int nativeGetDeviceId(int nativePtr); + private static native int nativeGetSource(int nativePtr); + private static native int nativeSetSource(int nativePtr, int source); + private static native int nativeGetAction(int nativePtr); + private static native void nativeSetAction(int nativePtr, int action); + private static native int nativeGetFlags(int nativePtr); + private static native int nativeGetEdgeFlags(int nativePtr); + private static native void nativeSetEdgeFlags(int nativePtr, int action); + private static native int nativeGetMetaState(int nativePtr); + private static native void nativeOffsetLocation(int nativePtr, float deltaX, float deltaY); + private static native float nativeGetXPrecision(int nativePtr); + private static native float nativeGetYPrecision(int nativePtr); + private static native long nativeGetDownTimeNanos(int nativePtr); + + private static native int nativeGetPointerCount(int nativePtr); + private static native int nativeGetPointerId(int nativePtr, int pointerIndex); + private static native int nativeFindPointerIndex(int nativePtr, int pointerId); + + private static native int nativeGetHistorySize(int nativePtr); + private static native long nativeGetEventTimeNanos(int nativePtr, int historyPos); + private static native float nativeGetRawAxisValue(int nativePtr, + int axis, int pointerIndex, int historyPos); + private static native float nativeGetAxisValue(int nativePtr, + int axis, int pointerIndex, int historyPos); + private static native void nativeGetPointerCoords(int nativePtr, + int pointerIndex, int historyPos, PointerCoords outPointerCoords); + + private static native void nativeScale(int nativePtr, float scale); + private static native void nativeTransform(int nativePtr, Matrix matrix); + + private static native int nativeReadFromParcel(int nativePtr, Parcel parcel); + private static native void nativeWriteToParcel(int nativePtr, Parcel parcel); + + private MotionEvent() { + } - private MotionEvent(int pointerCount, int sampleCount) { - mPointerIdentifiers = new int[pointerCount]; - mDataSamples = new float[pointerCount * sampleCount * NUM_SAMPLE_DATA]; - mEventTimeNanoSamples = new long[sampleCount]; + @Override + protected void finalize() throws Throwable { + try { + if (mNativePtr != 0) { + nativeDispose(mNativePtr); + mNativePtr = 0; + } + } finally { + super.finalize(); + } } - static private MotionEvent obtain(int pointerCount, int sampleCount) { + static private MotionEvent obtain() { final MotionEvent ev; synchronized (gRecyclerLock) { ev = gRecyclerTop; if (ev == null) { - if (pointerCount < BASE_AVAIL_POINTERS) { - pointerCount = BASE_AVAIL_POINTERS; - } - if (sampleCount < BASE_AVAIL_SAMPLES) { - sampleCount = BASE_AVAIL_SAMPLES; - } - return new MotionEvent(pointerCount, sampleCount); + return new MotionEvent(); } gRecyclerTop = ev.mNext; gRecyclerUsed -= 1; @@ -377,23 +503,9 @@ public final class MotionEvent extends InputEvent implements Parcelable { ev.mRecycledLocation = null; ev.mRecycled = false; ev.mNext = null; - - if (ev.mPointerIdentifiers.length < pointerCount) { - ev.mPointerIdentifiers = new int[pointerCount]; - } - - if (ev.mEventTimeNanoSamples.length < sampleCount) { - ev.mEventTimeNanoSamples = new long[sampleCount]; - } - - final int neededDataSamplesLength = pointerCount * sampleCount * NUM_SAMPLE_DATA; - if (ev.mDataSamples.length < neededDataSamplesLength) { - ev.mDataSamples = new float[neededDataSamplesLength]; - } - return ev; } - + /** * Create a new MotionEvent, filling in all of the basic values that * define the motion. @@ -426,34 +538,15 @@ public final class MotionEvent extends InputEvent implements Parcelable { int action, int pointers, int[] pointerIds, PointerCoords[] pointerCoords, int metaState, float xPrecision, float yPrecision, int deviceId, int edgeFlags, int source, int flags) { - MotionEvent ev = obtain(pointers, 1); - ev.mDeviceId = deviceId; - ev.mSource = source; - ev.mEdgeFlags = edgeFlags; - ev.mDownTimeNano = downTime * MS_PER_NS; - ev.mAction = action; - ev.mFlags = flags; - ev.mMetaState = metaState; - ev.mXOffset = 0; - ev.mYOffset = 0; - ev.mXPrecision = xPrecision; - ev.mYPrecision = yPrecision; - - ev.mNumPointers = pointers; - ev.mNumSamples = 1; - - ev.mLastDataSampleIndex = 0; - ev.mLastEventTimeNanoSampleIndex = 0; - - System.arraycopy(pointerIds, 0, ev.mPointerIdentifiers, 0, pointers); - - ev.mEventTimeNanoSamples[0] = eventTime * MS_PER_NS; - - ev.setPointerCoordsAtSampleIndex(0, pointerCoords); - + MotionEvent ev = obtain(); + ev.mNativePtr = nativeInitialize(ev.mNativePtr, + deviceId, source, action, flags, edgeFlags, metaState, + 0, 0, xPrecision, yPrecision, + downTime * NS_PER_MS, eventTime * NS_PER_MS, + pointers, pointerIds, pointerCoords); return ev; } - + /** * Create a new MotionEvent, filling in all of the basic values that * define the motion. @@ -488,31 +581,22 @@ public final class MotionEvent extends InputEvent implements Parcelable { static public MotionEvent obtain(long downTime, long eventTime, int action, float x, float y, float pressure, float size, int metaState, float xPrecision, float yPrecision, int deviceId, int edgeFlags) { - MotionEvent ev = obtain(1, 1); - ev.mDeviceId = deviceId; - ev.mSource = InputDevice.SOURCE_UNKNOWN; - ev.mEdgeFlags = edgeFlags; - ev.mDownTimeNano = downTime * MS_PER_NS; - ev.mAction = action; - ev.mFlags = 0; - ev.mMetaState = metaState; - ev.mXOffset = 0; - ev.mYOffset = 0; - ev.mXPrecision = xPrecision; - ev.mYPrecision = yPrecision; - - ev.mNumPointers = 1; - ev.mNumSamples = 1; - - ev.mLastDataSampleIndex = 0; - ev.mLastEventTimeNanoSampleIndex = 0; - - ev.mPointerIdentifiers[0] = 0; - - ev.mEventTimeNanoSamples[0] = eventTime * MS_PER_NS; - - ev.setPointerCoordsAtSampleIndex(0, x, y, pressure, size); - return ev; + synchronized (gTmpPointerCoords) { + final PointerCoords pc = gTmpPointerCoords[0]; + pc.clear(); + pc.x = x; + pc.y = y; + pc.pressure = pressure; + pc.size = size; + + MotionEvent ev = obtain(); + ev.mNativePtr = nativeInitialize(ev.mNativePtr, + deviceId, InputDevice.SOURCE_UNKNOWN, action, 0, edgeFlags, metaState, + 0, 0, xPrecision, yPrecision, + downTime * NS_PER_MS, eventTime * NS_PER_MS, + 1, gTmpPointerIds, gTmpPointerCoords); + return ev; + } } /** @@ -584,31 +668,13 @@ public final class MotionEvent extends InputEvent implements Parcelable { /** * Create a new MotionEvent, copying from an existing one. */ - static public MotionEvent obtain(MotionEvent o) { - MotionEvent ev = obtain(o.mNumPointers, o.mNumSamples); - ev.mDeviceId = o.mDeviceId; - ev.mSource = o.mSource; - ev.mEdgeFlags = o.mEdgeFlags; - ev.mDownTimeNano = o.mDownTimeNano; - ev.mAction = o.mAction; - ev.mFlags = o.mFlags; - ev.mMetaState = o.mMetaState; - ev.mXOffset = o.mXOffset; - ev.mYOffset = o.mYOffset; - ev.mXPrecision = o.mXPrecision; - ev.mYPrecision = o.mYPrecision; - int numPointers = ev.mNumPointers = o.mNumPointers; - int numSamples = ev.mNumSamples = o.mNumSamples; - - ev.mLastDataSampleIndex = o.mLastDataSampleIndex; - ev.mLastEventTimeNanoSampleIndex = o.mLastEventTimeNanoSampleIndex; - - System.arraycopy(o.mPointerIdentifiers, 0, ev.mPointerIdentifiers, 0, numPointers); - - System.arraycopy(o.mEventTimeNanoSamples, 0, ev.mEventTimeNanoSamples, 0, numSamples); - - System.arraycopy(o.mDataSamples, 0, ev.mDataSamples, 0, - numPointers * numSamples * NUM_SAMPLE_DATA); + static public MotionEvent obtain(MotionEvent other) { + if (other == null) { + throw new IllegalArgumentException("other motion event must not be null"); + } + + MotionEvent ev = obtain(); + ev.mNativePtr = nativeCopy(ev.mNativePtr, other.mNativePtr, true /*keepHistory*/); return ev; } @@ -616,32 +682,13 @@ public final class MotionEvent extends InputEvent implements Parcelable { * Create a new MotionEvent, copying from an existing one, but not including * any historical point information. */ - static public MotionEvent obtainNoHistory(MotionEvent o) { - MotionEvent ev = obtain(o.mNumPointers, 1); - ev.mDeviceId = o.mDeviceId; - ev.mSource = o.mSource; - ev.mEdgeFlags = o.mEdgeFlags; - ev.mDownTimeNano = o.mDownTimeNano; - ev.mAction = o.mAction; - o.mFlags = o.mFlags; - ev.mMetaState = o.mMetaState; - ev.mXOffset = o.mXOffset; - ev.mYOffset = o.mYOffset; - ev.mXPrecision = o.mXPrecision; - ev.mYPrecision = o.mYPrecision; - - int numPointers = ev.mNumPointers = o.mNumPointers; - ev.mNumSamples = 1; - - ev.mLastDataSampleIndex = 0; - ev.mLastEventTimeNanoSampleIndex = 0; - - System.arraycopy(o.mPointerIdentifiers, 0, ev.mPointerIdentifiers, 0, numPointers); - - ev.mEventTimeNanoSamples[0] = o.mEventTimeNanoSamples[o.mLastEventTimeNanoSampleIndex]; - - System.arraycopy(o.mDataSamples, o.mLastDataSampleIndex, ev.mDataSamples, 0, - numPointers * NUM_SAMPLE_DATA); + static public MotionEvent obtainNoHistory(MotionEvent other) { + if (other == null) { + throw new IllegalArgumentException("other motion event must not be null"); + } + + MotionEvent ev = obtain(); + ev.mNativePtr = nativeCopy(ev.mNativePtr, other.mNativePtr, false /*keepHistory*/); return ev; } @@ -667,7 +714,6 @@ public final class MotionEvent extends InputEvent implements Parcelable { synchronized (gRecyclerLock) { if (gRecyclerUsed < MAX_RECYCLED) { gRecyclerUsed++; - mNumSamples = 0; mNext = gRecyclerTop; gRecyclerTop = this; } @@ -680,23 +726,25 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @hide */ public final void scale(float scale) { - mXOffset *= scale; - mYOffset *= scale; - mXPrecision *= scale; - mYPrecision *= scale; - - float[] history = mDataSamples; - final int length = mNumPointers * mNumSamples * NUM_SAMPLE_DATA; - for (int i = 0; i < length; i += NUM_SAMPLE_DATA) { - history[i + SAMPLE_X] *= scale; - history[i + SAMPLE_Y] *= scale; - // no need to scale pressure - history[i + SAMPLE_SIZE] *= scale; // TODO: square this? - history[i + SAMPLE_TOUCH_MAJOR] *= scale; - history[i + SAMPLE_TOUCH_MINOR] *= scale; - history[i + SAMPLE_TOOL_MAJOR] *= scale; - history[i + SAMPLE_TOOL_MINOR] *= scale; - } + nativeScale(mNativePtr, scale); + } + + /** {@inheritDoc} */ + @Override + public final int getDeviceId() { + return nativeGetDeviceId(mNativePtr); + } + + /** {@inheritDoc} */ + @Override + public final int getSource() { + return nativeGetSource(mNativePtr); + } + + /** {@inheritDoc} */ + @Override + public final void setSource(int source) { + nativeSetSource(mNativePtr, source); } /** @@ -707,7 +755,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { * and pointer index. */ public final int getAction() { - return mAction; + return nativeGetAction(mNativePtr); } /** @@ -719,7 +767,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { * pointer actions. */ public final int getActionMasked() { - return mAction & ACTION_MASK; + return nativeGetAction(mNativePtr) & ACTION_MASK; } /** @@ -731,7 +779,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { * gone down or up. */ public final int getActionIndex() { - return (mAction & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT; + return (nativeGetAction(mNativePtr) & ACTION_POINTER_INDEX_MASK) + >> ACTION_POINTER_INDEX_SHIFT; } /** @@ -740,7 +789,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @see #FLAG_WINDOW_IS_OBSCURED */ public final int getFlags() { - return mFlags; + return nativeGetFlags(mNativePtr); } /** @@ -748,14 +797,14 @@ public final class MotionEvent extends InputEvent implements Parcelable { * a stream of position events. */ public final long getDownTime() { - return mDownTimeNano / MS_PER_NS; + return nativeGetDownTimeNanos(mNativePtr) / NS_PER_MS; } /** * Returns the time (in ms) when this specific event was generated. */ public final long getEventTime() { - return mEventTimeNanoSamples[mLastEventTimeNanoSampleIndex] / MS_PER_NS; + return nativeGetEventTimeNanos(mNativePtr, HISTORY_CURRENT) / NS_PER_MS; } /** @@ -765,79 +814,110 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @hide */ public final long getEventTimeNano() { - return mEventTimeNanoSamples[mLastEventTimeNanoSampleIndex]; + return nativeGetEventTimeNanos(mNativePtr, HISTORY_CURRENT); } /** * {@link #getX(int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @see #AXIS_X */ public final float getX() { - return mDataSamples[mLastDataSampleIndex + SAMPLE_X] + mXOffset; + return nativeGetAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT); } /** * {@link #getY(int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @see #AXIS_Y */ public final float getY() { - return mDataSamples[mLastDataSampleIndex + SAMPLE_Y] + mYOffset; + return nativeGetAxisValue(mNativePtr, AXIS_Y, 0, HISTORY_CURRENT); } /** * {@link #getPressure(int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @see #AXIS_PRESSURE */ public final float getPressure() { - return mDataSamples[mLastDataSampleIndex + SAMPLE_PRESSURE]; + return nativeGetAxisValue(mNativePtr, AXIS_PRESSURE, 0, HISTORY_CURRENT); } /** * {@link #getSize(int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @see #AXIS_SIZE */ public final float getSize() { - return mDataSamples[mLastDataSampleIndex + SAMPLE_SIZE]; + return nativeGetAxisValue(mNativePtr, AXIS_SIZE, 0, HISTORY_CURRENT); } /** * {@link #getTouchMajor(int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @see #AXIS_TOUCH_MAJOR */ public final float getTouchMajor() { - return mDataSamples[mLastDataSampleIndex + SAMPLE_TOUCH_MAJOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MAJOR, 0, HISTORY_CURRENT); } /** * {@link #getTouchMinor(int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @see #AXIS_TOUCH_MINOR */ public final float getTouchMinor() { - return mDataSamples[mLastDataSampleIndex + SAMPLE_TOUCH_MINOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MINOR, 0, HISTORY_CURRENT); } /** * {@link #getToolMajor(int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @see #AXIS_TOOL_MAJOR */ public final float getToolMajor() { - return mDataSamples[mLastDataSampleIndex + SAMPLE_TOOL_MAJOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MAJOR, 0, HISTORY_CURRENT); } /** * {@link #getToolMinor(int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @see #AXIS_TOOL_MINOR */ public final float getToolMinor() { - return mDataSamples[mLastDataSampleIndex + SAMPLE_TOOL_MINOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MINOR, 0, HISTORY_CURRENT); } - + /** * {@link #getOrientation(int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @see #AXIS_ORIENTATION */ public final float getOrientation() { - return mDataSamples[mLastDataSampleIndex + SAMPLE_ORIENTATION]; + return nativeGetAxisValue(mNativePtr, AXIS_ORIENTATION, 0, HISTORY_CURRENT); + } + + /** + * {@link #getAxisValue(int)} for the first pointer index (may be an + * arbitrary pointer identifier). + * + * @param axis The axis identifier for the axis value to retrieve. + * + * @see #AXIS_X + * @see #AXIS_Y + */ + public final float getAxisValue(int axis) { + return nativeGetAxisValue(mNativePtr, axis, 0, HISTORY_CURRENT); } /** @@ -845,7 +925,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { * >= 1. */ public final int getPointerCount() { - return mNumPointers; + return nativeGetPointerCount(mNativePtr); } /** @@ -857,7 +937,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { * (the first pointer that is down) to {@link #getPointerCount()}-1. */ public final int getPointerId(int pointerIndex) { - return mPointerIdentifiers[pointerIndex]; + return nativeGetPointerId(mNativePtr, pointerIndex); } /** @@ -869,14 +949,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { * that pointer identifier. */ public final int findPointerIndex(int pointerId) { - int i = mNumPointers; - while (i > 0) { - i--; - if (mPointerIdentifiers[i] == pointerId) { - return i; - } - } - return -1; + return nativeFindPointerIndex(mNativePtr, pointerId); } /** @@ -887,10 +960,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * value may have a fraction for input devices that are sub-pixel precise. * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 * (the first pointer that is down) to {@link #getPointerCount()}-1. + * + * @see #AXIS_X */ public final float getX(int pointerIndex) { - return mDataSamples[mLastDataSampleIndex - + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_X] + mXOffset; + return nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT); } /** @@ -901,10 +975,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * value may have a fraction for input devices that are sub-pixel precise. * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 * (the first pointer that is down) to {@link #getPointerCount()}-1. + * + * @see #AXIS_Y */ public final float getY(int pointerIndex) { - return mDataSamples[mLastDataSampleIndex - + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_Y] + mYOffset; + return nativeGetAxisValue(mNativePtr, AXIS_Y, pointerIndex, HISTORY_CURRENT); } /** @@ -917,10 +992,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * the input device. * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 * (the first pointer that is down) to {@link #getPointerCount()}-1. + * + * @see #AXIS_PRESSURE */ public final float getPressure(int pointerIndex) { - return mDataSamples[mLastDataSampleIndex - + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_PRESSURE]; + return nativeGetAxisValue(mNativePtr, AXIS_PRESSURE, pointerIndex, HISTORY_CURRENT); } /** @@ -934,10 +1010,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * determine fat touch events. * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 * (the first pointer that is down) to {@link #getPointerCount()}-1. + * + * @see #AXIS_SIZE */ public final float getSize(int pointerIndex) { - return mDataSamples[mLastDataSampleIndex - + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_SIZE]; + return nativeGetAxisValue(mNativePtr, AXIS_SIZE, pointerIndex, HISTORY_CURRENT); } /** @@ -947,10 +1024,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * identifier for this index). * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 * (the first pointer that is down) to {@link #getPointerCount()}-1. + * + * @see #AXIS_TOUCH_MAJOR */ public final float getTouchMajor(int pointerIndex) { - return mDataSamples[mLastDataSampleIndex - + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_TOUCH_MAJOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MAJOR, pointerIndex, HISTORY_CURRENT); } /** @@ -960,10 +1038,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * identifier for this index). * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 * (the first pointer that is down) to {@link #getPointerCount()}-1. + * + * @see #AXIS_TOUCH_MINOR */ public final float getTouchMinor(int pointerIndex) { - return mDataSamples[mLastDataSampleIndex - + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_TOUCH_MINOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MINOR, pointerIndex, HISTORY_CURRENT); } /** @@ -975,10 +1054,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * touching the device independent of its actual touch area at the point of contact. * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 * (the first pointer that is down) to {@link #getPointerCount()}-1. + * + * @see #AXIS_TOOL_MAJOR */ public final float getToolMajor(int pointerIndex) { - return mDataSamples[mLastDataSampleIndex - + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_TOOL_MAJOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MAJOR, pointerIndex, HISTORY_CURRENT); } /** @@ -990,10 +1070,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * touching the device independent of its actual touch area at the point of contact. * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 * (the first pointer that is down) to {@link #getPointerCount()}-1. + * + * @see #AXIS_TOOL_MINOR */ public final float getToolMinor(int pointerIndex) { - return mDataSamples[mLastDataSampleIndex - + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_TOOL_MINOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MINOR, pointerIndex, HISTORY_CURRENT); } /** @@ -1008,12 +1089,29 @@ public final class MotionEvent extends InputEvent implements Parcelable { * (finger pointing fully right). * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 * (the first pointer that is down) to {@link #getPointerCount()}-1. + * + * @see #AXIS_ORIENTATION */ public final float getOrientation(int pointerIndex) { - return mDataSamples[mLastDataSampleIndex - + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_ORIENTATION]; + return nativeGetAxisValue(mNativePtr, AXIS_ORIENTATION, pointerIndex, HISTORY_CURRENT); } - + + /** + * Returns the value of the requested axis for the given pointer <em>index</em> + * (use {@link #getPointerId(int)} to find the pointer identifier for this index). + * + * @param axis The axis identifier for the axis value to retrieve. + * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 + * (the first pointer that is down) to {@link #getPointerCount()}-1. + * @return The value of the axis, or 0 if the axis is not available. + * + * @see #AXIS_X + * @see #AXIS_Y + */ + public final float getAxisValue(int axis, int pointerIndex) { + return nativeGetAxisValue(mNativePtr, axis, pointerIndex, HISTORY_CURRENT); + } + /** * Populates a {@link PointerCoords} object with pointer coordinate data for * the specified pointer index. @@ -1021,10 +1119,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 * (the first pointer that is down) to {@link #getPointerCount()}-1. * @param outPointerCoords The pointer coordinate object to populate. + * + * @see PointerCoords */ public final void getPointerCoords(int pointerIndex, PointerCoords outPointerCoords) { - final int sampleIndex = mLastDataSampleIndex + pointerIndex * NUM_SAMPLE_DATA; - getPointerCoordsAtSampleIndex(sampleIndex, outPointerCoords); + nativeGetPointerCoords(mNativePtr, pointerIndex, HISTORY_CURRENT, outPointerCoords); } /** @@ -1038,7 +1137,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @see KeyEvent#getMetaState() */ public final int getMetaState() { - return mMetaState; + return nativeGetMetaState(mNativePtr); } /** @@ -1046,39 +1145,49 @@ public final class MotionEvent extends InputEvent implements Parcelable { * events on the screen, this is the original location of the event * on the screen, before it had been adjusted for the containing window * and views. + * + * @see getX() + * @see #AXIS_X */ public final float getRawX() { - return mDataSamples[mLastDataSampleIndex + SAMPLE_X]; + return nativeGetRawAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT); } - + /** * Returns the original raw Y coordinate of this event. For touch * events on the screen, this is the original location of the event * on the screen, before it had been adjusted for the containing window * and views. + * + * @see getY() + * @see #AXIS_Y */ public final float getRawY() { - return mDataSamples[mLastDataSampleIndex + SAMPLE_Y]; + return nativeGetRawAxisValue(mNativePtr, AXIS_Y, 0, HISTORY_CURRENT); } /** * Return the precision of the X coordinates being reported. You can - * multiple this number with {@link #getX} to find the actual hardware + * multiply this number with {@link #getX} to find the actual hardware * value of the X coordinate. * @return Returns the precision of X coordinates being reported. + * + * @see #AXIS_X */ public final float getXPrecision() { - return mXPrecision; + return nativeGetXPrecision(mNativePtr); } /** * Return the precision of the Y coordinates being reported. You can - * multiple this number with {@link #getY} to find the actual hardware + * multiply this number with {@link #getY} to find the actual hardware * value of the Y coordinate. * @return Returns the precision of Y coordinates being reported. + * + * @see #AXIS_Y */ public final float getYPrecision() { - return mYPrecision; + return nativeGetYPrecision(mNativePtr); } /** @@ -1090,7 +1199,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @return Returns the number of historical points in the event. */ public final int getHistorySize() { - return mLastEventTimeNanoSampleIndex; + return nativeGetHistorySize(mNativePtr); } /** @@ -1104,81 +1213,161 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @see #getEventTime */ public final long getHistoricalEventTime(int pos) { - return mEventTimeNanoSamples[pos] / MS_PER_NS; + return nativeGetEventTimeNanos(mNativePtr, pos) / NS_PER_MS; } /** - * {@link #getHistoricalX(int)} for the first pointer index (may be an + * {@link #getHistoricalX(int, int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @param pos Which historical value to return; must be less than + * {@link #getHistorySize} + * + * @see #getHistorySize + * @see #getX() + * @see #AXIS_X */ public final float getHistoricalX(int pos) { - return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_X] + mXOffset; + return nativeGetAxisValue(mNativePtr, AXIS_X, 0, pos); } /** - * {@link #getHistoricalY(int)} for the first pointer index (may be an + * {@link #getHistoricalY(int, int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @param pos Which historical value to return; must be less than + * {@link #getHistorySize} + * + * @see #getHistorySize + * @see #getY() + * @see #AXIS_Y */ public final float getHistoricalY(int pos) { - return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_Y] + mYOffset; + return nativeGetAxisValue(mNativePtr, AXIS_Y, 0, pos); } /** - * {@link #getHistoricalPressure(int)} for the first pointer index (may be an + * {@link #getHistoricalPressure(int, int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @param pos Which historical value to return; must be less than + * {@link #getHistorySize} + * + * @see #getHistorySize + * @see #getPressure() + * @see #AXIS_PRESSURE */ public final float getHistoricalPressure(int pos) { - return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_PRESSURE]; + return nativeGetAxisValue(mNativePtr, AXIS_PRESSURE, 0, pos); } /** - * {@link #getHistoricalSize(int)} for the first pointer index (may be an + * {@link #getHistoricalSize(int, int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @param pos Which historical value to return; must be less than + * {@link #getHistorySize} + * + * @see #getHistorySize + * @see #getSize() + * @see #AXIS_SIZE */ public final float getHistoricalSize(int pos) { - return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_SIZE]; + return nativeGetAxisValue(mNativePtr, AXIS_SIZE, 0, pos); } /** - * {@link #getHistoricalTouchMajor(int)} for the first pointer index (may be an + * {@link #getHistoricalTouchMajor(int, int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @param pos Which historical value to return; must be less than + * {@link #getHistorySize} + * + * @see #getHistorySize + * @see #getTouchMajor() + * @see #AXIS_TOUCH_MAJOR */ public final float getHistoricalTouchMajor(int pos) { - return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_TOUCH_MAJOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MAJOR, 0, pos); } /** - * {@link #getHistoricalTouchMinor(int)} for the first pointer index (may be an + * {@link #getHistoricalTouchMinor(int, int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @param pos Which historical value to return; must be less than + * {@link #getHistorySize} + * + * @see #getHistorySize + * @see #getTouchMinor() + * @see #AXIS_TOUCH_MINOR */ public final float getHistoricalTouchMinor(int pos) { - return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_TOUCH_MINOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MINOR, 0, pos); } /** - * {@link #getHistoricalToolMajor(int)} for the first pointer index (may be an + * {@link #getHistoricalToolMajor(int, int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @param pos Which historical value to return; must be less than + * {@link #getHistorySize} + * + * @see #getHistorySize + * @see #getToolMajor() + * @see #AXIS_TOOL_MAJOR */ public final float getHistoricalToolMajor(int pos) { - return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_TOOL_MAJOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MAJOR, 0, pos); } /** - * {@link #getHistoricalToolMinor(int)} for the first pointer index (may be an + * {@link #getHistoricalToolMinor(int, int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @param pos Which historical value to return; must be less than + * {@link #getHistorySize} + * + * @see #getHistorySize + * @see #getToolMinor() + * @see #AXIS_TOOL_MINOR */ public final float getHistoricalToolMinor(int pos) { - return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_TOOL_MINOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MINOR, 0, pos); } /** - * {@link #getHistoricalOrientation(int)} for the first pointer index (may be an + * {@link #getHistoricalOrientation(int, int)} for the first pointer index (may be an * arbitrary pointer identifier). + * + * @param pos Which historical value to return; must be less than + * {@link #getHistorySize} + * + * @see #getHistorySize + * @see #getOrientation() + * @see #AXIS_ORIENTATION */ public final float getHistoricalOrientation(int pos) { - return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_ORIENTATION]; + return nativeGetAxisValue(mNativePtr, AXIS_ORIENTATION, 0, pos); } - + + /** + * {@link #getHistoricalAxisValue(int, int, int)} for the first pointer index (may be an + * arbitrary pointer identifier). + * + * @param axis The axis identifier for the axis value to retrieve. + * @param pos Which historical value to return; must be less than + * {@link #getHistorySize} + * + * @see #getHistorySize + * @see #getAxisValue(int) + * @see #AXIS_X + * @see #AXIS_Y + */ + public final float getHistoricalAxisValue(int axis, int pos) { + return nativeGetAxisValue(mNativePtr, axis, 0, pos); + } + /** * Returns a historical X coordinate, as per {@link #getX(int)}, that * occurred between this event and the previous event for the given pointer. @@ -1190,11 +1379,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * {@link #getHistorySize} * * @see #getHistorySize - * @see #getX + * @see #getX(int) + * @see #AXIS_X */ public final float getHistoricalX(int pointerIndex, int pos) { - return mDataSamples[(pos * mNumPointers + pointerIndex) - * NUM_SAMPLE_DATA + SAMPLE_X] + mXOffset; + return nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, pos); } /** @@ -1208,11 +1397,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * {@link #getHistorySize} * * @see #getHistorySize - * @see #getY + * @see #getY(int) + * @see #AXIS_Y */ public final float getHistoricalY(int pointerIndex, int pos) { - return mDataSamples[(pos * mNumPointers + pointerIndex) - * NUM_SAMPLE_DATA + SAMPLE_Y] + mYOffset; + return nativeGetAxisValue(mNativePtr, AXIS_Y, pointerIndex, pos); } /** @@ -1226,11 +1415,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * {@link #getHistorySize} * * @see #getHistorySize - * @see #getPressure + * @see #getPressure(int) + * @see #AXIS_PRESSURE */ public final float getHistoricalPressure(int pointerIndex, int pos) { - return mDataSamples[(pos * mNumPointers + pointerIndex) - * NUM_SAMPLE_DATA + SAMPLE_PRESSURE]; + return nativeGetAxisValue(mNativePtr, AXIS_PRESSURE, pointerIndex, pos); } /** @@ -1244,11 +1433,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * {@link #getHistorySize} * * @see #getHistorySize - * @see #getSize + * @see #getSize(int) + * @see #AXIS_SIZE */ public final float getHistoricalSize(int pointerIndex, int pos) { - return mDataSamples[(pos * mNumPointers + pointerIndex) - * NUM_SAMPLE_DATA + SAMPLE_SIZE]; + return nativeGetAxisValue(mNativePtr, AXIS_SIZE, pointerIndex, pos); } /** @@ -1262,11 +1451,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * {@link #getHistorySize} * * @see #getHistorySize - * @see #getTouchMajor + * @see #getTouchMajor(int) + * @see #AXIS_TOUCH_MAJOR */ public final float getHistoricalTouchMajor(int pointerIndex, int pos) { - return mDataSamples[(pos * mNumPointers + pointerIndex) - * NUM_SAMPLE_DATA + SAMPLE_TOUCH_MAJOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MAJOR, pointerIndex, pos); } /** @@ -1280,11 +1469,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * {@link #getHistorySize} * * @see #getHistorySize - * @see #getTouchMinor + * @see #getTouchMinor(int) + * @see #AXIS_TOUCH_MINOR */ public final float getHistoricalTouchMinor(int pointerIndex, int pos) { - return mDataSamples[(pos * mNumPointers + pointerIndex) - * NUM_SAMPLE_DATA + SAMPLE_TOUCH_MINOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOUCH_MINOR, pointerIndex, pos); } /** @@ -1298,11 +1487,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * {@link #getHistorySize} * * @see #getHistorySize - * @see #getToolMajor + * @see #getToolMajor(int) + * @see #AXIS_TOOL_MAJOR */ public final float getHistoricalToolMajor(int pointerIndex, int pos) { - return mDataSamples[(pos * mNumPointers + pointerIndex) - * NUM_SAMPLE_DATA + SAMPLE_TOOL_MAJOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MAJOR, pointerIndex, pos); } /** @@ -1316,11 +1505,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * {@link #getHistorySize} * * @see #getHistorySize - * @see #getToolMinor + * @see #getToolMinor(int) + * @see #AXIS_TOOL_MINOR */ public final float getHistoricalToolMinor(int pointerIndex, int pos) { - return mDataSamples[(pos * mNumPointers + pointerIndex) - * NUM_SAMPLE_DATA + SAMPLE_TOOL_MINOR]; + return nativeGetAxisValue(mNativePtr, AXIS_TOOL_MINOR, pointerIndex, pos); } /** @@ -1334,11 +1523,30 @@ public final class MotionEvent extends InputEvent implements Parcelable { * {@link #getHistorySize} * * @see #getHistorySize - * @see #getOrientation + * @see #getOrientation(int) + * @see #AXIS_ORIENTATION */ public final float getHistoricalOrientation(int pointerIndex, int pos) { - return mDataSamples[(pos * mNumPointers + pointerIndex) - * NUM_SAMPLE_DATA + SAMPLE_ORIENTATION]; + return nativeGetAxisValue(mNativePtr, AXIS_ORIENTATION, pointerIndex, pos); + } + + /** + * Returns the historical value of the requested axis, as per {@link #getAxisValue(int, int)}, + * occurred between this event and the previous event for the given pointer. + * Only applies to ACTION_MOVE events. + * + * @param axis The axis identifier for the axis value to retrieve. + * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 + * (the first pointer that is down) to {@link #getPointerCount()}-1. + * @param pos Which historical value to return; must be less than + * {@link #getHistorySize} + * @return The value of the axis, or 0 if the axis is not available. + * + * @see #AXIS_X + * @see #AXIS_Y + */ + public final float getHistoricalAxisValue(int axis, int pointerIndex, int pos) { + return nativeGetAxisValue(mNativePtr, axis, pointerIndex, pos); } /** @@ -1355,11 +1563,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { * * @see #getHistorySize * @see #getPointerCoords + * @see PointerCoords */ public final void getHistoricalPointerCoords(int pointerIndex, int pos, PointerCoords outPointerCoords) { - final int sampleIndex = (pos * mNumPointers + pointerIndex) * NUM_SAMPLE_DATA; - getPointerCoordsAtSampleIndex(sampleIndex, outPointerCoords); + nativeGetPointerCoords(mNativePtr, pointerIndex, pos, outPointerCoords); } /** @@ -1373,10 +1581,9 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @see #EDGE_BOTTOM */ public final int getEdgeFlags() { - return mEdgeFlags; + return nativeGetEdgeFlags(mNativePtr); } - /** * Sets the bitfield indicating which edges, if any, were touched by this * MotionEvent. @@ -1384,14 +1591,14 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @see #getEdgeFlags() */ public final void setEdgeFlags(int flags) { - mEdgeFlags = flags; + nativeSetEdgeFlags(mNativePtr, flags); } /** * Sets this event's action. */ public final void setAction(int action) { - mAction = action; + nativeSetAction(mNativePtr, action); } /** @@ -1400,8 +1607,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @param deltaY Amount to add to the current Y coordinate of the event. */ public final void offsetLocation(float deltaX, float deltaY) { - mXOffset += deltaX; - mYOffset += deltaY; + nativeOffsetLocation(mNativePtr, deltaX, deltaY); } /** @@ -1412,10 +1618,9 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @param y New absolute Y location. */ public final void setLocation(float x, float y) { - final float[] dataSamples = mDataSamples; - final int lastDataSampleIndex = mLastDataSampleIndex; - mXOffset = x - dataSamples[lastDataSampleIndex + SAMPLE_X]; - mYOffset = y - dataSamples[lastDataSampleIndex + SAMPLE_Y]; + float oldX = getX(); + float oldY = getY(); + nativeOffsetLocation(mNativePtr, x - oldX, y - oldY); } /** @@ -1428,85 +1633,14 @@ public final class MotionEvent extends InputEvent implements Parcelable { throw new IllegalArgumentException("matrix must not be null"); } - nativeTransform(matrix); - } - - private final void getPointerCoordsAtSampleIndex(int sampleIndex, - PointerCoords outPointerCoords) { - final float[] dataSamples = mDataSamples; - outPointerCoords.x = dataSamples[sampleIndex + SAMPLE_X] + mXOffset; - outPointerCoords.y = dataSamples[sampleIndex + SAMPLE_Y] + mYOffset; - outPointerCoords.pressure = dataSamples[sampleIndex + SAMPLE_PRESSURE]; - outPointerCoords.size = dataSamples[sampleIndex + SAMPLE_SIZE]; - outPointerCoords.touchMajor = dataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR]; - outPointerCoords.touchMinor = dataSamples[sampleIndex + SAMPLE_TOUCH_MINOR]; - outPointerCoords.toolMajor = dataSamples[sampleIndex + SAMPLE_TOOL_MAJOR]; - outPointerCoords.toolMinor = dataSamples[sampleIndex + SAMPLE_TOOL_MINOR]; - outPointerCoords.orientation = dataSamples[sampleIndex + SAMPLE_ORIENTATION]; - } - - private final void setPointerCoordsAtSampleIndex(int sampleIndex, - PointerCoords[] pointerCoords) { - final int numPointers = mNumPointers; - for (int i = 0; i < numPointers; i++) { - setPointerCoordsAtSampleIndex(sampleIndex, pointerCoords[i]); - sampleIndex += NUM_SAMPLE_DATA; - } - } - - private final void setPointerCoordsAtSampleIndex(int sampleIndex, - PointerCoords pointerCoords) { - final float[] dataSamples = mDataSamples; - dataSamples[sampleIndex + SAMPLE_X] = pointerCoords.x - mXOffset; - dataSamples[sampleIndex + SAMPLE_Y] = pointerCoords.y - mYOffset; - dataSamples[sampleIndex + SAMPLE_PRESSURE] = pointerCoords.pressure; - dataSamples[sampleIndex + SAMPLE_SIZE] = pointerCoords.size; - dataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR] = pointerCoords.touchMajor; - dataSamples[sampleIndex + SAMPLE_TOUCH_MINOR] = pointerCoords.touchMinor; - dataSamples[sampleIndex + SAMPLE_TOOL_MAJOR] = pointerCoords.toolMajor; - dataSamples[sampleIndex + SAMPLE_TOOL_MINOR] = pointerCoords.toolMinor; - dataSamples[sampleIndex + SAMPLE_ORIENTATION] = pointerCoords.orientation; - } - - private final void setPointerCoordsAtSampleIndex(int sampleIndex, - float x, float y, float pressure, float size) { - final float[] dataSamples = mDataSamples; - dataSamples[sampleIndex + SAMPLE_X] = x - mXOffset; - dataSamples[sampleIndex + SAMPLE_Y] = y - mYOffset; - dataSamples[sampleIndex + SAMPLE_PRESSURE] = pressure; - dataSamples[sampleIndex + SAMPLE_SIZE] = size; - dataSamples[sampleIndex + SAMPLE_TOUCH_MAJOR] = pressure; - dataSamples[sampleIndex + SAMPLE_TOUCH_MINOR] = pressure; - dataSamples[sampleIndex + SAMPLE_TOOL_MAJOR] = size; - dataSamples[sampleIndex + SAMPLE_TOOL_MINOR] = size; - dataSamples[sampleIndex + SAMPLE_ORIENTATION] = 0; - } - - private final void incrementNumSamplesAndReserveStorage(int dataSampleStride) { - if (mNumSamples == mEventTimeNanoSamples.length) { - long[] newEventTimeNanoSamples = new long[mNumSamples + BASE_AVAIL_SAMPLES]; - System.arraycopy(mEventTimeNanoSamples, 0, newEventTimeNanoSamples, 0, mNumSamples); - mEventTimeNanoSamples = newEventTimeNanoSamples; - } - - int nextDataSampleIndex = mLastDataSampleIndex + dataSampleStride; - if (nextDataSampleIndex + dataSampleStride > mDataSamples.length) { - float[] newDataSamples = new float[nextDataSampleIndex - + BASE_AVAIL_SAMPLES * dataSampleStride]; - System.arraycopy(mDataSamples, 0, newDataSamples, 0, nextDataSampleIndex); - mDataSamples = newDataSamples; - } - - mLastEventTimeNanoSampleIndex = mNumSamples; - mLastDataSampleIndex = nextDataSampleIndex; - mNumSamples += 1; + nativeTransform(mNativePtr, matrix); } /** * Add a new movement to the batch of movements in this event. The event's * current location, position and size is updated to the new values. * The current values in the event are added to a list of historical values. - * + * * Only applies to {@link #ACTION_MOVE} events. * * @param eventTime The time stamp (in ms) for this data. @@ -1518,19 +1652,22 @@ public final class MotionEvent extends InputEvent implements Parcelable { */ public final void addBatch(long eventTime, float x, float y, float pressure, float size, int metaState) { - incrementNumSamplesAndReserveStorage(NUM_SAMPLE_DATA); - - mEventTimeNanoSamples[mLastEventTimeNanoSampleIndex] = eventTime * MS_PER_NS; - setPointerCoordsAtSampleIndex(mLastDataSampleIndex, x, y, pressure, size); - - mMetaState |= metaState; + synchronized (gTmpPointerCoords) { + final PointerCoords pc = gTmpPointerCoords[0]; + pc.clear(); + pc.x = x; + pc.y = y; + pc.pressure = pressure; + pc.size = size; + nativeAddBatch(mNativePtr, eventTime * NS_PER_MS, gTmpPointerCoords, metaState); + } } /** * Add a new movement to the batch of movements in this event. The event's * current location, position and size is updated to the new values. * The current values in the event are added to a list of historical values. - * + * * Only applies to {@link #ACTION_MOVE} events. * * @param eventTime The time stamp (in ms) for this data. @@ -1538,20 +1675,14 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @param metaState Meta key state. */ public final void addBatch(long eventTime, PointerCoords[] pointerCoords, int metaState) { - final int dataSampleStride = mNumPointers * NUM_SAMPLE_DATA; - incrementNumSamplesAndReserveStorage(dataSampleStride); - - mEventTimeNanoSamples[mLastEventTimeNanoSampleIndex] = eventTime * MS_PER_NS; - setPointerCoordsAtSampleIndex(mLastDataSampleIndex, pointerCoords); - - mMetaState |= metaState; + nativeAddBatch(mNativePtr, eventTime * NS_PER_MS, pointerCoords, metaState); } @Override public String toString() { return "MotionEvent{" + Integer.toHexString(System.identityHashCode(this)) + " pointerId=" + getPointerId(0) - + " action=" + actionToString(mAction) + + " action=" + actionToString(getAction()) + " x=" + getX() + " y=" + getY() + " pressure=" + getPressure() @@ -1561,13 +1692,13 @@ public final class MotionEvent extends InputEvent implements Parcelable { + " toolMajor=" + getToolMajor() + " toolMinor=" + getToolMinor() + " orientation=" + getOrientation() - + " meta=" + KeyEvent.metaStateToString(mMetaState) + + " meta=" + KeyEvent.metaStateToString(getMetaState()) + " pointerCount=" + getPointerCount() + " historySize=" + getHistorySize() - + " flags=0x" + Integer.toHexString(mFlags) - + " edgeFlags=0x" + Integer.toHexString(mEdgeFlags) - + " device=" + mDeviceId - + " source=0x" + Integer.toHexString(mSource) + + " flags=0x" + Integer.toHexString(getFlags()) + + " edgeFlags=0x" + Integer.toHexString(getEdgeFlags()) + + " device=" + getDeviceId() + + " source=0x" + Integer.toHexString(getSource()) + (getPointerCount() > 1 ? " pointerId2=" + getPointerId(1) + " x2=" + getX(1) + " y2=" + getY(1) : "") + "}"; @@ -1575,7 +1706,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { /** * Returns a string that represents the symbolic name of the specified action - * such as "ACTION_DOWN", "ACTION_POINTER_DOWN(3)" or "35" (if unknown). + * such as "ACTION_DOWN", "ACTION_POINTER_DOWN(3)" or an equivalent numeric constant + * such as "35" if unknown. * * @param action The action. * @return The symbolic name of the specified action. @@ -1603,6 +1735,39 @@ public final class MotionEvent extends InputEvent implements Parcelable { } } + /** + * Returns a string that represents the symbolic name of the specified axis + * such as "AXIS_X" or an equivalent numeric constants such as "42" if unknown. + * + * @param axis The axis + * @return The symbolic name of the specified axis. + * @hide + */ + public static String axisToString(int axis) { + switch (axis) { + case AXIS_X: + return "AXIS_X"; + case AXIS_Y: + return "AXIS_Y"; + case AXIS_PRESSURE: + return "AXIS_PRESSURE"; + case AXIS_SIZE: + return "AXIS_SIZE"; + case AXIS_TOUCH_MAJOR: + return "AXIS_TOUCH_MAJOR"; + case AXIS_TOUCH_MINOR: + return "AXIS_TOUCH_MINOR"; + case AXIS_TOOL_MAJOR: + return "AXIS_TOOL_MAJOR"; + case AXIS_TOOL_MINOR: + return "AXIS_TOOL_MINOR"; + case AXIS_ORIENTATION: + return "AXIS_ORIENTATION"; + default: + return Integer.toString(axis); + } + } + public static final Parcelable.Creator<MotionEvent> CREATOR = new Parcelable.Creator<MotionEvent>() { public MotionEvent createFromParcel(Parcel in) { @@ -1617,84 +1782,16 @@ public final class MotionEvent extends InputEvent implements Parcelable { /** @hide */ public static MotionEvent createFromParcelBody(Parcel in) { - final int NP = in.readInt(); - final int NS = in.readInt(); - final int NI = NP * NS * NUM_SAMPLE_DATA; - - MotionEvent ev = obtain(NP, NS); - ev.mNumPointers = NP; - ev.mNumSamples = NS; - - ev.readBaseFromParcel(in); - - ev.mDownTimeNano = in.readLong(); - ev.mAction = in.readInt(); - ev.mXOffset = in.readFloat(); - ev.mYOffset = in.readFloat(); - ev.mXPrecision = in.readFloat(); - ev.mYPrecision = in.readFloat(); - ev.mEdgeFlags = in.readInt(); - ev.mMetaState = in.readInt(); - ev.mFlags = in.readInt(); - - final int[] pointerIdentifiers = ev.mPointerIdentifiers; - for (int i = 0; i < NP; i++) { - pointerIdentifiers[i] = in.readInt(); - } - - final long[] eventTimeNanoSamples = ev.mEventTimeNanoSamples; - for (int i = 0; i < NS; i++) { - eventTimeNanoSamples[i] = in.readLong(); - } - - final float[] dataSamples = ev.mDataSamples; - for (int i = 0; i < NI; i++) { - dataSamples[i] = in.readFloat(); - } - - ev.mLastEventTimeNanoSampleIndex = NS - 1; - ev.mLastDataSampleIndex = (NS - 1) * NP * NUM_SAMPLE_DATA; + MotionEvent ev = obtain(); + ev.mNativePtr = nativeReadFromParcel(ev.mNativePtr, in); return ev; } - + public void writeToParcel(Parcel out, int flags) { out.writeInt(PARCEL_TOKEN_MOTION_EVENT); - - final int NP = mNumPointers; - final int NS = mNumSamples; - final int NI = NP * NS * NUM_SAMPLE_DATA; - - out.writeInt(NP); - out.writeInt(NS); - - writeBaseToParcel(out); - - out.writeLong(mDownTimeNano); - out.writeInt(mAction); - out.writeFloat(mXOffset); - out.writeFloat(mYOffset); - out.writeFloat(mXPrecision); - out.writeFloat(mYPrecision); - out.writeInt(mEdgeFlags); - out.writeInt(mMetaState); - out.writeInt(mFlags); - - final int[] pointerIdentifiers = mPointerIdentifiers; - for (int i = 0; i < NP; i++) { - out.writeInt(pointerIdentifiers[i]); - } - - final long[] eventTimeNanoSamples = mEventTimeNanoSamples; - for (int i = 0; i < NS; i++) { - out.writeLong(eventTimeNanoSamples[i]); - } - - final float[] dataSamples = mDataSamples; - for (int i = 0; i < NI; i++) { - out.writeFloat(dataSamples[i]); - } + nativeWriteToParcel(mNativePtr, out); } - + /** * Transfer object for pointer coordinates. * @@ -1705,49 +1802,87 @@ public final class MotionEvent extends InputEvent implements Parcelable { * input devices and sources represent pointer coordinates. */ public static final class PointerCoords { + private static final int INITIAL_PACKED_AXIS_VALUES = 8; + private int mPackedAxisBits; // 32bits are enough for now, can raise to 64bit when needed + private float[] mPackedAxisValues; + + /** + * Creates a pointer coords object with all axes initialized to zero. + */ + public PointerCoords() { + } + + /** + * Creates a pointer coords object as a copy of the + * contents of another pointer coords object. + * + * @param other The pointer coords object to copy. + */ + public PointerCoords(PointerCoords other) { + copyFrom(other); + } + /** * The X coordinate of the pointer movement. - * The interpretation varies by input source and may represent the position of - * the center of the contact area, a relative displacement in device-specific units - * or something else. + * The interpretation of the X axis varies by input source. + * It may represent the X position of the center of the touch contact area, + * a relative horizontal displacement of a trackball or joystick, or something else. + * + * @see MotionEvent#AXIS_X */ public float x; /** * The Y coordinate of the pointer movement. - * The interpretation varies by input source and may represent the position of - * the center of the contact area, a relative displacement in device-specific units - * or something else. + * The interpretation of the Y axis varies by input source. + * It may represent the Y position of the center of the touch contact area, + * a relative vertical displacement of a trackball or joystick, or something else. + * + * @see MotionEvent#AXIS_Y */ public float y; /** - * A scaled value that describes the pressure applied to the pointer. + * A normalized value that describes the pressure applied to the device + * by a finger or other tool. * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure), - * however values higher than 1 may be generated depending on the calibration of + * although values higher than 1 may be generated depending on the calibration of * the input device. + * + * @see MotionEvent#AXIS_PRESSURE */ public float pressure; /** - * A scaled value of the approximate size of the pointer touch area. - * This represents some approximation of the area of the screen being + * A normalized value that describes the approximate size of the pointer touch area + * in relation to the maximum detectable size of the device. + * It represents some approximation of the area of the screen being * pressed; the actual value in pixels corresponding to the * touch is normalized with the device specific range of values * and scaled to a value between 0 and 1. The value of size can be used to * determine fat touch events. + * + * @see MotionEvent#AXIS_SIZE */ public float size; /** * The length of the major axis of an ellipse that describes the touch area at * the point of contact. + * If the device is a touch screen, the length is reported in pixels, otherwise it is + * reported in device-specific units. + * + * @see MotionEvent#AXIS_TOUCH_MAJOR */ public float touchMajor; /** * The length of the minor axis of an ellipse that describes the touch area at * the point of contact. + * If the device is a touch screen, the length is reported in pixels, otherwise it is + * reported in device-specific units. + * + * @see MotionEvent#AXIS_TOUCH_MINOR */ public float touchMinor; @@ -1756,6 +1891,10 @@ public final class MotionEvent extends InputEvent implements Parcelable { * the approaching tool. * The tool area represents the estimated size of the finger or pen that is * touching the device independent of its actual touch area at the point of contact. + * If the device is a touch screen, the length is reported in pixels, otherwise it is + * reported in device-specific units. + * + * @see MotionEvent#AXIS_TOOL_MAJOR */ public float toolMajor; @@ -1764,6 +1903,10 @@ public final class MotionEvent extends InputEvent implements Parcelable { * the approaching tool. * The tool area represents the estimated size of the finger or pen that is * touching the device independent of its actual touch area at the point of contact. + * If the device is a touch screen, the length is reported in pixels, otherwise it is + * reported in device-specific units. + * + * @see MotionEvent#AXIS_TOOL_MINOR */ public float toolMinor; @@ -1775,27 +1918,168 @@ public final class MotionEvent extends InputEvent implements Parcelable { * indicates that the major axis of contact is oriented to the left. * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians * (finger pointing fully right). + * + * @see MotionEvent#AXIS_ORIENTATION */ public float orientation; - - /* - private static final float PI_4 = (float) (Math.PI / 4); - - public float getTouchWidth() { - return Math.abs(orientation) > PI_4 ? touchMajor : touchMinor; + + /** + * Clears the contents of this object. + * Resets all axes to zero. + */ + public void clear() { + mPackedAxisBits = 0; + + x = 0; + y = 0; + pressure = 0; + size = 0; + touchMajor = 0; + touchMinor = 0; + toolMajor = 0; + toolMinor = 0; + orientation = 0; } - - public float getTouchHeight() { - return Math.abs(orientation) > PI_4 ? touchMinor : touchMajor; + + /** + * Copies the contents of another pointer coords object. + * + * @param other The pointer coords object to copy. + */ + public void copyFrom(PointerCoords other) { + final int bits = other.mPackedAxisBits; + mPackedAxisBits = bits; + if (bits != 0) { + final float[] otherValues = other.mPackedAxisValues; + final int count = Integer.bitCount(bits); + float[] values = mPackedAxisValues; + if (values == null || count > values.length) { + values = new float[otherValues.length]; + mPackedAxisValues = values; + } + System.arraycopy(otherValues, 0, values, 0, count); + } + + x = other.x; + y = other.y; + pressure = other.pressure; + size = other.size; + touchMajor = other.touchMajor; + touchMinor = other.touchMinor; + toolMajor = other.toolMajor; + toolMinor = other.toolMinor; + orientation = other.orientation; } - - public float getToolWidth() { - return Math.abs(orientation) > PI_4 ? toolMajor : toolMinor; + + /** + * Gets the value associated with the specified axis. + * + * @param axis The axis identifier for the axis value to retrieve. + * @return The value associated with the axis, or 0 if none. + * + * @see MotionEvent#AXIS_X + * @see MotionEvent#AXIS_Y + */ + public float getAxisValue(int axis) { + switch (axis) { + case AXIS_X: + return x; + case AXIS_Y: + return y; + case AXIS_PRESSURE: + return pressure; + case AXIS_SIZE: + return size; + case AXIS_TOUCH_MAJOR: + return touchMajor; + case AXIS_TOUCH_MINOR: + return touchMinor; + case AXIS_TOOL_MAJOR: + return toolMajor; + case AXIS_TOOL_MINOR: + return toolMinor; + case AXIS_ORIENTATION: + return orientation; + default: { + final int bits = mPackedAxisBits; + final int axisBit = 1 << axis; + if ((bits & axisBit) == 0) { + return 0; + } + final int index = Integer.bitCount(bits & (axisBit - 1)); + return mPackedAxisValues[index]; + } + } } - - public float getToolHeight() { - return Math.abs(orientation) > PI_4 ? toolMinor : toolMajor; + + /** + * Sets the value associated with the specified axis. + * + * @param axis The axis identifier for the axis value to assign. + * @param value The value to set. + * + * @see MotionEvent#AXIS_X + * @see MotionEvent#AXIS_Y + */ + public void setAxisValue(int axis, float value) { + switch (axis) { + case AXIS_X: + x = value; + break; + case AXIS_Y: + y = value; + break; + case AXIS_PRESSURE: + pressure = value; + break; + case AXIS_SIZE: + size = value; + break; + case AXIS_TOUCH_MAJOR: + touchMajor = value; + break; + case AXIS_TOUCH_MINOR: + touchMinor = value; + break; + case AXIS_TOOL_MAJOR: + toolMajor = value; + break; + case AXIS_TOOL_MINOR: + toolMinor = value; + break; + case AXIS_ORIENTATION: + orientation = value; + break; + default: { + final int bits = mPackedAxisBits; + final int axisBit = 1 << axis; + final int index = Integer.bitCount(bits & (axisBit - 1)); + float[] values = mPackedAxisValues; + if ((bits & axisBit) == 0) { + if (values == null) { + values = new float[INITIAL_PACKED_AXIS_VALUES]; + mPackedAxisValues = values; + } else { + final int count = Integer.bitCount(bits); + if (count < values.length) { + if (index != count) { + System.arraycopy(values, index, values, index + 1, + count - index); + } + } else { + float[] newValues = new float[count * 2]; + System.arraycopy(values, 0, newValues, 0, index); + System.arraycopy(values, index, newValues, index + 1, + count - index); + values = newValues; + mPackedAxisValues = values; + } + } + mPackedAxisBits = bits | axisBit; + } + values[index] = value; + } + } } - */ } } diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 0326a8f46182..83f91190f74d 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -99,6 +99,17 @@ public class Surface implements Parcelable { */ public static final int OPAQUE = 0x00000400; + /** + * Application requires a hardware-protected path to an + * external display sink. If a hardware-protected path is not available, + * then this surface will not be displayed on the external sink. + * + * @hide + */ + public static final int PROTECTED_APP = 0x00000800; + + // 0x1000 is reserved for an independent DRM protected flag in framework + /** Creates a normal surface. This is the default. */ public static final int FX_SURFACE_NORMAL = 0x00000000; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0240daf66726..48451bad5736 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1421,55 +1421,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** - * Used by views that contain lists of items. This state indicates that - * the view is showing the last item. - * @hide - */ - protected static final int[] LAST_STATE_SET = {R.attr.state_last}; - /** - * Used by views that contain lists of items. This state indicates that - * the view is showing the first item. - * @hide - */ - protected static final int[] FIRST_STATE_SET = {R.attr.state_first}; - /** - * Used by views that contain lists of items. This state indicates that - * the view is showing the middle item. - * @hide - */ - protected static final int[] MIDDLE_STATE_SET = {R.attr.state_middle}; - /** - * Used by views that contain lists of items. This state indicates that - * the view is showing only one item. - * @hide - */ - protected static final int[] SINGLE_STATE_SET = {R.attr.state_single}; - /** - * Used by views that contain lists of items. This state indicates that - * the view is pressed and showing the last item. - * @hide - */ - protected static final int[] PRESSED_LAST_STATE_SET = {R.attr.state_last, R.attr.state_pressed}; - /** - * Used by views that contain lists of items. This state indicates that - * the view is pressed and showing the first item. - * @hide - */ - protected static final int[] PRESSED_FIRST_STATE_SET = {R.attr.state_first, R.attr.state_pressed}; - /** - * Used by views that contain lists of items. This state indicates that - * the view is pressed and showing the middle item. - * @hide - */ - protected static final int[] PRESSED_MIDDLE_STATE_SET = {R.attr.state_middle, R.attr.state_pressed}; - /** - * Used by views that contain lists of items. This state indicates that - * the view is pressed and showing only one item. - * @hide - */ - protected static final int[] PRESSED_SINGLE_STATE_SET = {R.attr.state_single, R.attr.state_pressed}; - - /** * Temporary Rect currently for use in setBackground(). This will probably * be extended in the future to hold our own class with more than just * a Rect. :) @@ -1497,14 +1448,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * {@hide} */ @ViewDebug.ExportedProperty(category = "measurement") - /*package*/ int mMeasuredWidth; + int mMeasuredWidth; /** * Height as measured during measure pass. * {@hide} */ @ViewDebug.ExportedProperty(category = "measurement") - /*package*/ int mMeasuredHeight; + int mMeasuredHeight; /** * Flag to indicate that this view was marked INVALIDATED, or had its display list @@ -2187,6 +2138,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility private int[] mDrawableState = null; + /** + * Set to true when drawing cache is enabled and cannot be created. + * + * @hide + */ + public boolean mCachingFailed; + private Bitmap mDrawingCache; private Bitmap mUnscaledDrawingCache; private DisplayList mDisplayList; @@ -2279,7 +2237,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility boolean mCanAcceptDrop; /** - * Flag indicating that a drag can cross window boundaries + * Flag indicating that a drag can cross window boundaries. When + * {@link #startDrag(ClipData, DragShadowBuilder, Object, int)} is called + * with this flag set, all visible applications will be able to participate + * in the drag operation and receive the dragged content. + * * @hide */ public static final int DRAG_FLAG_GLOBAL = 1; @@ -3747,16 +3709,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** - * Determine if this view has the FITS_SYSTEM_WINDOWS flag set. - * @return True if window has FITS_SYSTEM_WINDOWS set - * - * @hide - */ - public boolean isFitsSystemWindowsFlagSet() { - return (mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS; - } - - /** * Returns the visibility status for this view. * * @return One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}. @@ -4630,6 +4582,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Pass a generic motion event down to the focused view. + * + * @param event The motion event to be dispatched. + * @return True if the event was handled by the view, false otherwise. + */ + public boolean dispatchGenericMotionEvent(MotionEvent event) { + return onGenericMotionEvent(event); + } + + /** * Called when the window containing this view gains or loses window focus. * ViewGroups should override to route to their children. * @@ -5136,6 +5098,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Implement this method to handle generic motion events. + * <p> + * Generic motion events are dispatched to the focused view to describe + * the motions of input devices such as joysticks. The + * {@link MotionEvent#getSource() source} of the motion event specifies + * the class of input that was received. Implementations of this method + * must examine the bits in the source before processing the event. + * The following code example shows how this is done. + * </p> + * <code> + * public boolean onGenericMotionEvent(MotionEvent event) { + * if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { + * float x = event.getX(); + * float y = event.getY(); + * // process the joystick motion + * return true; + * } + * return super.onGenericMotionEvent(event); + * } + * </code> + * + * @param event The generic motion event being processed. + * + * @return Return true if you have consumed the event, false if you haven't. + * The default implementation always returns false. + */ + public boolean onGenericMotionEvent(MotionEvent event) { + return false; + } + + /** * Implement this method to handle touch screen motion events. * * @param event The motion event. @@ -6068,9 +6061,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (top != mTop) { updateMatrix(); if (mMatrixIsIdentity) { - final ViewParent p = mParent; - if (p != null && mAttachInfo != null) { - final Rect r = mAttachInfo.mTmpInvalRect; + if (mAttachInfo != null) { int minTop; int yLoc; if (top < mTop) { @@ -6080,8 +6071,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility minTop = mTop; yLoc = 0; } - r.set(0, yLoc, mRight - mLeft, mBottom - minTop); - p.invalidateChild(this, r); + invalidate(0, yLoc, mRight - mLeft, mBottom - minTop); } } else { // Double-invalidation is necessary to capture view's old and new areas @@ -6138,17 +6128,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (bottom != mBottom) { updateMatrix(); if (mMatrixIsIdentity) { - final ViewParent p = mParent; - if (p != null && mAttachInfo != null) { - final Rect r = mAttachInfo.mTmpInvalRect; + if (mAttachInfo != null) { int maxBottom; if (bottom < mBottom) { maxBottom = mBottom; } else { maxBottom = bottom; } - r.set(0, 0, mRight - mLeft, maxBottom - mTop); - p.invalidateChild(this, r); + invalidate(0, 0, mRight - mLeft, maxBottom - mTop); } } else { // Double-invalidation is necessary to capture view's old and new areas @@ -6196,9 +6183,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (left != mLeft) { updateMatrix(); if (mMatrixIsIdentity) { - final ViewParent p = mParent; - if (p != null && mAttachInfo != null) { - final Rect r = mAttachInfo.mTmpInvalRect; + if (mAttachInfo != null) { int minLeft; int xLoc; if (left < mLeft) { @@ -6208,8 +6193,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility minLeft = mLeft; xLoc = 0; } - r.set(xLoc, 0, mRight - minLeft, mBottom - mTop); - p.invalidateChild(this, r); + invalidate(xLoc, 0, mRight - minLeft, mBottom - mTop); } } else { // Double-invalidation is necessary to capture view's old and new areas @@ -6257,17 +6241,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (right != mRight) { updateMatrix(); if (mMatrixIsIdentity) { - final ViewParent p = mParent; - if (p != null && mAttachInfo != null) { - final Rect r = mAttachInfo.mTmpInvalRect; + if (mAttachInfo != null) { int maxRight; if (right < mRight) { maxRight = mRight; } else { maxRight = right; } - r.set(0, 0, maxRight - mLeft, mBottom - mTop); - p.invalidateChild(this, r); + invalidate(0, 0, maxRight - mLeft, mBottom - mTop); } } else { // Double-invalidation is necessary to capture view's old and new areas @@ -8371,6 +8352,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @see #setLayerType(int, android.graphics.Paint) */ public void setDrawingCacheEnabled(boolean enabled) { + mCachingFailed = false; setFlags(enabled ? DRAWING_CACHE_ENABLED : 0, DRAWING_CACHE_ENABLED); } @@ -8393,6 +8375,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * * @hide */ + @SuppressWarnings({"UnusedDeclaration"}) public void outputDirtyFlags(String indent, boolean clear, int clearMask) { Log.d("View", indent + this + " DIRTY(" + (mPrivateFlags & View.DIRTY_MASK) + ") DRAWN(" + (mPrivateFlags & DRAWN) + ")" + " CACHE_VALID(" + @@ -8430,10 +8413,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @hide */ public boolean canHaveDisplayList() { - if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { - return false; - } - return true; + return !(mAttachInfo == null || mAttachInfo.mHardwareRenderer == null); } /** @@ -8615,7 +8595,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility public void buildDrawingCache() { buildDrawingCache(false); } - + /** * <p>Forces the drawing cache to be built if the drawing cache is invalid.</p> * @@ -8642,6 +8622,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility public void buildDrawingCache(boolean autoScale) { if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ? mDrawingCache == null : mUnscaledDrawingCache == null)) { + mCachingFailed = false; if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE); @@ -8667,6 +8648,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility (width * height * (opaque && !use32BitCache ? 2 : 4) > ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) { destroyDrawingCache(); + mCachingFailed = true; return; } @@ -8676,12 +8658,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) { Bitmap.Config quality; if (!opaque) { + // Never pick ARGB_4444 because it looks awful + // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) { case DRAWING_CACHE_QUALITY_AUTO: quality = Bitmap.Config.ARGB_8888; break; case DRAWING_CACHE_QUALITY_LOW: - quality = Bitmap.Config.ARGB_4444; + quality = Bitmap.Config.ARGB_8888; break; case DRAWING_CACHE_QUALITY_HIGH: quality = Bitmap.Config.ARGB_8888; @@ -8717,6 +8701,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } else { mUnscaledDrawingCache = null; } + mCachingFailed = true; return; } @@ -11281,6 +11266,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * </p> */ public boolean dispatchDragEvent(DragEvent event) { + //noinspection SimplifiableIfStatement if (mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnDragListener.onDrag(this, event)) { return true; diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 0444496c5d17..cc4e89cce417 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -152,12 +152,12 @@ public class ViewConfiguration { * should be at least equal to the size of the screen in ARGB888 format. */ @Deprecated - private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; // HVGA screen, ARGB8888 + private static final int MAXIMUM_DRAWING_CACHE_SIZE = 480 * 800 * 4; // ARGB8888 /** * The coefficient of friction applied to flings/scrolls. */ - private static float SCROLL_FRICTION = 0.015f; + private static final float SCROLL_FRICTION = 0.015f; /** * Max distance to overscroll for edge effects diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 26f8627836fc..b5a255892208 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -17,10 +17,6 @@ package android.view; import android.animation.LayoutTransition; -import android.view.animation.AlphaAnimation; -import com.android.internal.R; -import com.android.internal.util.Predicate; - import android.content.Context; import android.content.res.Configuration; import android.content.res.TypedArray; @@ -39,10 +35,13 @@ import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; import android.view.accessibility.AccessibilityEvent; +import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.LayoutAnimationController; import android.view.animation.Transformation; +import com.android.internal.R; +import com.android.internal.util.Predicate; import java.util.ArrayList; import java.util.HashSet; @@ -1145,6 +1144,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@inheritDoc} */ @Override + public boolean dispatchGenericMotionEvent(MotionEvent event) { + if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { + return super.dispatchGenericMotionEvent(event); + } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { + return mFocused.dispatchGenericMotionEvent(event); + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (!onFilterTouchEventForSecurity(ev)) { return false; @@ -2925,6 +2937,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) { + if (mTransition != null && mTransition.isRunning()) { + mTransition.cancel(); + } + if (child.getParent() != null) { throw new IllegalStateException("The specified child already has a parent. " + "You must call removeView() on the child's parent first."); diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index b1d509acfe79..ec1a373397ae 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -128,6 +128,11 @@ public final class ViewRoot extends Handler implements ViewParent, final TrackballAxis mTrackballAxisX = new TrackballAxis(); final TrackballAxis mTrackballAxisY = new TrackballAxis(); + int mLastJoystickXDirection; + int mLastJoystickYDirection; + int mLastJoystickXKeyCode; + int mLastJoystickYKeyCode; + final int[] mTmpLocation = new int[2]; final TypedValue mTmpValue = new TypedValue(); @@ -1924,6 +1929,7 @@ public final class ViewRoot extends Handler implements ViewParent, public final static int DISPATCH_DRAG_EVENT = 1015; public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016; public final static int DISPATCH_SYSTEM_UI_VISIBILITY = 1017; + public final static int DISPATCH_GENERIC_MOTION = 1018; @Override public void handleMessage(Message msg) { @@ -1960,6 +1966,9 @@ public final class ViewRoot extends Handler implements ViewParent, case DISPATCH_TRACKBALL: deliverTrackballEvent((MotionEvent) msg.obj, msg.arg1 != 0); break; + case DISPATCH_GENERIC_MOTION: + deliverGenericMotionEvent((MotionEvent) msg.obj, msg.arg1 != 0); + break; case DISPATCH_APP_VISIBILITY: handleAppVisibility(msg.arg1 != 0); break; @@ -2472,6 +2481,102 @@ public final class ViewRoot extends Handler implements ViewParent, } } + private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) { + final int source = event.getSource(); + final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0; + + // If there is no view, then the event will not be handled. + if (mView == null || !mAdded) { + if (isJoystick) { + updateJoystickDirection(event, false); + } + finishGenericMotionEvent(event, sendDone, false); + return; + } + + // Deliver the event to the view. + if (mView.dispatchGenericMotionEvent(event)) { + ensureTouchMode(false); + if (isJoystick) { + updateJoystickDirection(event, false); + } + finishGenericMotionEvent(event, sendDone, true); + return; + } + + if (isJoystick) { + // Translate the joystick event into DPAD keys and try to deliver those. + updateJoystickDirection(event, true); + finishGenericMotionEvent(event, sendDone, true); + } else { + finishGenericMotionEvent(event, sendDone, false); + } + } + + private void finishGenericMotionEvent(MotionEvent event, boolean sendDone, boolean handled) { + event.recycle(); + if (sendDone) { + finishInputEvent(handled); + } + } + + private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) { + final long time = event.getEventTime(); + final int metaState = event.getMetaState(); + final int deviceId = event.getDeviceId(); + final int source = event.getSource(); + final int xDirection = joystickAxisValueToDirection(event.getX()); + final int yDirection = joystickAxisValueToDirection(event.getY()); + + if (xDirection != mLastJoystickXDirection) { + if (mLastJoystickXKeyCode != 0) { + deliverKeyEvent(new KeyEvent(time, time, + KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState, + deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); + mLastJoystickXKeyCode = 0; + } + + mLastJoystickXDirection = xDirection; + + if (xDirection != 0 && synthesizeNewKeys) { + mLastJoystickXKeyCode = xDirection > 0 + ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT; + deliverKeyEvent(new KeyEvent(time, time, + KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState, + deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); + } + } + + if (yDirection != mLastJoystickYDirection) { + if (mLastJoystickYKeyCode != 0) { + deliverKeyEvent(new KeyEvent(time, time, + KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState, + deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); + mLastJoystickYKeyCode = 0; + } + + mLastJoystickYDirection = yDirection; + + if (yDirection != 0 && synthesizeNewKeys) { + mLastJoystickYKeyCode = yDirection > 0 + ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP; + deliverKeyEvent(new KeyEvent(time, time, + KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState, + deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); + } + } + } + + private static int joystickAxisValueToDirection(float value) { + if (value >= 0.5f) { + return 1; + } else if (value <= -0.5f) { + return -1; + } else { + return 0; + } + } + /** * Returns true if the key is used for keyboard navigation. * @param keyEvent The key event. @@ -3114,11 +3219,7 @@ public final class ViewRoot extends Handler implements ViewParent, } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { dispatchTrackball(event, sendDone); } else { - // TODO - Log.v(TAG, "Dropping unsupported motion event (unimplemented): " + event); - if (sendDone) { - finishInputEvent(false); - } + dispatchGenericMotion(event, sendDone); } } @@ -3143,7 +3244,14 @@ public final class ViewRoot extends Handler implements ViewParent, msg.arg1 = sendDone ? 1 : 0; sendMessageAtTime(msg, event.getEventTime()); } - + + private void dispatchGenericMotion(MotionEvent event, boolean sendDone) { + Message msg = obtainMessage(DISPATCH_GENERIC_MOTION); + msg.obj = event; + msg.arg1 = sendDone ? 1 : 0; + sendMessageAtTime(msg, event.getEventTime()); + } + public void dispatchAppVisibility(boolean visible) { Message msg = obtainMessage(DISPATCH_APP_VISIBILITY); msg.arg1 = visible ? 1 : 0; diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index 3bab29fcc27f..89b7aaadd2d8 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -342,11 +342,10 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")"); - if (mActiveStreamType == -1) { - reorderSliders(streamType); - } - if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { + if (mActiveStreamType == -1) { + reorderSliders(streamType); + } onShowVolumeChanged(streamType, flags); } @@ -403,7 +402,10 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie case AudioManager.STREAM_MUSIC: { // message = MUSIC_VOLUME_TEXT; // Special case for when Bluetooth is active for music - if (mAudioManager.isBluetoothA2dpOn()) { + if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) & + (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP | + AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) { // additionalMessage = // com.android.internal.R.string.volume_music_hint_playing_through_bluetooth; // setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_ad2p); diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 217e731c39f9..2095a935ae0b 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -187,6 +187,18 @@ public abstract class Window { public boolean dispatchTrackballEvent(MotionEvent event); /** + * Called to process generic motion events. At the very least your + * implementation must call + * {@link android.view.Window#superDispatchGenericMotionEvent} to do the + * standard processing. + * + * @param event The generic motion event. + * + * @return boolean Return true if this event was consumed. + */ + public boolean dispatchGenericMotionEvent(MotionEvent event); + + /** * Called to process population of {@link AccessibilityEvent}s. * * @param event The event. @@ -1101,6 +1113,14 @@ public abstract class Window { public abstract boolean superDispatchTrackballEvent(MotionEvent event); /** + * Used by custom windows, such as Dialog, to pass the generic motion event + * further down the view hierarchy. Application developers should + * not need to implement or call this. + * + */ + public abstract boolean superDispatchGenericMotionEvent(MotionEvent event); + + /** * Retrieve the top-level window decor view (containing the standard * window frame/decorations and the client's content inside of that), which * can be added as a window to the window manager. diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 02e5b63d0503..ca932e958f0f 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -537,7 +537,7 @@ public interface WindowManager extends ViewManager { public static final int FLAG_DITHER = 0x00001000; /** Window flag: don't allow screen shots while this window is - * displayed. */ + * displayed. Maps to Surface.SECURE. */ public static final int FLAG_SECURE = 0x00002000; /** Window flag: a special mode where the layout parameters are used diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java index ba425a6ae761..25f2229936aa 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtype.java +++ b/core/java/android/view/inputmethod/InputMethodSubtype.java @@ -16,10 +16,16 @@ package android.view.inputmethod; +import android.content.Context; import android.os.Parcel; import android.os.Parcelable; +import android.util.Slog; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; /** * This class is used to specify meta information of a subtype contained in an input method. @@ -28,12 +34,17 @@ import java.util.Arrays; * specified subtype of the designated input method directly. */ public final class InputMethodSubtype implements Parcelable { + private static final String TAG = InputMethodSubtype.class.getSimpleName(); + private static final String EXTRA_VALUE_PAIR_SEPARATOR = ","; + private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "="; + private final int mSubtypeNameResId; private final int mSubtypeIconResId; private final String mSubtypeLocale; private final String mSubtypeMode; private final String mSubtypeExtraValue; private final int mSubtypeHashCode; + private HashMap<String, String> mExtraValueHashMapCache; /** * Constructor @@ -102,6 +113,46 @@ public final class InputMethodSubtype implements Parcelable { return mSubtypeExtraValue; } + private HashMap<String, String> getExtraValueHashMap() { + if (mExtraValueHashMapCache == null) { + mExtraValueHashMapCache = new HashMap<String, String>(); + final String[] pairs = mSubtypeExtraValue.split(EXTRA_VALUE_PAIR_SEPARATOR); + final int N = pairs.length; + for (int i = 0; i < N; ++i) { + final String[] pair = pairs[i].split(EXTRA_VALUE_KEY_VALUE_SEPARATOR); + if (pair.length == 1) { + mExtraValueHashMapCache.put(pair[0], null); + } else if (pair.length > 1) { + if (pair.length > 2) { + Slog.w(TAG, "ExtraValue has two or more '='s"); + } + mExtraValueHashMapCache.put(pair[0], pair[1]); + } + } + } + return mExtraValueHashMapCache; + } + + /** + * The string of ExtraValue in subtype should be defined as follows: + * example: key0,key1=value1,key2,key3,key4=value4 + * @param key the key of extra value + * @return the subtype contains specified the extra value + */ + public boolean containsExtraValueKey(String key) { + return getExtraValueHashMap().containsKey(key); + } + + /** + * The string of ExtraValue in subtype should be defined as follows: + * example: key0,key1=value1,key2,key3,key4=value4 + * @param key the key of extra value + * @return the value of the specified key + */ + public String getExtraValueOf(String key) { + return getExtraValueHashMap().get(key); + } + @Override public int hashCode() { return mSubtypeHashCode; @@ -148,4 +199,35 @@ public final class InputMethodSubtype implements Parcelable { String mode, String extraValue) { return Arrays.hashCode(new Object[] {nameResId, iconResId, locale, mode, extraValue}); } + + /** + * Sort the list of InputMethodSubtype + * @param context Context will be used for getting localized strings from IME + * @param flags Flags for the sort order + * @param imi InputMethodInfo of which subtypes are subject to be sorted + * @param subtypeList List of InputMethodSubtype which will be sorted + * @return Sorted list of subtypes + * @hide + */ + public static List<InputMethodSubtype> sort(Context context, int flags, InputMethodInfo imi, + List<InputMethodSubtype> subtypeList) { + if (imi == null) return subtypeList; + final HashSet<InputMethodSubtype> inputSubtypesSet = new HashSet<InputMethodSubtype>( + subtypeList); + final ArrayList<InputMethodSubtype> sortedList = new ArrayList<InputMethodSubtype>(); + int N = imi.getSubtypeCount(); + for (int i = 0; i < N; ++i) { + InputMethodSubtype subtype = imi.getSubtypeAt(i); + if (inputSubtypesSet.contains(subtype)) { + sortedList.add(subtype); + inputSubtypesSet.remove(subtype); + } + } + // If subtypes in inputSubtypesSet remain, that means these subtypes are not + // contained in imi, so the remaining subtypes will be appended. + for (InputMethodSubtype subtype: inputSubtypesSet) { + sortedList.add(subtype); + } + return sortedList; + } } diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index b7ffd14cf72a..fc1240f77f25 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -21,7 +21,8 @@ import android.content.ComponentCallbacks; import android.content.Context; import android.content.res.AssetManager; import android.content.res.Configuration; -import android.database.Cursor; +import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; import android.graphics.Bitmap; import android.net.ParseException; import android.net.Uri; @@ -29,10 +30,8 @@ import android.net.WebAddress; import android.net.http.ErrorStrings; import android.net.http.SslCertificate; import android.net.http.SslError; -import android.os.Bundle; import android.os.Handler; import android.os.Message; -import android.provider.OpenableColumns; import android.util.Log; import android.util.TypedValue; import android.view.Surface; @@ -41,7 +40,6 @@ import android.view.WindowManager; import junit.framework.Assert; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; @@ -399,22 +397,33 @@ class BrowserFrame extends Handler { // set to true in didFirstLayout() mWebViewCore.removeMessages(WebViewCore.EventHub.WEBKIT_DRAW); } + } + } - // Note: only saves committed form data in standard load - if (loadType == FRAME_LOADTYPE_STANDARD - && mSettings.getSaveFormData()) { - final WebHistoryItem h = mCallbackProxy.getBackForwardList() - .getCurrentItem(); - if (h != null) { - String currentUrl = h.getUrl(); - if (currentUrl != null) { - mDatabase.setFormData(currentUrl, getFormTextData()); - } + @SuppressWarnings("unused") + private void saveFormData(HashMap<String, String> data) { + if (mSettings.getSaveFormData()) { + final WebHistoryItem h = mCallbackProxy.getBackForwardList() + .getCurrentItem(); + if (h != null) { + String currentUrl = h.getUrl(); + if (currentUrl != null) { + mDatabase.setFormData(currentUrl, data); } } } } + @SuppressWarnings("unused") + private boolean shouldSaveFormData() { + if (mSettings.getSaveFormData()) { + final WebHistoryItem h = mCallbackProxy.getBackForwardList() + .getCurrentItem(); + return h != null && h.getUrl() != null; + } + return false; + } + /** * native callback * Indicates the WebKit has committed to the new load @@ -926,7 +935,19 @@ class BrowserFrame extends Handler { if (androidResource != null) { return new WebResourceResponse(null, null, androidResource); } - return mCallbackProxy.shouldInterceptRequest(url); + WebResourceResponse response = mCallbackProxy.shouldInterceptRequest(url); + if (response == null && "browser:incognito".equals(url)) { + try { + Resources res = mContext.getResources(); + InputStream ins = res.openRawResource( + com.android.internal.R.raw.incognito_mode_start_page); + response = new WebResourceResponse("text/html", "utf8", ins); + } catch (NotFoundException ex) { + // This shouldn't happen, but try and gracefully handle it jic + Log.w(LOGTAG, "Failed opening raw.incognito_mode_start_page", ex); + } + } + return response; } /** @@ -1114,7 +1135,7 @@ class BrowserFrame extends Handler { } /** - * Called by JNI when the native HTTP(S) stack gets a invalid cert chain. + * Called by JNI when the native HTTP(S) stack gets an invalid cert chain. * * We delegate the request to CallbackProxy, and route its response to * {@link #nativeSslCertErrorProceed(int)} or @@ -1126,8 +1147,8 @@ class BrowserFrame extends Handler { X509Certificate cert = new X509CertImpl(cert_der); ssl_error = new SslError(cert_error, cert); } catch (IOException e) { - // Can't get the cert, not much to do. - Log.e(LOGTAG, "Can't get the certificate from WebKit, cancling"); + // Can't get the certificate, not much to do. + Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling"); nativeSslCertErrorCancel(handle, cert_error); return; } @@ -1202,12 +1223,15 @@ class BrowserFrame extends Handler { /** * Called by JNI when we load a page over SSL. */ - private void setCertificate(String issuedTo, String issuedBy, - long validNotBeforeMillis, long validNotAfterMillis) { - Date validNotBefore = new Date(validNotBeforeMillis); - Date validNotAfter = new Date(validNotAfterMillis); - mCallbackProxy.onReceivedCertificate(new SslCertificate( - issuedTo, issuedBy, validNotBefore, validNotAfter)); + private void setCertificate(byte cert_der[]) { + try { + X509Certificate cert = new X509CertImpl(cert_der); + mCallbackProxy.onReceivedCertificate(new SslCertificate(cert)); + } catch (IOException e) { + // Can't get the certificate, not much to do. + Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling"); + return; + } } //========================================================================== @@ -1316,13 +1340,6 @@ class BrowserFrame extends Handler { */ private native void setUsernamePassword(String username, String password); - /** - * Get form's "text" type data associated with the current frame. - * @return HashMap If succeed, returns a list of name/value pair. Otherwise - * returns null. - */ - private native HashMap getFormTextData(); - private native String nativeSaveWebArchive(String basename, boolean autoname); private native void nativeOrientationChanged(int orientation); diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java index e440eb90eef0..3ce073092c7e 100644 --- a/core/java/android/webkit/CacheManager.java +++ b/core/java/android/webkit/CacheManager.java @@ -45,6 +45,8 @@ import com.android.org.bouncycastle.crypto.digests.SHA1Digest; * are attached, as appropriate, to the request for revalidation of content. The * class also manages the cache size. * + * CacheManager may only be used if your activity contains a WebView. + * * @deprecated Access to the HTTP cache will be removed in a future release. */ @Deprecated @@ -196,8 +198,6 @@ public final class CacheManager { mBaseDir = new File(context.getCacheDir(), "webviewCacheChromiumStaging"); if (!mBaseDir.exists()) { mBaseDir.mkdirs(); - } else { - // TODO: Should we clear out old files? } return; } @@ -603,11 +603,12 @@ public final class CacheManager { * @return Whether the removal succeeded. */ static boolean removeAllCacheFiles() { - assert !JniUtil.useChromiumHttpStack(); - // Note, this is called before init() when the database is // created or upgraded. if (mBaseDir == null) { + // This method should not be called before init() when using the + // chrome http stack + assert !JniUtil.useChromiumHttpStack(); // Init() has not been called yet, so just flag that // we need to clear the cache when init() is called. mClearCacheOnInit = true; diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java index 9b0d4e0eaa27..40877e77a70d 100644 --- a/core/java/android/webkit/CookieManager.java +++ b/core/java/android/webkit/CookieManager.java @@ -293,13 +293,6 @@ public final class CookieManager { * @param value The value for set-cookie: in http response header */ public void setCookie(String url, String value) { - if (JniUtil.useChromiumHttpStack()) { - if (url.indexOf("://") == -1) - url = "http://" + url; - nativeSetCookie(url, value); - return; - } - WebAddress uri; try { uri = new WebAddress(url); @@ -307,7 +300,12 @@ public final class CookieManager { Log.e(LOGTAG, "Bad address: " + url); return; } - setCookie(uri, value); + + if (JniUtil.useChromiumHttpStack()) { + nativeSetCookie(uri.toString(), value); + } else { + setCookie(uri, value); + } } /** @@ -426,12 +424,6 @@ public final class CookieManager { * @return The cookies in the format of NAME=VALUE [; NAME=VALUE] */ public String getCookie(String url) { - if (JniUtil.useChromiumHttpStack()) { - if (url.indexOf("://") == -1) - url = "http://" + url; - return nativeGetCookie(url); - } - WebAddress uri; try { uri = new WebAddress(url); @@ -439,7 +431,12 @@ public final class CookieManager { Log.e(LOGTAG, "Bad address: " + url); return null; } - return getCookie(uri); + + if (JniUtil.useChromiumHttpStack()) { + return nativeGetCookie(uri.toString()); + } else { + return getCookie(uri); + } } /** @@ -660,6 +657,32 @@ public final class CookieManager { } /** + * Whether cookies are accepted for file scheme URLs. + */ + public static boolean allowFileSchemeCookies() { + if (JniUtil.useChromiumHttpStack()) { + return nativeAcceptFileSchemeCookies(); + } else { + return true; + } + } + + /** + * Sets whether cookies are accepted for file scheme URLs. + * + * Use of cookies with file scheme URLs is potentially insecure. Do not use this feature unless + * you can be sure that no unintentional sharing of cookie data can take place. + * <p> + * Note that calls to this method will have no effect if made after a WebView or CookieManager + * instance has been created. + */ + public static void setAcceptFileSchemeCookies(boolean accept) { + if (JniUtil.useChromiumHttpStack()) { + nativeSetAcceptFileSchemeCookies(accept); + } + } + + /** * Package level api, called from CookieSyncManager * * Get a list of cookies which are updated since a given time. @@ -1117,4 +1140,6 @@ public final class CookieManager { private static native void nativeSetAcceptCookie(boolean accept); private static native void nativeSetCookie(String url, String value); private static native void nativeFlushCookieStore(); + private static native boolean nativeAcceptFileSchemeCookies(); + private static native void nativeSetAcceptFileSchemeCookies(boolean accept); } diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 0bf0eabdd419..71d6080287c4 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -174,6 +174,7 @@ public class WebSettings { private boolean mBlockNetworkImage = false; private boolean mBlockNetworkLoads; private boolean mJavaScriptEnabled = false; + private boolean mShowVisualIndicator = false; private PluginState mPluginState = PluginState.OFF; private boolean mJavaScriptCanOpenWindowsAutomatically = false; private boolean mUseDoubleTree = false; @@ -1191,6 +1192,26 @@ public class WebSettings { } /** + * Tell the WebView to show the visual indicator + * @param flag True if the WebView should show the visual indicator + * @hide + */ + public synchronized void setShowVisualIndicator(boolean flag) { + if (mShowVisualIndicator != flag) { + mShowVisualIndicator = flag; + postSync(); + } + } + + /** + * @return True if the WebView is showing the visual indicator + * @hide + */ + public synchronized boolean getShowVisualIndicator() { + return mShowVisualIndicator; + } + + /** * Tell the WebView to enable plugins. * @param flag True if the WebView should load plugins. * @deprecated This method has been deprecated in favor of diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 6e1a6fcba4bb..492cb8024efc 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -67,7 +67,8 @@ import junit.framework.Assert; * to overlay html textfields (and textareas) to use our standard * text editing. */ -/* package */ class WebTextView extends AutoCompleteTextView { +/* package */ class WebTextView extends AutoCompleteTextView + implements AdapterView.OnItemClickListener { static final String LOGTAG = "webtextview"; @@ -558,6 +559,27 @@ import junit.framework.Assert; mFromFocusChange = false; } + // AdapterView.OnItemClickListener implementation + + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + if (id == 0 && position == 0) { + // Blank out the text box while we wait for WebCore to fill the form. + replaceText(""); + WebSettings settings = mWebView.getSettings(); + if (mAutoFillProfileIsSet) { + // Call a webview method to tell WebCore to autofill the form. + mWebView.autoFillForm(mQueryId); + } else { + // There is no autofill profile setup yet and the user has + // elected to try and set one up. Call through to the + // embedder to action that. + mWebView.getWebChromeClient().setupAutoFill( + mHandler.obtainMessage(AUTOFILL_FORM)); + } + } + } + @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); @@ -814,33 +836,16 @@ import junit.framework.Assert; setInputType(getInputType() | EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE); adapter.setTextView(this); - } - super.setAdapter(adapter); - if (mAutoFillable) { - setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - if (id == 0 && position == 0) { - // Blank out the text box while we wait for WebCore to fill the form. - replaceText(""); - WebSettings settings = mWebView.getSettings(); - if (mAutoFillProfileIsSet) { - // Call a webview method to tell WebCore to autofill the form. - mWebView.autoFillForm(mQueryId); - } else { - // There is no autofill profile setup yet and the user has - // elected to try and set one up. Call through to the - // embedder to action that. - mWebView.getWebChromeClient().setupAutoFill( - mHandler.obtainMessage(AUTOFILL_FORM)); - } - } - } - }); + if (mAutoFillable) { + setOnItemClickListener(this); + } else { + setOnItemClickListener(null); + } + showDropDown(); } else { - setOnItemClickListener(null); + dismissDropDown(); } - showDropDown(); + super.setAdapter(adapter); } /** @@ -858,6 +863,7 @@ import junit.framework.Assert; /** * {@inheritDoc} */ + @Override public View getView(int position, View convertView, ViewGroup parent) { TextView tv = (TextView) super.getView(position, convertView, parent); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index cdbf748fb0d9..72e31901495c 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -104,6 +104,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -557,6 +558,9 @@ public class WebView extends AbsoluteLayout // sending the same dimensions more than once. int mLastWidthSent; int mLastHeightSent; + // Since view height sent to webkit could be fixed to avoid relayout, this + // value records the last sent actual view height. + int mLastActualHeightSent; private int mContentWidth; // cache of value from WebViewCore private int mContentHeight; // cache of value from WebViewCore @@ -633,13 +637,7 @@ public class WebView extends AbsoluteLayout /* * Package message ids */ - //! arg1=x, arg2=y static final int SCROLL_TO_MSG_ID = 101; - static final int SCROLL_BY_MSG_ID = 102; - //! arg1=x, arg2=y - static final int SPAWN_SCROLL_TO_MSG_ID = 103; - //! arg1=x, arg2=y - static final int SYNC_SCROLL_TO_MSG_ID = 104; static final int NEW_PICTURE_MSG_ID = 105; static final int UPDATE_TEXT_ENTRY_MSG_ID = 106; static final int WEBCORE_INITIALIZED_MSG_ID = 107; @@ -695,9 +693,9 @@ public class WebView extends AbsoluteLayout static final String[] HandlerPackageDebugString = { "SCROLL_TO_MSG_ID", // = 101; - "SCROLL_BY_MSG_ID", // = 102; - "SPAWN_SCROLL_TO_MSG_ID", // = 103; - "SYNC_SCROLL_TO_MSG_ID", // = 104; + "102", // = 102; + "103", // = 103; + "104", // = 104; "NEW_PICTURE_MSG_ID", // = 105; "UPDATE_TEXT_ENTRY_MSG_ID", // = 106; "WEBCORE_INITIALIZED_MSG_ID", // = 107; @@ -745,7 +743,9 @@ public class WebView extends AbsoluteLayout // initial scale in percent. 0 means using default. private int mInitialScaleInPercent = 0; - private boolean mUserScroll = false; + // Whether or not a scroll event should be sent to webkit. This is only set + // to false when restoring the scroll position. + private boolean mSendScrollEvent = true; private int mSnapScrollMode = SNAP_NONE; private static final int SNAP_NONE = 0; @@ -791,6 +791,9 @@ public class WebView extends AbsoluteLayout // The value of 1 means the accessibility script is already injected private static final String PATTERN_MATCH_AXS_URL_PARAMETER = "(\\?axs=(0|1))|(&axs=(0|1))"; + // TextToSpeech instance exposed to JavaScript to the injected screenreader. + private TextToSpeech mTextToSpeech; + // variable to cache the above pattern in case accessibility is enabled. private Pattern mMatchAxsUrlParameterPattern; @@ -809,7 +812,7 @@ public class WebView extends AbsoluteLayout private OverScrollGlow mOverScrollGlow; // Used to match key downs and key ups - private boolean mGotKeyDown; + private Vector<Integer> mKeysPressed; /* package */ static boolean mLogEvent = true; @@ -981,13 +984,6 @@ public class WebView extends AbsoluteLayout // Used by the chrome stack to find application paths JniUtil.setContext(context); - if (AccessibilityManager.getInstance(context).isEnabled()) { - if (javaScriptInterfaces == null) { - javaScriptInterfaces = new HashMap<String, Object>(); - } - exposeAccessibilityJavaScriptApi(javaScriptInterfaces); - } - mCallbackProxy = new CallbackProxy(context, this); mViewManager = new ViewManager(this); L10nUtils.loadStrings(context); @@ -1180,26 +1176,38 @@ public class WebView extends AbsoluteLayout mOverscrollDistance = configuration.getScaledOverscrollDistance(); mOverflingDistance = configuration.getScaledOverflingDistance(); + + setScrollBarStyle(super.getScrollBarStyle()); + // Initially use a size of two, since the user is likely to only hold + // down two keys at a time (shift + another key) + mKeysPressed = new Vector<Integer>(2); } /** - * Exposes accessibility APIs to JavaScript by appending them to the JavaScript - * interfaces map provided by the WebView client. In case of conflicting - * alias with the one of the accessibility API the user specified one wins. + * Adds accessibility APIs to JavaScript. * - * @param javaScriptInterfaces A map with interfaces to be exposed to JavaScript. + * Note: This method is responsible to performing the necessary + * check if the accessibility APIs should be exposed. */ - private void exposeAccessibilityJavaScriptApi(Map<String, Object> javaScriptInterfaces) { - if (javaScriptInterfaces.containsKey(ALIAS_ACCESSIBILITY_JS_INTERFACE)) { - Log.w(LOGTAG, "JavaScript interface mapped to \"" + ALIAS_ACCESSIBILITY_JS_INTERFACE - + "\" overrides the accessibility API JavaScript interface. No accessibility" - + "API will be exposed to JavaScript!"); - return; + private void addAccessibilityApisToJavaScript() { + if (AccessibilityManager.getInstance(mContext).isEnabled() + && getSettings().getJavaScriptEnabled()) { + // exposing the TTS for now ... + mTextToSpeech = new TextToSpeech(getContext(), null); + addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE); } + } - // expose the TTS for now ... - javaScriptInterfaces.put(ALIAS_ACCESSIBILITY_JS_INTERFACE, - new TextToSpeech(getContext(), null)); + /** + * Removes accessibility APIs from JavaScript. + */ + private void removeAccessibilityApisFromJavaScript() { + // exposing the TTS for now ... + if (mTextToSpeech != null) { + removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE); + mTextToSpeech.shutdown(); + mTextToSpeech = null; + } } @Override @@ -2049,7 +2057,6 @@ public class WebView extends AbsoluteLayout } else { y = -h / 2; } - mUserScroll = true; return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) : extendScroll(y); } @@ -2075,7 +2082,6 @@ public class WebView extends AbsoluteLayout } else { y = h / 2; } - mUserScroll = true; return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) : extendScroll(y); } @@ -2087,7 +2093,7 @@ public class WebView extends AbsoluteLayout public void clearView() { mContentWidth = 0; mContentHeight = 0; - setBaseLayer(0, null); + setBaseLayer(0, null, false); mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT); } @@ -2323,6 +2329,11 @@ public class WebView extends AbsoluteLayout private View mTitleBar; /** + * the title bar rendering gravity + */ + private int mTitleGravity; + + /** * Add or remove a title bar to be embedded into the WebView, and scroll * along with it vertically, while remaining in view horizontally. Pass * null to remove the title bar from the WebView, and return to drawing @@ -2343,6 +2354,16 @@ public class WebView extends AbsoluteLayout } /** + * Set where to render the embedded title bar + * NO_GRAVITY at the top of the page + * TOP at the top of the screen + * @hide + */ + public void setTitleBarGravity(int gravity) { + mTitleGravity = gravity; + } + + /** * Given a distance in view space, convert it to content space. Note: this * does not reflect translation, just scaling, so this should not be called * with coordinates, but should be called for dimensions like width or @@ -2509,7 +2530,7 @@ public class WebView extends AbsoluteLayout Point pos = new Point(rect.left, rect.top); mWebViewCore.removeMessages(EventHub.SET_SCROLL_OFFSET); mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET, - nativeMoveGeneration(), mUserScroll ? 1 : 0, pos); + nativeMoveGeneration(), mSendScrollEvent ? 1 : 0, pos); mLastVisibleRectSent = rect; mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); } @@ -2570,6 +2591,8 @@ public class WebView extends AbsoluteLayout static class ViewSizeData { int mWidth; int mHeight; + float mHeightWidthRatio; + int mActualViewHeight; int mTextWrapWidth; int mAnchorX; int mAnchorY; @@ -2591,7 +2614,12 @@ public class WebView extends AbsoluteLayout int viewWidth = getViewWidth(); int newWidth = Math.round(viewWidth * mZoomManager.getInvScale()); - int newHeight = Math.round((getViewHeightWithTitle() - getTitleHeight()) * mZoomManager.getInvScale()); + // This height could be fixed and be different from actual visible height. + int viewHeight = getViewHeightWithTitle() - getTitleHeight(); + int newHeight = Math.round(viewHeight * mZoomManager.getInvScale()); + // Make the ratio more accurate than (newHeight / newWidth), since the + // latter both are calculated and rounded. + float heightWidthRatio = (float) viewHeight / viewWidth; /* * Because the native side may have already done a layout before the * View system was able to measure us, we have to send a height of 0 to @@ -2602,12 +2630,18 @@ public class WebView extends AbsoluteLayout */ if (newWidth > mLastWidthSent && mWrapContent) { newHeight = 0; + heightWidthRatio = 0; } + // Actual visible content height. + int actualViewHeight = Math.round(getViewHeight() * mZoomManager.getInvScale()); // Avoid sending another message if the dimensions have not changed. - if (newWidth != mLastWidthSent || newHeight != mLastHeightSent || force) { + if (newWidth != mLastWidthSent || newHeight != mLastHeightSent || force || + actualViewHeight != mLastActualHeightSent) { ViewSizeData data = new ViewSizeData(); data.mWidth = newWidth; data.mHeight = newHeight; + data.mHeightWidthRatio = heightWidthRatio; + data.mActualViewHeight = actualViewHeight; data.mTextWrapWidth = Math.round(viewWidth / mZoomManager.getTextWrapScale()); data.mScale = mZoomManager.getScale(); data.mIgnoreHeight = mZoomManager.isFixedLengthAnimationInProgress() @@ -2617,6 +2651,7 @@ public class WebView extends AbsoluteLayout mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data); mLastWidthSent = newWidth; mLastHeightSent = newHeight; + mLastActualHeightSent = actualViewHeight; mZoomManager.clearDocumentAnchor(); return true; } @@ -3163,6 +3198,9 @@ public class WebView extends AbsoluteLayout if (!mSelectingText) { WebViewCore.resumeUpdatePicture(mWebViewCore); } + if (oldX != mScrollX || oldY != mScrollY) { + sendOurVisibleRect(); + } } } else { super.computeScroll(); @@ -3268,7 +3306,7 @@ public class WebView extends AbsoluteLayout } mPageThatNeedsToSlideTitleBarOffScreen = null; } - + mZoomManager.onPageFinished(url); injectAccessibilityForUrl(url); } @@ -3666,7 +3704,12 @@ public class WebView extends AbsoluteLayout // When drawing the title bar, move it horizontally to always show // at the top of the WebView. mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft()); - int newTop = Math.min(0, mScrollY); + int newTop = 0; + if (mTitleGravity == Gravity.NO_GRAVITY) { + newTop = Math.min(0, mScrollY); + } else if (mTitleGravity == Gravity.TOP) { + newTop = mScrollY; + } mTitleBar.setBottom(newTop + getTitleHeight()); mTitleBar.setTop(newTop); } @@ -3825,7 +3868,7 @@ public class WebView extends AbsoluteLayout if (detector != null && detector.isInProgress()) { return false; } - + if (mNativeClass != 0 && nativeCursorIsTextInput()) { // Send the click so that the textfield is in focus centerKeyPressOnTextField(); @@ -3877,18 +3920,14 @@ public class WebView extends AbsoluteLayout * Select the word at the indicated content coordinates. */ boolean selectText(int x, int y) { - if (!setUpSelect()) { + if (!setUpSelect(true, x, y)) { return false; } - if (mNativeClass != 0 && nativeWordSelection(x, y)) { - nativeSetExtendSelection(); - mDrawSelectionPointer = false; - mSelectionStarted = true; - mTouchMode = TOUCH_DRAG_MODE; - return true; - } - selectionDone(); - return false; + nativeSetExtendSelection(); + mDrawSelectionPointer = false; + mSelectionStarted = true; + mTouchMode = TOUCH_DRAG_MODE; + return true; } private int mOrientation = Configuration.ORIENTATION_UNDEFINED; @@ -3961,14 +4000,14 @@ public class WebView extends AbsoluteLayout } } - void setBaseLayer(int layer, Rect invalRect) { + void setBaseLayer(int layer, Rect invalRect, boolean showVisualIndciator) { if (mNativeClass == 0) return; if (invalRect == null) { Rect rect = new Rect(0, 0, mContentWidth, mContentHeight); - nativeSetBaseLayer(layer, rect); + nativeSetBaseLayer(layer, rect, showVisualIndciator); } else { - nativeSetBaseLayer(layer, invalRect); + nativeSetBaseLayer(layer, invalRect, showVisualIndciator); } } @@ -4141,9 +4180,6 @@ public class WebView extends AbsoluteLayout mScrollX = pinLocX(mScrollX); mScrollY = pinLocY(mScrollY); if (oldScrollX != mScrollX || oldScrollY != mScrollY) { - mUserScroll = false; - mWebViewCore.sendMessage(EventHub.SYNC_SCROLL, oldScrollX, - oldScrollY); onScrollChanged(mScrollX, mScrollY, oldScrollX, oldScrollY); } else { sendOurVisibleRect(); @@ -4642,7 +4678,7 @@ public class WebView extends AbsoluteLayout return false; } - if (isEnterActionKey(keyCode)) { + if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { switchOutDrawHistory(); boolean wantsKeyEvents = nativeCursorNodePointer() == 0 || nativeCursorWantsKeyEvents(); @@ -4843,19 +4879,32 @@ public class WebView extends AbsoluteLayout } /* - * Enter selecting text mode. Returns true if the WebView is now in + * Enter selecting text mode, and see if CAB should be shown. + * Returns true if the WebView is now in * selecting text mode (including if it was already in that mode, and this * method did nothing). */ - private boolean setUpSelect() { + private boolean setUpSelect(boolean selectWord, int x, int y) { if (0 == mNativeClass) return false; // client isn't initialized if (inFullScreenMode()) return false; if (mSelectingText) return true; + nativeResetSelection(); + if (selectWord && !nativeWordSelection(x, y)) { + selectionDone(); + return false; + } + mSelectCallback = new SelectActionModeCallback(); + mSelectCallback.setWebView(this); + if (startActionMode(mSelectCallback) == null) { + // There is no ActionMode, so do not allow the user to modify a + // selection. + selectionDone(); + return false; + } mExtendSelection = false; mSelectingText = mDrawSelectionPointer = true; // don't let the picture change during text selection WebViewCore.pauseUpdatePicture(mWebViewCore); - nativeResetSelection(); if (nativeHasCursorNode()) { Rect rect = nativeCursorNodeBounds(); mSelectX = contentToViewX(rect.left); @@ -4868,14 +4917,6 @@ public class WebView extends AbsoluteLayout mSelectY = mScrollY + getViewHeightWithTitle() / 2; } nativeHideCursor(); - mSelectCallback = new SelectActionModeCallback(); - mSelectCallback.setWebView(this); - if (startActionMode(mSelectCallback) == null) { - // There is no ActionMode, so do not allow the user to modify a - // selection. - selectionDone(); - return false; - } mMinAutoScrollX = 0; mMaxAutoScrollX = getViewWidth(); mMinAutoScrollY = 0; @@ -4909,7 +4950,7 @@ public class WebView extends AbsoluteLayout * Do not rely on this functionality; it will be deprecated in the future. */ public void emulateShiftHeld() { - setUpSelect(); + setUpSelect(false, 0, 0); } /** @@ -4997,6 +5038,8 @@ public class WebView extends AbsoluteLayout treeObserver.addOnScrollChangedListener(mScrollChangedListener); } } + + addAccessibilityApisToJavaScript(); } @Override @@ -5017,6 +5060,8 @@ public class WebView extends AbsoluteLayout } } + removeAccessibilityApisFromJavaScript(); + super.onDetachedFromWindow(); } @@ -5085,7 +5130,7 @@ public class WebView extends AbsoluteLayout */ mDrawCursorRing = false; } - mGotKeyDown = false; + mKeysPressed.clear(); mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); mTouchMode = TOUCH_DONE_MODE; if (mNativeClass != 0) { @@ -5155,7 +5200,7 @@ public class WebView extends AbsoluteLayout } setFocusControllerActive(false); } - mGotKeyDown = false; + mKeysPressed.clear(); } super.onFocusChanged(focused, direction, previouslyFocusedRect); @@ -5230,39 +5275,36 @@ public class WebView extends AbsoluteLayout @Override public boolean dispatchKeyEvent(KeyEvent event) { - boolean dispatch = true; - - // Textfields, plugins, and contentEditable nodes need to receive the - // shift up key even if another key was released while the shift key - // was held down. - boolean inEditingMode = inEditingMode(); - if (!inEditingMode && (mNativeClass == 0 - || !nativePageShouldHandleShiftAndArrows())) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - mGotKeyDown = true; - } else { - if (!mGotKeyDown && event.getAction() != KeyEvent.ACTION_MULTIPLE) { - /* - * We got a key up for which we were not the recipient of - * the original key down. Don't give it to the view. - */ - dispatch = false; + switch (event.getAction()) { + case KeyEvent.ACTION_DOWN: + mKeysPressed.add(Integer.valueOf(event.getKeyCode())); + break; + case KeyEvent.ACTION_MULTIPLE: + // Always accept the action. + break; + case KeyEvent.ACTION_UP: + int location = mKeysPressed.indexOf(Integer.valueOf(event.getKeyCode())); + if (location == -1) { + // We did not receive the key down for this key, so do not + // handle the key up. + return false; + } else { + // We did receive the key down. Handle the key up, and + // remove it from our pressed keys. + mKeysPressed.remove(location); } - mGotKeyDown = false; - } + break; + default: + // Accept the action. This should not happen, unless a new + // action is added to KeyEvent. + break; } - - if (dispatch) { - if (inEditingMode) { - // Ensure that the WebTextView gets the event, even if it does - // not currently have a bounds. - return mWebTextView.dispatchKeyEvent(event); - } else { - return super.dispatchKeyEvent(event); - } + if (inEditingMode() && mWebTextView.isFocused()) { + // Ensure that the WebTextView gets the event, even if it does + // not currently have a bounds. + return mWebTextView.dispatchKeyEvent(event); } else { - // We didn't dispatch, so let something else handle the key - return false; + return super.dispatchKeyEvent(event); } } @@ -5680,7 +5722,6 @@ public class WebView extends AbsoluteLayout mHeldMotionless = MOTIONLESS_FALSE; } mLastTouchTime = eventTime; - mUserScroll = true; } doDrag(deltaX, deltaY); @@ -6381,7 +6422,6 @@ public class WebView extends AbsoluteLayout if (xMove != 0 || yMove != 0) { pinScrollBy(xMove, yMove, true, 0); } - mUserScroll = true; } } @@ -7132,20 +7172,10 @@ public class WebView extends AbsoluteLayout doShortPress(); break; } - case SCROLL_BY_MSG_ID: - setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj); - break; - case SYNC_SCROLL_TO_MSG_ID: - if (mUserScroll) { - // if user has scrolled explicitly, don't sync the - // scroll position any more - mUserScroll = false; - break; - } - setContentScrollTo(msg.arg1, msg.arg2); - break; - case SCROLL_TO_MSG_ID: - if (((Boolean) msg.obj).booleanValue()) { + case SCROLL_TO_MSG_ID: { + // arg1 = animate, arg2 = onlyIfImeIsShowing + // obj = Point(x, y) + if (msg.arg2 == 1) { // This scroll is intended to bring the textfield into // view, but is only necessary if the IME is showing InputMethodManager imm = InputMethodManager.peekInstance(); @@ -7155,18 +7185,14 @@ public class WebView extends AbsoluteLayout break; } } - if (setContentScrollTo(msg.arg1, msg.arg2)) { - // if we can't scroll to the exact position due to pin, - // send a message to WebCore to re-scroll when we get a - // new picture - mUserScroll = false; - mWebViewCore.sendMessage(EventHub.SYNC_SCROLL, - msg.arg1, msg.arg2); + final Point p = (Point) msg.obj; + if (msg.arg1 == 1) { + spawnContentScrollTo(p.x, p.y); + } else { + setContentScrollTo(p.x, p.y); } break; - case SPAWN_SCROLL_TO_MSG_ID: - spawnContentScrollTo(msg.arg1, msg.arg2); - break; + } case UPDATE_ZOOM_RANGE: { WebViewCore.ViewState viewState = (WebViewCore.ViewState) msg.obj; // mScrollX contains the new minPrefWidth @@ -7179,9 +7205,9 @@ public class WebView extends AbsoluteLayout } case NEW_PICTURE_MSG_ID: { // called for new content - mUserScroll = false; final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj; - setBaseLayer(draw.mBaseLayer, draw.mInvalRegion.getBounds()); + setBaseLayer(draw.mBaseLayer, draw.mInvalRegion.getBounds(), + getSettings().getShowVisualIndicator()); final Point viewSize = draw.mViewSize; WebViewCore.ViewState viewState = draw.mViewState; boolean isPictureAfterFirstLayout = viewState != null; @@ -7190,7 +7216,14 @@ public class WebView extends AbsoluteLayout mLastWidthSent = 0; mZoomManager.onFirstLayout(draw); if (!mDrawHistory) { + // Do not send the scroll event for this particular + // scroll message. Note that a scroll event may + // still be fired if the user scrolls before the + // message can be handled. + mSendScrollEvent = false; setContentScrollTo(viewState.mScrollX, viewState.mScrollY); + mSendScrollEvent = true; + // As we are on a new page, remove the WebTextView. This // is necessary for page loads driven by webkit, and in // particular when the user was on a password field, so @@ -7205,8 +7238,13 @@ public class WebView extends AbsoluteLayout // received in the fixed dimension. final boolean updateLayout = viewSize.x == mLastWidthSent && viewSize.y == mLastHeightSent; + // Don't send scroll event for picture coming from webkit, + // since the new picture may cause a scroll event to override + // the saved history scroll position. + mSendScrollEvent = false; recordNewContentSize(draw.mContentSize.x, draw.mContentSize.y, updateLayout); + mSendScrollEvent = true; if (DebugFlags.WEB_VIEW) { Rect b = draw.mInvalRegion.getBounds(); Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" + @@ -8116,7 +8154,6 @@ public class WebView extends AbsoluteLayout + contentCursorRingBounds); } requestRectangleOnScreen(viewCursorRingBounds); - mUserScroll = true; return keyHandled; } @@ -8302,7 +8339,8 @@ public class WebView extends AbsoluteLayout private native void nativeSetFindIsEmpty(); private native void nativeSetFindIsUp(boolean isUp); private native void nativeSetHeightCanMeasure(boolean measure); - private native void nativeSetBaseLayer(int layer, Rect invalRect); + private native void nativeSetBaseLayer(int layer, Rect invalRect, + boolean showVisualIndciator); private native void nativeShowCursorTimed(); private native void nativeReplaceBaseContent(int content); private native void nativeCopyBaseContentToPicture(Picture pict); diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index b410699ba001..42889cbf81ab 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -124,9 +124,6 @@ final class WebViewCore { private int mRestoredX = 0; private int mRestoredY = 0; - private int mWebkitScrollX = 0; - private int mWebkitScrollY = 0; - private DeviceMotionAndOrientationManager mDeviceMotionAndOrientationManager = new DeviceMotionAndOrientationManager(this); private DeviceMotionService mDeviceMotionService; @@ -868,7 +865,7 @@ final class WebViewCore { "SAVE_DOCUMENT_STATE", // = 128; "129", // = 129; "WEBKIT_DRAW", // = 130; - "SYNC_SCROLL", // = 131; + "131", // = 131; "POST_URL", // = 132; "SPLIT_PICTURE_SET", // = 133; "CLEAR_CONTENT", // = 134; @@ -926,7 +923,6 @@ final class WebViewCore { static final int SAVE_DOCUMENT_STATE = 128; static final int WEBKIT_DRAW = 130; - static final int SYNC_SCROLL = 131; static final int POST_URL = 132; static final int SPLIT_PICTURE_SET = 133; static final int CLEAR_CONTENT = 134; @@ -1177,19 +1173,15 @@ final class WebViewCore { break; case VIEW_SIZE_CHANGED: { - WebView.ViewSizeData data = - (WebView.ViewSizeData) msg.obj; - viewSizeChanged(data.mWidth, data.mHeight, - data.mTextWrapWidth, data.mScale, - data.mAnchorX, data.mAnchorY, - data.mIgnoreHeight); + viewSizeChanged((WebView.ViewSizeData) msg.obj); break; } case SET_SCROLL_OFFSET: // note: these are in document coordinates // (inv-zoom) Point pt = (Point) msg.obj; - nativeSetScrollOffset(msg.arg1, msg.arg2, pt.x, pt.y); + nativeSetScrollOffset(msg.arg1, msg.arg2 == 1, + pt.x, pt.y); break; case SET_GLOBAL_BOUNDS: @@ -1481,11 +1473,6 @@ final class WebViewCore { data.mAllow, data.mRemember); break; - case SYNC_SCROLL: - mWebkitScrollX = msg.arg1; - mWebkitScrollY = msg.arg2; - break; - case SPLIT_PICTURE_SET: nativeSplitContent(msg.arg1); mWebView.mPrivateHandler.obtainMessage( @@ -1501,9 +1488,7 @@ final class WebViewCore { break; case MESSAGE_RELAY: - if (msg.obj instanceof Message) { - ((Message) msg.obj).sendToTarget(); - } + ((Message) msg.obj).sendToTarget(); break; case POPULATE_VISITED_LINKS: @@ -1816,8 +1801,11 @@ final class WebViewCore { private float mCurrentViewScale = 1.0f; // notify webkit that our virtual view size changed size (after inv-zoom) - private void viewSizeChanged(int w, int h, int textwrapWidth, float scale, - int anchorX, int anchorY, boolean ignoreHeight) { + private void viewSizeChanged(WebView.ViewSizeData data) { + int w = data.mWidth; + int h = data.mHeight; + int textwrapWidth = data.mTextWrapWidth; + float scale = data.mScale; if (DebugFlags.WEB_VIEW_CORE) { Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale); @@ -1862,8 +1850,15 @@ final class WebViewCore { width = textwrapWidth; } } - nativeSetSize(width, width == w ? h : Math.round((float) width * h / w), - textwrapWidth, scale, w, h, anchorX, anchorY, ignoreHeight); + int height = h; + if (width != w) { + float heightWidthRatio = data.mHeightWidthRatio; + float ratio = (heightWidthRatio > 0) ? heightWidthRatio : (float) h / w; + height = Math.round(ratio * width); + } + nativeSetSize(width, height, textwrapWidth, scale, w, + data.mActualViewHeight > 0 ? data.mActualViewHeight : h, + data.mAnchorX, data.mAnchorY, data.mIgnoreHeight); // Remember the current width and height boolean needInvalidate = (mCurrentViewWidth == 0); mCurrentViewWidth = w; @@ -1997,13 +1992,6 @@ final class WebViewCore { if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); Message.obtain(mWebView.mPrivateHandler, WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget(); - if (mWebkitScrollX != 0 || mWebkitScrollY != 0) { - // as we have the new picture, try to sync the scroll position - Message.obtain(mWebView.mPrivateHandler, - WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX, - mWebkitScrollY).sendToTarget(); - mWebkitScrollX = mWebkitScrollY = 0; - } } } @@ -2106,50 +2094,8 @@ final class WebViewCore { } // called by JNI - private void contentScrollBy(int dx, int dy, boolean animate) { - if (!mBrowserFrame.firstLayoutDone()) { - // Will this happen? If yes, we need to do something here. - return; - } - if (mWebView != null) { - Message msg = Message.obtain(mWebView.mPrivateHandler, - WebView.SCROLL_BY_MSG_ID, dx, dy, Boolean.valueOf(animate)); - if (mDrawIsScheduled) { - mEventHub.sendMessage(Message.obtain(null, - EventHub.MESSAGE_RELAY, msg)); - } else { - msg.sendToTarget(); - } - } - } - - // called by JNI - private void contentScrollTo(int x, int y, boolean onlyIfImeIsShowing) { - if (!mBrowserFrame.firstLayoutDone()) { - /* - * WebKit restore state will be called before didFirstLayout(), - * remember the position as it has to be applied after restoring - * zoom factor which is controlled by screenWidth. - */ - mRestoredX = x; - mRestoredY = y; - return; - } - if (mWebView != null) { - Message msg = Message.obtain(mWebView.mPrivateHandler, - WebView.SCROLL_TO_MSG_ID, x, y, - Boolean.valueOf(onlyIfImeIsShowing)); - if (mDrawIsScheduled) { - mEventHub.sendMessage(Message.obtain(null, - EventHub.MESSAGE_RELAY, msg)); - } else { - msg.sendToTarget(); - } - } - } - - // called by JNI - private void contentSpawnScrollTo(int x, int y) { + private void contentScrollTo(int x, int y, boolean animate, + boolean onlyIfImeIsShowing) { if (!mBrowserFrame.firstLayoutDone()) { /* * WebKit restore state will be called before didFirstLayout(), @@ -2162,7 +2108,8 @@ final class WebViewCore { } if (mWebView != null) { Message msg = Message.obtain(mWebView.mPrivateHandler, - WebView.SPAWN_SCROLL_TO_MSG_ID, x, y); + WebView.SCROLL_TO_MSG_ID, animate ? 1 : 0, + onlyIfImeIsShowing ? 1 : 0, new Point(x, y)); if (mDrawIsScheduled) { mEventHub.sendMessage(Message.obtain(null, EventHub.MESSAGE_RELAY, msg)); @@ -2239,7 +2186,7 @@ final class WebViewCore { } // reset the scroll position, the restored offset and scales - mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY = 0; + mRestoredX = mRestoredY = 0; mRestoredScale = mRestoredTextWrapScale = 0; } @@ -2501,7 +2448,7 @@ final class WebViewCore { private native void nativeScrollFocusedTextInput(float xPercent, int y); // these must be in document space (i.e. not scaled/zoomed). - private native void nativeSetScrollOffset(int gen, int userScrolled, int dx, int dy); + private native void nativeSetScrollOffset(int gen, boolean sendScrollEvent, int dx, int dy); private native void nativeSetGlobalBounds(int x, int y, int w, int h); diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index efbcd5811db8..d7a6a537284e 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -361,6 +361,7 @@ class ZoomManager { // returns TRUE if zoom out succeeds and FALSE if no zoom changes. private boolean zoom(float zoomMultiplier) { + mInitialZoomOverview = false; // TODO: alternatively we can disallow this during draw history mode mWebView.switchOutDrawHistory(); // Center zooming to the center of the screen. @@ -378,6 +379,7 @@ class ZoomManager { * @return true if the new scale triggered an animation and false otherwise. */ public boolean startZoomAnimation(float scale, boolean reflowText) { + mInitialZoomOverview = false; float oldScale = mActualScale; mInitialScrollX = mWebView.getScrollX(); mInitialScrollY = mWebView.getScrollY(); @@ -421,6 +423,7 @@ class ZoomManager { * in progress by calling isFixedLengthAnimationInProgress(). */ public void animateZoom(Canvas canvas) { + mInitialZoomOverview = false; if (mZoomScale == 0) { Log.w(LOGTAG, "A WebView is attempting to perform a fixed length " + "zoom animation when no zoom is in progress"); @@ -568,6 +571,8 @@ class ZoomManager { * C. If the page is in overmode then change to the default scale. */ public void handleDoubleTap(float lastTouchX, float lastTouchY) { + // User takes action, set initial zoom overview to false. + mInitialZoomOverview = false; WebSettings settings = mWebView.getSettings(); if (!isDoubleTapEnabled()) { return; @@ -706,6 +711,7 @@ class ZoomManager { private class ScaleDetectorListener implements ScaleGestureDetector.OnScaleGestureListener { public boolean onScaleBegin(ScaleGestureDetector detector) { + mInitialZoomOverview = false; dismissZoomPicker(); mWebView.mViewManager.startZoom(); mWebView.onPinchToZoomAnimationStart(); @@ -791,22 +797,30 @@ class ZoomManager { // scaleAll(), we need to post a Runnable to ensure requestLayout(). // Additionally, only update the text wrap scale if the width changed. mWebView.post(new PostScale(w != ow && - !mWebView.getSettings().getUseFixedViewport())); + !mWebView.getSettings().getUseFixedViewport(), mInZoomOverview)); } private class PostScale implements Runnable { final boolean mUpdateTextWrap; + // Remember the zoom overview state right after rotation since + // it could be changed between the time this callback is initiated and + // the time it's actually run. + final boolean mInZoomOverviewBeforeSizeChange; - public PostScale(boolean updateTextWrap) { + public PostScale(boolean updateTextWrap, boolean inZoomOverview) { mUpdateTextWrap = updateTextWrap; + mInZoomOverviewBeforeSizeChange = inZoomOverview; } public void run() { if (mWebView.getWebViewCore() != null) { // we always force, in case our height changed, in which case we // still want to send the notification over to webkit. - setZoomScale(Math.max(mActualScale, getZoomOverviewScale()), - mUpdateTextWrap, true); + // Keep overview mode unchanged when rotating. + final float zoomOverviewScale = getZoomOverviewScale(); + final float newScale = (mInZoomOverviewBeforeSizeChange) ? + zoomOverviewScale : Math.max(mActualScale, zoomOverviewScale); + setZoomScale(newScale, mUpdateTextWrap, true); // update the zoom buttons as the scale can be changed updateZoomPicker(); } @@ -846,23 +860,42 @@ class ZoomManager { public void onNewPicture(WebViewCore.DrawData drawData) { final int viewWidth = mWebView.getViewWidth(); final boolean zoomOverviewWidthChanged = setupZoomOverviewWidth(drawData, viewWidth); + final float newZoomOverviewScale = getZoomOverviewScale(); WebSettings settings = mWebView.getSettings(); if (zoomOverviewWidthChanged && settings.isNarrowColumnLayout() && settings.getUseFixedViewport() && (mInitialZoomOverview || mInZoomOverview)) { - mTextWrapScale = getReadingLevelScale(); + // Keep mobile site's text wrap scale unchanged. For mobile sites, + // the text wrap scale is the same as zoom overview scale, which is 1.0f. + if (exceedsMinScaleIncrement(mTextWrapScale, 1.0f) || + exceedsMinScaleIncrement(newZoomOverviewScale, 1.0f)) { + mTextWrapScale = getReadingLevelScale(); + } else { + mTextWrapScale = newZoomOverviewScale; + } } - final float zoomOverviewScale = getZoomOverviewScale(); if (!mMinZoomScaleFixed) { - mMinZoomScale = zoomOverviewScale; + mMinZoomScale = newZoomOverviewScale; } - // fit the content width to the current view. Ignore the rounding error case. - if (!mWebView.drawHistory() && (mInitialZoomOverview || (mInZoomOverview - && Math.abs((viewWidth * mInvActualScale) - mZoomOverviewWidth) > 1))) { + // fit the content width to the current view for the first new picture + // after first layout. + boolean scaleHasDiff = exceedsMinScaleIncrement(newZoomOverviewScale, mActualScale); + // Make sure the actual scale is no less than zoom overview scale. + boolean scaleLessThanOverview = + (newZoomOverviewScale - mActualScale) >= MINIMUM_SCALE_INCREMENT; + // Make sure mobile sites are correctly handled since mobile site will + // change content width after rotating. + boolean mobileSiteInOverview = mInZoomOverview && + !exceedsMinScaleIncrement(newZoomOverviewScale, 1.0f); + if (!mWebView.drawHistory() && + (mInitialZoomOverview || scaleLessThanOverview || mobileSiteInOverview) && + scaleHasDiff) { mInitialZoomOverview = false; - setZoomScale(zoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale) && + setZoomScale(newZoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale) && !mWebView.getSettings().getUseFixedViewport()); + } else { + mInZoomOverview = !scaleHasDiff; } } @@ -1043,4 +1076,12 @@ class ZoomManager { public void setHardwareAccelerated() { mHardwareAccelerated = true; } + + /** + * OnPageFinished called by webview when a page is fully loaded. + */ + /* package*/ void onPageFinished(String url) { + // Turn off initial zoom overview flag when a page is fully loaded. + mInitialZoomOverview = false; + } } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 3f38f2eec581..eb53e56b4b0d 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -334,6 +334,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * the drawing cache was enabled on the children */ boolean mCachingStarted; + boolean mCachingActive; /** * The position of the view that received the down motion event @@ -3266,9 +3267,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final int scrollY = mScrollY; if (!mEdgeGlowTop.isFinished()) { final int restoreCount = canvas.save(); - final int width = getWidth(); + final int width = getWidth() - mListPadding.left - mListPadding.right; - canvas.translate(0, Math.min(0, scrollY + mFirstPositionDistanceGuess)); + canvas.translate(mListPadding.left, + Math.min(0, scrollY + mFirstPositionDistanceGuess)); mEdgeGlowTop.setSize(width, getHeight()); if (mEdgeGlowTop.draw(canvas)) { invalidate(); @@ -3277,10 +3279,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } if (!mEdgeGlowBottom.isFinished()) { final int restoreCount = canvas.save(); - final int width = getWidth(); + final int width = getWidth() - mListPadding.left - mListPadding.right; final int height = getHeight(); - canvas.translate(-width, Math.max(height, scrollY + mLastPositionDistanceGuess)); + canvas.translate(-width + mListPadding.left, + Math.max(height, scrollY + mLastPositionDistanceGuess)); canvas.rotate(180, width, 0); mEdgeGlowBottom.setSize(width, height); if (mEdgeGlowBottom.draw(canvas)) { @@ -4169,7 +4172,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (mScrollingCacheEnabled && !mCachingStarted) { setChildrenDrawnWithCacheEnabled(true); setChildrenDrawingCacheEnabled(true); - mCachingStarted = true; + mCachingStarted = mCachingActive = true; } } @@ -4178,7 +4181,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mClearScrollingCache = new Runnable() { public void run() { if (mCachingStarted) { - mCachingStarted = false; + mCachingStarted = mCachingActive = false; setChildrenDrawnWithCacheEnabled(false); if ((mPersistentDrawingCache & PERSISTENT_SCROLLING_CACHE) == 0) { setChildrenDrawingCacheEnabled(false); diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java index 190c0fc40907..072992efef2b 100644 --- a/core/java/android/widget/AdapterViewAnimator.java +++ b/core/java/android/widget/AdapterViewAnimator.java @@ -279,6 +279,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> * * @param whichChild the index of the child view to display */ + @android.view.RemotableViewMethod public void setDisplayedChild(int whichChild) { setDisplayedChild(whichChild, true); } diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index e8ce4e97237d..27610b9fe5c2 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -92,6 +92,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe static final boolean DEBUG = false; static final String TAG = "AutoCompleteTextView"; + static final int EXPAND_MAX = 3; + private CharSequence mHintText; private TextView mHintView; private int mHintResource; @@ -204,7 +206,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe private void onClickImpl() { // If the dropdown is showing, bring the keyboard to the front // when the user touches the text field. - if (mPopup.isShowing()) { + if (isPopupShowing()) { ensureImeVisible(true); } } @@ -648,6 +650,12 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe return true; } } + + if (isPopupShowing() && keyCode == KeyEvent.KEYCODE_TAB && event.hasNoModifiers()) { + performCompletion(); + return true; + } + return super.onKeyUp(keyCode, event); } @@ -666,6 +674,10 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } } + if (isPopupShowing() && keyCode == KeyEvent.KEYCODE_TAB && event.hasNoModifiers()) { + return true; + } + mLastKeyCode = keyCode; boolean handled = super.onKeyDown(keyCode, event); mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN; @@ -998,7 +1010,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe protected boolean setFrame(final int l, int t, final int r, int b) { boolean result = super.setFrame(l, t, r, b); - if (mPopup.isShowing()) { + if (isPopupShowing()) { showDropDown(); } @@ -1044,7 +1056,13 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mPopup.setAnchorView(this); } } + if (!isPopupShowing()) { + // Make sure the list does not obscure the IME when shown for the first time. + mPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED); + mPopup.setListItemExpandMax(EXPAND_MAX); + } mPopup.show(); + mPopup.getListView().setOverScrollMode(View.OVER_SCROLL_ALWAYS); } /** diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index 4a34b45e57d3..cd3862fa7f3f 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -229,36 +229,34 @@ public class DatePicker extends FrameLayout { } else { setSpinnersShown(spinnersShown); setCalendarViewShown(calendarViewShown); + } - // set the min date giving priority of the minDate over startYear - mTempDate.clear(); - if (!TextUtils.isEmpty(minDate)) { - if (!parseDate(minDate, mTempDate)) { - mTempDate.set(startYear, 0, 1); - } - } else { + // set the min date giving priority of the minDate over startYear + mTempDate.clear(); + if (!TextUtils.isEmpty(minDate)) { + if (!parseDate(minDate, mTempDate)) { mTempDate.set(startYear, 0, 1); } - mMinDate.clear(); - setMinDate(mTempDate.getTimeInMillis()); - - // set the max date giving priority of the minDate over startYear - mTempDate.clear(); - if (!TextUtils.isEmpty(maxDate)) { - if (!parseDate(maxDate, mTempDate)) { - mTempDate.set(endYear, 11, 31); - } - } else { + } else { + mTempDate.set(startYear, 0, 1); + } + setMinDate(mTempDate.getTimeInMillis()); + + // set the max date giving priority of the maxDate over endYear + mTempDate.clear(); + if (!TextUtils.isEmpty(maxDate)) { + if (!parseDate(maxDate, mTempDate)) { mTempDate.set(endYear, 11, 31); } - mMaxDate.clear(); - setMaxDate(mTempDate.getTimeInMillis()); - - // initialize to current date - mCurrentDate.setTimeInMillis(System.currentTimeMillis()); - init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), mCurrentDate - .get(Calendar.DAY_OF_MONTH), null); + } else { + mTempDate.set(endYear, 11, 31); } + setMaxDate(mTempDate.getTimeInMillis()); + + // initialize to current date + mCurrentDate.setTimeInMillis(System.currentTimeMillis()); + init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), mCurrentDate + .get(Calendar.DAY_OF_MONTH), null); // re-order the number spinners to match the current date format reorderSpinners(); diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 13f0890c9d92..d75748ff2cc8 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -1422,11 +1422,11 @@ public class HorizontalScrollView extends FrameLayout { final int scrollX = mScrollX; if (!mEdgeGlowLeft.isFinished()) { final int restoreCount = canvas.save(); - final int height = getHeight(); + final int height = getHeight() - mPaddingTop - mPaddingBottom; canvas.rotate(270); - canvas.translate(-height, Math.min(0, scrollX)); - mEdgeGlowLeft.setSize(getHeight(), getWidth()); + canvas.translate(-height + mPaddingTop, Math.min(0, scrollX)); + mEdgeGlowLeft.setSize(height, getWidth()); if (mEdgeGlowLeft.draw(canvas)) { invalidate(); } @@ -1435,10 +1435,10 @@ public class HorizontalScrollView extends FrameLayout { if (!mEdgeGlowRight.isFinished()) { final int restoreCount = canvas.save(); final int width = getWidth(); - final int height = getHeight(); + final int height = getHeight() - mPaddingTop - mPaddingBottom; canvas.rotate(90); - canvas.translate(0, + canvas.translate(-mPaddingTop, -(Math.max(getScrollRange(), scrollX) + width)); mEdgeGlowRight.setSize(height, width); if (mEdgeGlowRight.draw(canvas)) { diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index 8116a1237112..564287785abf 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -26,10 +26,10 @@ import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; import android.view.View.MeasureSpec; import android.view.View.OnTouchListener; +import android.view.ViewGroup; +import android.view.ViewParent; /** * A ListPopupWindow anchors itself to a host view and displays a @@ -65,6 +65,7 @@ public class ListPopupWindow { private boolean mDropDownAlwaysVisible = false; private boolean mForceIgnoreOutsideTouch = false; + int mListItemExpandMaximum = Integer.MAX_VALUE; private View mPromptView; private int mPromptPosition = POSITION_PROMPT_ABOVE; @@ -519,6 +520,7 @@ public class ListPopupWindow { int heightSpec = 0; boolean noInputMethod = isInputMethodNotNeeded(); + mPopup.setAllowScrollingAnchorParent(!noInputMethod); if (mPopup.isShowing()) { if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) { @@ -604,6 +606,7 @@ public class ListPopupWindow { removePromptView(); mPopup.setContentView(null); mDropDownList = null; + mHandler.removeCallbacks(mResizePopupRunnable); } /** @@ -774,6 +777,16 @@ public class ListPopupWindow { } /** + * The maximum number of list items that can be visible and still have + * the list expand when touched. + * + * @param max Max number of items that can be visible and still allow the list to expand. + */ + void setListItemExpandMax(int max) { + mListItemExpandMaximum = max; + } + + /** * Filter key down events. By forwarding key down events to this function, * views using non-modal ListPopupWindow can have it handle key selection of items. * @@ -1209,8 +1222,11 @@ public class ListPopupWindow { private class ResizePopupRunnable implements Runnable { public void run() { - mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); - show(); + if (mDropDownList != null && mDropDownList.getCount() > mDropDownList.getChildCount() && + mDropDownList.getChildCount() <= mListItemExpandMaximum) { + mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); + show(); + } } } @@ -1222,7 +1238,7 @@ public class ListPopupWindow { if (action == MotionEvent.ACTION_DOWN && mPopup != null && mPopup.isShowing() && - (x >= 0 && x < getWidth() && y >= 0 && y < getHeight())) { + (x >= 0 && x < mPopup.getWidth() && y >= 0 && y < mPopup.getHeight())) { mHandler.postDelayed(mResizePopupRunnable, EXPAND_LIST_TIMEOUT); } else if (action == MotionEvent.ACTION_UP) { mHandler.removeCallbacks(mResizePopupRunnable); diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 12a0ebf254e0..2802144b5e06 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -3013,12 +3013,9 @@ public class ListView extends AbsListView { return mItemsCanFocus; } - /** - * @hide Pending API council approval. - */ @Override public boolean isOpaque() { - return (mCachingStarted && mIsCacheColorOpaque && mDividerIsOpaque && + return (mCachingActive && mIsCacheColorOpaque && mDividerIsOpaque && hasOpaqueScrollbars()) || super.isOpaque(); } @@ -3071,6 +3068,10 @@ public class ListView extends AbsListView { @Override protected void dispatchDraw(Canvas canvas) { + if (mCachingStarted) { + mCachingActive = true; + } + // Draw the dividers final int dividerHeight = mDividerHeight; final Drawable overscrollHeader = mOverScrollHeader; @@ -3164,7 +3165,6 @@ public class ListView extends AbsListView { } } else { int top; - int listTop = effectivePaddingTop; final int scrollY = mScrollY; @@ -3181,7 +3181,7 @@ public class ListView extends AbsListView { View child = getChildAt(i); top = child.getTop(); // Don't draw dividers next to items that are not enabled - if (top > listTop) { + if (top > effectivePaddingTop) { if ((areAllItemsSelectable || (adapter.isEnabled(first + i) && (i == count - 1 || adapter.isEnabled(first + i + 1))))) { @@ -3220,6 +3220,15 @@ public class ListView extends AbsListView { super.dispatchDraw(canvas); } + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + boolean more = super.drawChild(canvas, child, drawingTime); + if (mCachingActive && child.mCachingFailed) { + mCachingActive = false; + } + return more; + } + /** * Draws a divider for the given child in the given bounds. * @@ -3558,6 +3567,7 @@ public class ListView extends AbsListView { @Override public boolean onTouchEvent(MotionEvent ev) { + //noinspection SimplifiableIfStatement if (mItemsCanFocus && ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { // Don't handle edge touches immediately -- they may actually belong to one of our // descendants. diff --git a/core/java/android/widget/MultiAutoCompleteTextView.java b/core/java/android/widget/MultiAutoCompleteTextView.java index ae8027784bc3..02c1ec757cd8 100644 --- a/core/java/android/widget/MultiAutoCompleteTextView.java +++ b/core/java/android/widget/MultiAutoCompleteTextView.java @@ -17,24 +17,13 @@ package android.widget; import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.text.Editable; -import android.text.Selection; -import android.text.Spanned; -import android.text.Spannable; import android.text.SpannableString; +import android.text.Spanned; import android.text.TextUtils; import android.text.method.QwertyKeyListener; import android.util.AttributeSet; -import android.util.Log; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.android.internal.R; +import android.widget.MultiAutoCompleteTextView.Tokenizer; /** * An editable text view, extending {@link AutoCompleteTextView}, that diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index c5b1caae79d4..8e660ff17a73 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -583,7 +583,7 @@ public class NumberPicker extends LinearLayout { updateInputTextView(); updateIncrementAndDecrementButtonsVisibilityState(); - if (mFlingable) { + if (mFlingable && !isInEditMode()) { // Start with shown selector wheel and hidden controls. When made // visible hide the selector and fade-in the controls to suggest // fling interaction. @@ -1056,7 +1056,7 @@ public class NumberPicker extends LinearLayout { super.onAttachedToWindow(); // make sure we show the controls only the very // first time the user sees this widget - if (mFlingable) { + if (mFlingable && !isInEditMode()) { // animate a bit slower the very first time showInputControls(mShowInputControlsAnimimationDuration * 2); } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 439e0cabe19f..4b858d0dd154 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -90,6 +90,7 @@ public class PopupWindow { private int mSplitTouchEnabled = -1; private boolean mLayoutInScreen; private boolean mClipToScreen; + private boolean mAllowScrollingAnchorParent = true; private OnTouchListener mTouchInterceptor; @@ -592,6 +593,16 @@ public class PopupWindow { mClipToScreen = enabled; setClippingEnabled(!enabled); } + + /** + * Allow PopupWindow to scroll the anchor's parent to provide more room + * for the popup. Enabled by default. + * + * @param enabled True to scroll the anchor's parent when more room is desired by the popup. + */ + void setAllowScrollingAnchorParent(boolean enabled) { + mAllowScrollingAnchorParent = enabled; + } /** * <p>Indicates whether the popup window supports splitting touches.</p> @@ -1049,11 +1060,13 @@ public class PopupWindow { // if the drop down disappears at the bottom of the screen. we try to // scroll a parent scrollview or move the drop down back up on top of // the edit box - int scrollX = anchor.getScrollX(); - int scrollY = anchor.getScrollY(); - Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff, - scrollY + mPopupHeight + anchor.getHeight() + yoff); - anchor.requestRectangleOnScreen(r, true); + if (mAllowScrollingAnchorParent) { + int scrollX = anchor.getScrollX(); + int scrollY = anchor.getScrollY(); + Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff, + scrollY + mPopupHeight + anchor.getHeight() + yoff); + anchor.requestRectangleOnScreen(r, true); + } // now we re-evaluate the space available, and decide from that // whether the pop-up will go above or below the anchor. diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 482ce56f83ae..c854fac08626 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1056,24 +1056,34 @@ public class RemoteViews implements Parcelable, Filter { } /** - * Equivalent to calling {@link AdapterViewFlipper#showNext()} + * Equivalent to calling {@link AdapterViewAnimator#showNext()} * - * @param viewId The id of the view on which to call {@link AdapterViewFlipper#showNext()} + * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()} */ public void showNext(int viewId) { addAction(new ReflectionActionWithoutParams(viewId, "showNext")); } /** - * Equivalent to calling {@link AdapterViewFlipper#showPrevious()} + * Equivalent to calling {@link AdapterViewAnimator#showPrevious()} * - * @param viewId The id of the view on which to call {@link AdapterViewFlipper#showPrevious()} + * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()} */ public void showPrevious(int viewId) { addAction(new ReflectionActionWithoutParams(viewId, "showPrevious")); } /** + * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)} + * + * @param viewId The id of the view on which to call + * {@link AdapterViewAnimator#setDisplayedChild(int)} + */ + public void setDisplayedChild(int viewId, int childIndex) { + setInt(viewId, "setDisplayedChild", childIndex); + } + + /** * Equivalent to calling View.setVisibility * * @param viewId The id of the view whose visibility should change diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index 0a48febaed82..13a911b1d9ff 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -914,7 +914,9 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback // view and queueing it to be loaded if it has not already been loaded. Context context = parent.getContext(); RemoteViews rv = mCache.getRemoteViewsAt(position); - int typeId = mCache.getMetaDataAt(position).typeId; + RemoteViewsIndexMetaData indexMetaData = mCache.getMetaDataAt(position); + indexMetaData.isRequested = true; + int typeId = indexMetaData.typeId; // Reuse the convert view where possible if (layout != null) { diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index d1cfcec6c1c9..4cc4a2715027 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -1470,9 +1470,9 @@ public class ScrollView extends FrameLayout { final int scrollY = mScrollY; if (!mEdgeGlowTop.isFinished()) { final int restoreCount = canvas.save(); - final int width = getWidth(); + final int width = getWidth() - mPaddingLeft - mPaddingRight; - canvas.translate(0, Math.min(0, scrollY)); + canvas.translate(mPaddingLeft, Math.min(0, scrollY)); mEdgeGlowTop.setSize(width, getHeight()); if (mEdgeGlowTop.draw(canvas)) { invalidate(); @@ -1481,10 +1481,11 @@ public class ScrollView extends FrameLayout { } if (!mEdgeGlowBottom.isFinished()) { final int restoreCount = canvas.save(); - final int width = getWidth(); + final int width = getWidth() - mPaddingLeft - mPaddingRight; final int height = getHeight(); - canvas.translate(-width, Math.max(getScrollRange(), scrollY) + height); + canvas.translate(-width + mPaddingLeft, + Math.max(getScrollRange(), scrollY) + height); canvas.rotate(180, width, 0); mEdgeGlowBottom.setSize(width, height); if (mEdgeGlowBottom.draw(canvas)) { diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index f051b770ee0d..22edcd0e9462 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -394,7 +394,6 @@ public class SearchView extends LinearLayout { if (mIconifiedByDefault == iconified) return; mIconifiedByDefault = iconified; updateViewsVisibility(iconified); - setImeVisibility(!iconified); } /** diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java index a92272ce1ce4..b23a855f4d65 100644 --- a/core/java/android/widget/Spinner.java +++ b/core/java/android/widget/Spinner.java @@ -23,10 +23,9 @@ import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.res.TypedArray; import android.database.DataSetObserver; -import android.graphics.drawable.Drawable; import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.util.DisplayMetrics; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -66,9 +65,12 @@ public class Spinner extends AbsSpinner implements OnClickListener { private SpinnerPopup mPopup; private DropDownAdapter mTempAdapter; + int mDropDownWidth; private int mGravity; + private Rect mTempRect = new Rect(); + /** * Construct a new spinner with the given context's theme. * @@ -158,9 +160,9 @@ public class Spinner extends AbsSpinner implements OnClickListener { case MODE_DROPDOWN: { DropdownPopup popup = new DropdownPopup(context, attrs, defStyle); - popup.setWidth(a.getLayoutDimension( + mDropDownWidth = a.getLayoutDimension( com.android.internal.R.styleable.Spinner_dropDownWidth, - ViewGroup.LayoutParams.WRAP_CONTENT)); + ViewGroup.LayoutParams.WRAP_CONTENT); popup.setBackgroundDrawable(a.getDrawable( com.android.internal.R.styleable.Spinner_popupBackground)); popup.setVerticalOffset(a.getDimensionPixelOffset( @@ -260,7 +262,8 @@ public class Spinner extends AbsSpinner implements OnClickListener { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mPopup != null && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) { final int measuredWidth = getMeasuredWidth(); - setMeasuredDimension(Math.min(Math.max(measuredWidth, mPopup.measureContentWidth()), + setMeasuredDimension(Math.min(Math.max(measuredWidth, + measureContentWidth(getAdapter(), getBackground())), MeasureSpec.getSize(widthMeasureSpec)), getMeasuredHeight()); } @@ -455,7 +458,51 @@ public class Spinner extends AbsSpinner implements OnClickListener { public CharSequence getPrompt() { return mPopup.getHintText(); } - + + int measureContentWidth(SpinnerAdapter adapter, Drawable background) { + if (adapter == null) { + return 0; + } + + int width = 0; + View itemView = null; + int itemType = 0; + final int widthMeasureSpec = + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + final int heightMeasureSpec = + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + + // Make sure the number of items we'll measure is capped. If it's a huge data set + // with wildly varying sizes, oh well. + int start = Math.max(0, getSelectedItemPosition()); + final int end = Math.min(adapter.getCount(), start + MAX_ITEMS_MEASURED); + final int count = end - start; + start = Math.max(0, start - (MAX_ITEMS_MEASURED - count)); + for (int i = start; i < end; i++) { + final int positionType = adapter.getItemViewType(i); + if (positionType != itemType) { + itemType = positionType; + itemView = null; + } + itemView = adapter.getView(i, itemView, this); + if (itemView.getLayoutParams() == null) { + itemView.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + } + itemView.measure(widthMeasureSpec, heightMeasureSpec); + width = Math.max(width, itemView.getMeasuredWidth()); + } + + // Add background padding to measured width + if (background != null) { + background.getPadding(mTempRect); + width += mTempRect.left + mTempRect.right; + } + + return width; + } + /** * <p>Wrapper class for an Adapter. Transforms the embedded Adapter instance * into a ListAdapter.</p> @@ -581,8 +628,6 @@ public class Spinner extends AbsSpinner implements OnClickListener { */ public void setPromptText(CharSequence hintText); public CharSequence getHintText(); - - public int measureContentWidth(); } private class DialogPopup implements SpinnerPopup, DialogInterface.OnClickListener { @@ -624,23 +669,14 @@ public class Spinner extends AbsSpinner implements OnClickListener { setSelection(which); dismiss(); } - - public int measureContentWidth() { - // Doesn't matter for dialog mode - return 0; - } } private class DropdownPopup extends ListPopupWindow implements SpinnerPopup { private CharSequence mHintText; - private int mPopupMaxWidth; - private Rect mTempRect = new Rect(); + private ListAdapter mAdapter; public DropdownPopup(Context context, AttributeSet attrs, int defStyleRes) { super(context, attrs, 0, defStyleRes); - - final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - mPopupMaxWidth = metrics.widthPixels / 2; setAnchorView(Spinner.this); setModal(true); @@ -653,6 +689,12 @@ public class Spinner extends AbsSpinner implements OnClickListener { }); } + @Override + public void setAdapter(ListAdapter adapter) { + super.setAdapter(adapter); + mAdapter = adapter; + } + public CharSequence getHintText() { return mHintText; } @@ -664,59 +706,18 @@ public class Spinner extends AbsSpinner implements OnClickListener { @Override public void show() { - setWidth(Spinner.this.getWidth()); + if (mDropDownWidth == WRAP_CONTENT) { + setWidth(Math.max(measureContentWidth((SpinnerAdapter) mAdapter, getBackground()), + Spinner.this.getWidth())); + } else if (mDropDownWidth == MATCH_PARENT) { + setWidth(Spinner.this.getWidth()); + } else { + setWidth(mDropDownWidth); + } setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); super.show(); getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); setSelection(Spinner.this.getSelectedItemPosition()); } - - @Override - public int measureContentWidth() { - final SpinnerAdapter adapter = getAdapter(); - if (adapter == null) { - return 0; - } - - int width = 0; - View itemView = null; - int itemType = 0; - final int widthMeasureSpec = - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - final int heightMeasureSpec = - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - - // Make sure the number of items we'll measure is capped. If it's a huge data set - // with wildly varying sizes, oh well. - final int start = Math.max(0, getSelectedItemPosition()); - final int count = Math.min(adapter.getCount(), start + MAX_ITEMS_MEASURED); - for (int i = start; i < count; i++) { - final int positionType = adapter.getItemViewType(i); - if (positionType != itemType) { - itemType = positionType; - itemView = null; - } - itemView = adapter.getDropDownView(i, itemView, Spinner.this); - if (itemView.getLayoutParams() == null) { - itemView.setLayoutParams(generateDefaultLayoutParams()); - } - itemView.measure(widthMeasureSpec, heightMeasureSpec); - width = Math.max(width, itemView.getMeasuredWidth()); - } - - // Add background padding to measured width - Drawable popupBackground = getBackground(); - if (popupBackground != null) { - popupBackground.getPadding(mTempRect); - width += mTempRect.left + mTempRect.right; - } - - return width; - } - - private ViewGroup.LayoutParams generateDefaultLayoutParams() { - return new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT); - } } } diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java index 6a09d3548e79..e6cf31e68918 100644 --- a/core/java/android/widget/StackView.java +++ b/core/java/android/widget/StackView.java @@ -1207,6 +1207,7 @@ public class StackView extends AdapterViewAnimator { void invalidateGlobalRegion(View v, Rect r) { // We need to make a new rect here, so as not to modify the one passed globalInvalidateRect.set(r); + globalInvalidateRect.union(0, 0, getWidth(), getHeight()); View p = v; if (!(v.getParent() != null && v.getParent() instanceof View)) return; @@ -1223,8 +1224,9 @@ public class StackView extends AdapterViewAnimator { firstPass = false; p = (View) p.getParent(); parentRect.set(p.getScrollX(), p.getScrollY(), - p.getWidth() + p.getScrollX(), p.getHeight() + p.getScrollY()); - + p.getWidth() + p.getScrollX(), p.getHeight() + p.getScrollY()); + p.invalidate(globalInvalidateRect.left, globalInvalidateRect.top, + globalInvalidateRect.right, globalInvalidateRect.bottom); } p.invalidate(globalInvalidateRect.left, globalInvalidateRect.top, diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 772eefd8be66..d90d5bed8dc6 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -304,15 +304,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } InputMethodState mInputMethodState; - int mTextSelectHandleLeftRes; - int mTextSelectHandleRightRes; - int mTextSelectHandleRes; - int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout; - int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout; + private int mTextSelectHandleLeftRes; + private int mTextSelectHandleRightRes; + private int mTextSelectHandleRes; + private int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout; + private int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout; - Drawable mSelectHandleLeft; - Drawable mSelectHandleRight; - Drawable mSelectHandleCenter; + private int mCursorDrawableRes; + private final Drawable[] mCursorDrawable = new Drawable[2]; + private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 + + private Drawable mSelectHandleLeft; + private Drawable mSelectHandleRight; + private Drawable mSelectHandleCenter; private int mLastDownPositionX, mLastDownPositionY; private Callback mCustomSelectionActionModeCallback; @@ -742,6 +746,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } break; + case com.android.internal.R.styleable.TextView_textCursorDrawable: + mCursorDrawableRes = a.getResourceId(attr, 0); + break; + case com.android.internal.R.styleable.TextView_textSelectHandleLeft: mTextSelectHandleLeftRes = a.getResourceId(attr, 0); break; @@ -3770,33 +3778,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mHighlightPathBogus) { invalidateCursor(); } else { - synchronized (sTempRect) { - /* - * The reason for this concern about the thickness of the - * cursor and doing the floor/ceil on the coordinates is that - * some EditTexts (notably textfields in the Browser) have - * anti-aliased text where not all the characters are - * necessarily at integer-multiple locations. This should - * make sure the entire cursor gets invalidated instead of - * sometimes missing half a pixel. - */ + final int horizontalPadding = getCompoundPaddingLeft(); + final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true); - float thick = FloatMath.ceil(mTextPaint.getStrokeWidth()); - if (thick < 1.0f) { - thick = 1.0f; - } - - thick /= 2; + if (mCursorCount == 0) { + synchronized (sTempRect) { + /* + * The reason for this concern about the thickness of the + * cursor and doing the floor/ceil on the coordinates is that + * some EditTexts (notably textfields in the Browser) have + * anti-aliased text where not all the characters are + * necessarily at integer-multiple locations. This should + * make sure the entire cursor gets invalidated instead of + * sometimes missing half a pixel. + */ + float thick = FloatMath.ceil(mTextPaint.getStrokeWidth()); + if (thick < 1.0f) { + thick = 1.0f; + } - mHighlightPath.computeBounds(sTempRect, false); + thick /= 2.0f; - int left = getCompoundPaddingLeft(); - int top = getExtendedPaddingTop() + getVerticalOffset(true); + mHighlightPath.computeBounds(sTempRect, false); - invalidate((int) FloatMath.floor(left + sTempRect.left - thick), - (int) FloatMath.floor(top + sTempRect.top - thick), - (int) FloatMath.ceil(left + sTempRect.right + thick), - (int) FloatMath.ceil(top + sTempRect.bottom + thick)); + invalidate((int) FloatMath.floor(horizontalPadding + sTempRect.left - thick), + (int) FloatMath.floor(verticalPadding + sTempRect.top - thick), + (int) FloatMath.ceil(horizontalPadding + sTempRect.right + thick), + (int) FloatMath.ceil(verticalPadding + sTempRect.bottom + thick)); + } + } else { + for (int i = 0; i < mCursorCount; i++) { + Rect bounds = mCursorDrawable[i].getBounds(); + invalidate(bounds.left + horizontalPadding, bounds.top + verticalPadding, + bounds.right + horizontalPadding, bounds.bottom + verticalPadding); + } } } } @@ -3836,13 +3851,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener line2 = mLayout.getLineForOffset(last); int bottom = mLayout.getLineTop(line2 + 1); - int voffset = getVerticalOffset(true); - int left = getCompoundPaddingLeft() + mScrollX; - invalidate(left, top + voffset + getExtendedPaddingTop(), - left + getWidth() - getCompoundPaddingLeft() - - getCompoundPaddingRight(), - bottom + voffset + getExtendedPaddingTop()); + final int horizontalPadding = getCompoundPaddingLeft(); + final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true); + + // If used, the cursor drawables can have an arbitrary dimension that can go beyond + // the invalidated lines specified above. + for (int i = 0; i < mCursorCount; i++) { + Rect bounds = mCursorDrawable[i].getBounds(); + top = Math.min(top, bounds.top); + bottom = Math.max(bottom, bounds.bottom); + // Horizontal bounds are already full width, no need to update + } + + invalidate(horizontalPadding + mScrollX, top + verticalPadding, + horizontalPadding + mScrollX + getWidth() - + getCompoundPaddingLeft() - getCompoundPaddingRight(), + bottom + verticalPadding); } } } @@ -4346,6 +4371,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Path highlight = null; int selStart = -1, selEnd = -1; + boolean drawCursor = false; // If there is no movement method, then there can be no selection. // Check that first and attempt to skip everything having to do with @@ -4366,6 +4392,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mHighlightPathBogus) { mHighlightPath.reset(); mLayout.getCursorPath(selStart, mHighlightPath, mText); + updateCursorsPositions(); mHighlightPathBogus = false; } @@ -4376,8 +4403,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener (mCurrentAlpha * Color.alpha(cursorcolor)) / 255); } mHighlightPaint.setStyle(Paint.Style.STROKE); - highlight = mHighlightPath; + drawCursor = true; } } else { if (mHighlightPathBogus) { @@ -4460,6 +4487,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mCorrectionHighlighter.draw(canvas, cursorOffsetVertical); } + if (drawCursor) { + drawCursor(canvas, cursorOffsetVertical); + // Rely on the drawable entirely, do not draw the cursor line. + // Has to be done after the IMM related code above which relies on the highlight. + highlight = null; + } + layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical); if (mMarquee != null && mMarquee.shouldDrawGhost()) { @@ -4478,6 +4512,52 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener updateCursorControllerPositions(); } + private void updateCursorsPositions() { + if (mCursorDrawableRes == 0) return; + + final int offset = getSelectionStart(); + final int line = mLayout.getLineForOffset(offset); + final int top = mLayout.getLineTop(line); + final int bottom = mLayout.getLineTop(line + 1); + + mCursorCount = mLayout.isLevelBoundary(offset) ? 2 : 1; + + int middle = bottom; + if (mCursorCount == 2) { + // Similar to what is done in {@link Layout.#getCursorPath(int, Path, CharSequence)} + middle = (top + bottom) >> 1; + } + + updateCursorPosition(0, top, middle, mLayout.getPrimaryHorizontal(offset)); + + if (mCursorCount == 2) { + updateCursorPosition(1, middle, bottom, mLayout.getSecondaryHorizontal(offset)); + } + } + + private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) { + if (mCursorDrawable[cursorIndex] == null) + mCursorDrawable[cursorIndex] = mContext.getResources().getDrawable(mCursorDrawableRes); + + if (mTempRect == null) mTempRect = new Rect(); + + mCursorDrawable[cursorIndex].getPadding(mTempRect); + final int width = mCursorDrawable[cursorIndex].getIntrinsicWidth(); + horizontal = Math.max(0.5f, horizontal - 0.5f); + final int left = (int) (horizontal) - mTempRect.left; + mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width, + bottom + mTempRect.bottom); + } + + private void drawCursor(Canvas canvas, int cursorOffsetVertical) { + final boolean translate = cursorOffsetVertical != 0; + if (translate) canvas.translate(0, cursorOffsetVertical); + for (int i = 0; i < mCursorCount; i++) { + mCursorDrawable[i].draw(canvas); + } + if (translate) canvas.translate(0, -cursorOffsetVertical); + } + /** * Update the positions of the CursorControllers. Needed by WebTextView, * which does not draw. @@ -8012,11 +8092,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; case ID_SELECT_ALL: + // This does not enter text selection mode. Text is highlighted, so that it can be + // bulk edited, like selectAllOnFocus does. selectAll(); - // Update controller positions after selection change. - if (hasSelectionController()) { - getSelectionController().show(); - } return true; case ID_PASTE: @@ -8134,6 +8212,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; } + boolean handled = false; + // Long press in empty space moves cursor and shows the Paste affordance if available. if (!isPositionOnText(mLastDownPositionX, mLastDownPositionY) && mInsertionControllerEnabled) { @@ -8141,11 +8221,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener stopSelectionActionMode(); Selection.setSelection((Spannable)mText, offset); getInsertionController().show(0); - mDiscardNextActionUp = true; - return true; + handled = true; } - if (mSelectionActionMode != null) { + if (!handled && mSelectionActionMode != null) { if (touchPositionIsInSelection()) { // Start a drag final int start = getSelectionStart(); @@ -8156,21 +8235,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener startDrag(data, getTextThumbnailBuilder(selectedText), localState, 0); stopSelectionActionMode(); } else { + // New selection at touch position updateSelectedRegion(); } - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - mDiscardNextActionUp = true; - return true; + handled = true; } // Start a new selection - if (startSelectionActionMode()) { + handled |= !handled && startSelectionActionMode(); + + if (handled) { performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); mDiscardNextActionUp = true; - return true; } - return false; + return handled; } /** @@ -8700,7 +8779,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } mDrawable = mSelectHandleLeft; handleWidth = mDrawable.getIntrinsicWidth(); - mHotspotX = (handleWidth * 3) / 4; + mHotspotX = handleWidth * 3.0f / 4.0f; break; } @@ -8711,7 +8790,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } mDrawable = mSelectHandleRight; handleWidth = mDrawable.getIntrinsicWidth(); - mHotspotX = handleWidth / 4; + mHotspotX = handleWidth / 4.0f; break; } @@ -8723,7 +8802,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } mDrawable = mSelectHandleCenter; handleWidth = mDrawable.getIntrinsicWidth(); - mHotspotX = handleWidth / 2; + mHotspotX = handleWidth / 2.0f; mIsInsertionHandle = true; break; } @@ -8938,8 +9017,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int lineBottom = mLayout.getLineBottom(line); final Rect bounds = sCursorControllerTempRect; - bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - mHotspotX) - + TextView.this.mScrollX; + bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX) + + TextView.this.mScrollX; bounds.top = (bottom ? lineBottom : lineTop - mHeight) + TextView.this.mScrollY; bounds.right = bounds.left + width; diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index 50c88dbbd2f8..88a0e0130473 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -534,14 +534,14 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { } return true; } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { - if (mMediaPlayer.isPlaying()) { + if (!mMediaPlayer.isPlaying()) { start(); mMediaController.hide(); } return true; } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { - if (!mMediaPlayer.isPlaying()) { + if (mMediaPlayer.isPlaying()) { pause(); mMediaController.show(); } diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java index 7b668932fc9a..3c683d6b5330 100644 --- a/core/java/android/widget/ViewAnimator.java +++ b/core/java/android/widget/ViewAnimator.java @@ -96,6 +96,7 @@ public class ViewAnimator extends FrameLayout { * * @param whichChild the index of the child view to display */ + @android.view.RemotableViewMethod public void setDisplayedChild(int whichChild) { mWhichChild = whichChild; if (whichChild >= getChildCount()) { @@ -122,6 +123,7 @@ public class ViewAnimator extends FrameLayout { /** * Manually shows the next child. */ + @android.view.RemotableViewMethod public void showNext() { setDisplayedChild(mWhichChild + 1); } @@ -129,6 +131,7 @@ public class ViewAnimator extends FrameLayout { /** * Manually shows the previous child. */ + @android.view.RemotableViewMethod public void showPrevious() { setDisplayedChild(mWhichChild - 1); } diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 471a5a91f7b1..8f1354b566f1 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -19,6 +19,7 @@ package com.android.internal.app; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuPopupHelper; import com.android.internal.view.menu.SubMenuBuilder; +import com.android.internal.widget.ActionBarContainer; import com.android.internal.widget.ActionBarContextView; import com.android.internal.widget.ActionBarView; @@ -65,7 +66,7 @@ public class ActionBarImpl extends ActionBar { private Activity mActivity; private Dialog mDialog; - private FrameLayout mContainerView; + private ActionBarContainer mContainerView; private ActionBarView mActionView; private ActionBarContextView mUpperContextView; private LinearLayout mLowerContextView; @@ -92,6 +93,7 @@ public class ActionBarImpl extends ActionBar { final Handler mHandler = new Handler(); private Animator mCurrentAnim; + private boolean mShowHideAnimationEnabled; private static final TimeInterpolator sFadeOutInterpolator = new DecelerateInterpolator(); @@ -150,6 +152,7 @@ public class ActionBarImpl extends ActionBar { mContentView.setTranslationY(0); } mContainerView.setVisibility(View.GONE); + mContainerView.setTransitioning(false); mCurrentAnim = null; } @@ -204,7 +207,7 @@ public class ActionBarImpl extends ActionBar { com.android.internal.R.id.action_context_bar); mLowerContextView = (LinearLayout) decor.findViewById( com.android.internal.R.id.lower_action_context_bar); - mContainerView = (FrameLayout) decor.findViewById( + mContainerView = (ActionBarContainer) decor.findViewById( com.android.internal.R.id.action_bar_container); if (mActionView == null || mUpperContextView == null || mContainerView == null) { @@ -217,6 +220,20 @@ public class ActionBarImpl extends ActionBar { CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT; } + /** + * Enables or disables animation between show/hide states. + * If animation is disabled using this method, animations in progress + * will be finished. + * + * @param enabled true to animate, false to not animate. + */ + public void setShowHideAnimationEnabled(boolean enabled) { + mShowHideAnimationEnabled = enabled; + if (!enabled && mCurrentAnim != null) { + mCurrentAnim.end(); + } + } + public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) { mMenuVisibilityListeners.add(listener); } @@ -361,6 +378,7 @@ public class ActionBarImpl extends ActionBar { mLowerContextView.setVisibility(View.VISIBLE); } mActionMode = mode; + show(); return mode; } return null; @@ -487,18 +505,23 @@ public class ActionBarImpl extends ActionBar { return; } mContainerView.setVisibility(View.VISIBLE); - mContainerView.setAlpha(0); - AnimatorSet anim = new AnimatorSet(); - AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 1)); - if (mContentView != null) { - b.with(ObjectAnimator.ofFloat(mContentView, "translationY", - -mContainerView.getHeight(), 0)); - mContainerView.setTranslationY(-mContainerView.getHeight()); - b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0)); + + if (mShowHideAnimationEnabled) { + mContainerView.setAlpha(0); + AnimatorSet anim = new AnimatorSet(); + AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 1)); + if (mContentView != null) { + b.with(ObjectAnimator.ofFloat(mContentView, "translationY", + -mContainerView.getHeight(), 0)); + mContainerView.setTranslationY(-mContainerView.getHeight()); + b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0)); + } + anim.addListener(mShowListener); + mCurrentAnim = anim; + anim.start(); + } else { + mShowListener.onAnimationEnd(null); } - anim.addListener(mShowListener); - mCurrentAnim = anim; - anim.start(); } @Override @@ -509,18 +532,24 @@ public class ActionBarImpl extends ActionBar { if (mContainerView.getVisibility() == View.GONE) { return; } - mContainerView.setAlpha(1); - AnimatorSet anim = new AnimatorSet(); - AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 0)); - if (mContentView != null) { - b.with(ObjectAnimator.ofFloat(mContentView, "translationY", - 0, -mContainerView.getHeight())); - b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", - -mContainerView.getHeight())); - } - anim.addListener(mHideListener); - mCurrentAnim = anim; - anim.start(); + + if (mShowHideAnimationEnabled) { + mContainerView.setAlpha(1); + mContainerView.setTransitioning(true); + AnimatorSet anim = new AnimatorSet(); + AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 0)); + if (mContentView != null) { + b.with(ObjectAnimator.ofFloat(mContentView, "translationY", + 0, -mContainerView.getHeight())); + b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", + -mContainerView.getHeight())); + } + anim.addListener(mHideListener); + mCurrentAnim = anim; + anim.start(); + } else { + mHideListener.onAnimationEnd(null); + } } public boolean isShowing() { diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java index ff15e44f7e50..7775f00a50a4 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuView.java @@ -25,7 +25,6 @@ import android.view.Gravity; import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewGroup; -import android.view.ViewParent; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; @@ -193,61 +192,125 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo menu.setActionWidthLimit(width); menu.setMaxActionItems(mMaxItems); + final boolean cleared = mMenu != menu; mMenu = menu; - updateChildren(true); + updateChildren(cleared); } public void updateChildren(boolean cleared) { - final boolean reserveOverflow = mReserveOverflow; - removeAllViews(); - - final ArrayList<MenuItemImpl> itemsToShow = mMenu.getActionItems(reserveOverflow); + final ArrayList<MenuItemImpl> itemsToShow = mMenu.getActionItems(mReserveOverflow); final int itemCount = itemsToShow.size(); boolean needsDivider = false; + int childIndex = 0; for (int i = 0; i < itemCount; i++) { final MenuItemImpl itemData = itemsToShow.get(i); boolean hasDivider = false; if (needsDivider) { - addView(makeDividerView(), makeDividerLayoutParams()); + if (!isDivider(getChildAt(childIndex))) { + addView(makeDividerView(), childIndex, makeDividerLayoutParams()); + } hasDivider = true; + childIndex++; } - View actionView = itemData.getActionView(); - - if (actionView != null) { - final ViewParent parent = actionView.getParent(); - if (parent instanceof ViewGroup) { - ((ViewGroup) parent).removeView(actionView); - } - addView(actionView, makeActionViewLayoutParams(actionView)); + View childToAdd = itemData.getActionView(); + boolean needsPreDivider = false; + if (childToAdd != null) { + childToAdd.setLayoutParams(makeActionViewLayoutParams(childToAdd)); } else { ActionMenuItemView view = (ActionMenuItemView) itemData.getItemView( MenuBuilder.TYPE_ACTION_BUTTON, this); view.setItemInvoker(this); - if (i > 0 && !hasDivider && view.hasText() && itemData.getIcon() == null) { - addView(makeDividerView(), makeDividerLayoutParams()); - } - addView(view); + needsPreDivider = i > 0 && !hasDivider && view.hasText() && + itemData.getIcon() == null; needsDivider = view.hasText(); + childToAdd = view; } + + boolean addPreDivider = removeChildrenUntil(childIndex, childToAdd, needsPreDivider); + + if (addPreDivider) addView(makeDividerView(), childIndex, makeDividerLayoutParams()); + if (needsPreDivider) childIndex++; + + if (getChildAt(childIndex) != childToAdd) { + addView(childToAdd, childIndex); + } + childIndex++; } - if (reserveOverflow) { - if (mMenu.getNonActionItems(true).size() > 0) { - if (itemCount > 0) { - addView(makeDividerView(), makeDividerLayoutParams()); - } + final boolean hasOverflow = mOverflowButton != null && mOverflowButton.getParent() == this; + final boolean needsOverflow = mReserveOverflow && mMenu.getNonActionItems(true).size() > 0; + + if (hasOverflow != needsOverflow) { + if (needsOverflow) { if (mOverflowButton == null) { OverflowMenuButton button = new OverflowMenuButton(mContext); mOverflowButton = button; } - addView(mOverflowButton); + boolean addDivider = removeChildrenUntil(childIndex, mOverflowButton, true); + if (addDivider && itemCount > 0) { + addView(makeDividerView(), childIndex, makeDividerLayoutParams()); + childIndex++; + } + addView(mOverflowButton, childIndex); + childIndex++; } else { - mOverflowButton = null; + removeView(mOverflowButton); + } + } else { + if (needsOverflow) { + boolean overflowDivider = itemCount > 0; + boolean addDivider = removeChildrenUntil(childIndex, mOverflowButton, + overflowDivider); + if (addDivider && itemCount > 0) { + addView(makeDividerView(), childIndex, makeDividerLayoutParams()); + } + if (overflowDivider) { + childIndex += 2; + } else { + childIndex++; + } } } + + while (getChildCount() > childIndex) { + removeViewAt(childIndex); + } + } + + private boolean removeChildrenUntil(int start, View targetChild, boolean needsPreDivider) { + final int childCount = getChildCount(); + boolean found = false; + for (int i = start; i < childCount; i++) { + final View child = getChildAt(i); + if (child == targetChild) { + found = true; + break; + } + } + + if (!found) { + return needsPreDivider; + } + + for (int i = start; i < getChildCount(); ) { + final View child = getChildAt(i); + if (needsPreDivider && isDivider(child)) { + needsPreDivider = false; + i++; + continue; + } + if (child == targetChild) break; + removeViewAt(i); + } + + return needsPreDivider; + } + + private static boolean isDivider(View v) { + return v != null && v.getId() == com.android.internal.R.id.action_menu_divider; } public boolean showOverflowMenu() { @@ -302,6 +365,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo ImageView result = new ImageView(mContext); result.setImageDrawable(mDivider); result.setScaleType(ImageView.ScaleType.FIT_XY); + result.setId(com.android.internal.R.id.action_menu_divider); return result; } diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index 1f93eacf459b..6c9e7bb02dd9 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -101,8 +101,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On } if (anchor != null) { - mTreeObserver = anchor.getViewTreeObserver(); - mTreeObserver.addOnGlobalLayoutListener(this); + if (mTreeObserver == null) { + mTreeObserver = anchor.getViewTreeObserver(); + mTreeObserver.addOnGlobalLayoutListener(this); + } mPopup.setAnchorView(anchor); } else { return false; @@ -123,10 +125,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On public void onDismiss() { mPopup = null; - if (mTreeObserver != null) { - mTreeObserver.removeGlobalOnLayoutListener(MenuPopupHelper.this); - mTreeObserver = null; + if (mTreeObserver != null && mTreeObserver.isAlive()) { + mTreeObserver.removeGlobalOnLayoutListener(this); } + mTreeObserver = null; } public boolean isShowing() { @@ -134,6 +136,8 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On } public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + if (!isShowing()) return; + MenuItem item = null; if (mOverflowOnly) { item = mMenu.getOverflowItem(position); @@ -184,13 +188,15 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On @Override public void onGlobalLayout() { if (!isShowing()) { - mTreeObserver.removeGlobalOnLayoutListener(this); + if (mTreeObserver.isAlive()) { + mTreeObserver.removeGlobalOnLayoutListener(this); + } mTreeObserver = null; } else { final View anchor = mAnchorView != null ? mAnchorView.get() : null; - if (anchor != null && !anchor.isShown()) { + if (anchor == null || !anchor.isShown()) { dismiss(); - } else { + } else if (isShowing()) { // Recompute window size and position mPopup.show(); } diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index e63a68f069d8..c9b0ec9e838d 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -28,6 +28,8 @@ import android.widget.FrameLayout; * @hide */ public class ActionBarContainer extends FrameLayout { + private boolean mIsTransitioning; + public ActionBarContainer(Context context) { this(context, null); } @@ -41,6 +43,25 @@ public class ActionBarContainer extends FrameLayout { a.recycle(); } + /** + * Set the action bar into a "transitioning" state. While transitioning + * the bar will block focus and touch from all of its descendants. This + * prevents the user from interacting with the bar while it is animating + * in or out. + * + * @param isTransitioning true if the bar is currently transitioning, false otherwise. + */ + public void setTransitioning(boolean isTransitioning) { + mIsTransitioning = isTransitioning; + setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS + : FOCUS_AFTER_DESCENDANTS); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return mIsTransitioning || super.onInterceptTouchEvent(ev); + } + @Override public boolean onTouchEvent(MotionEvent ev) { super.onTouchEvent(ev); diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 3c3f14b647ac..e3a66c592829 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -250,6 +250,8 @@ public class ActionBarView extends ViewGroup { } public void setMenu(Menu menu) { + if (menu == mOptionsMenu) return; + MenuBuilder builder = (MenuBuilder) menu; mOptionsMenu = builder; if (mMenuView != null) { diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 2621dd0bc401..0dc0422967af 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -30,7 +30,6 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.storage.IMountService; import android.provider.Settings; -import android.security.MessageDigest; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -40,6 +39,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; +import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Arrays; diff --git a/core/jni/Android.mk b/core/jni/Android.mk index c635b39f32c3..41baca2bffb8 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -75,7 +75,6 @@ LOCAL_SRC_FILES:= \ android_nfc_NdefRecord.cpp \ android_pim_EventRecurrence.cpp \ android_text_format_Time.cpp \ - android_security_Md5MessageDigest.cpp \ android_util_AssetManager.cpp \ android_util_Binder.cpp \ android_util_EventLog.cpp \ @@ -124,6 +123,8 @@ LOCAL_SRC_FILES:= \ android_media_ToneGenerator.cpp \ android_hardware_Camera.cpp \ android_hardware_SensorManager.cpp \ + android_hardware_UsbDevice.cpp \ + android_hardware_UsbRequest.cpp \ android_debug_JNITest.cpp \ android_util_FileObserver.cpp \ android/opengl/poly_clip.cpp.arm \ @@ -136,7 +137,6 @@ LOCAL_SRC_FILES:= \ android_server_BluetoothEventLoop.cpp \ android_server_BluetoothA2dpService.cpp \ android_server_Watchdog.cpp \ - android_message_digest_sha1.cpp \ android_ddm_DdmHandleNativeHeap.cpp \ com_android_internal_os_ZygoteInit.cpp \ com_android_internal_graphics_NativeUtils.cpp \ @@ -202,6 +202,7 @@ LOCAL_SHARED_LIBRARIES := \ libwpa_client \ libjpeg \ libnfc_ndef \ + libusbhost \ ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_SHARED_LIBRARIES += libhwui diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 342b8840ed51..878af3d81344 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -77,6 +77,8 @@ extern int register_android_opengl_jni_GLES20(JNIEnv* env); extern int register_android_hardware_Camera(JNIEnv *env); extern int register_android_hardware_SensorManager(JNIEnv *env); +extern int register_android_hardware_UsbDevice(JNIEnv *env); +extern int register_android_hardware_UsbRequest(JNIEnv *env); extern int register_android_media_AudioRecord(JNIEnv *env); extern int register_android_media_AudioSystem(JNIEnv *env); @@ -84,8 +86,6 @@ extern int register_android_media_AudioTrack(JNIEnv *env); extern int register_android_media_JetPlayer(JNIEnv *env); extern int register_android_media_ToneGenerator(JNIEnv *env); -extern int register_android_message_digest_sha1(JNIEnv *env); - extern int register_android_util_FloatMath(JNIEnv* env); namespace android { @@ -147,7 +147,6 @@ extern int register_android_net_LocalSocketImpl(JNIEnv* env); extern int register_android_net_NetworkUtils(JNIEnv* env); extern int register_android_net_TrafficStats(JNIEnv* env); extern int register_android_net_wifi_WifiManager(JNIEnv* env); -extern int register_android_security_Md5MessageDigest(JNIEnv *env); extern int register_android_text_AndroidCharacter(JNIEnv *env); extern int register_android_text_AndroidBidi(JNIEnv *env); extern int register_android_text_KeyCharacterMap(JNIEnv *env); @@ -1200,7 +1199,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), REG_JNI(register_android_emoji_EmojiFactory), - REG_JNI(register_android_security_Md5MessageDigest), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_AndroidBidi), REG_JNI(register_android_text_KeyCharacterMap), @@ -1275,6 +1273,8 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_com_android_internal_os_ZygoteInit), REG_JNI(register_android_hardware_Camera), REG_JNI(register_android_hardware_SensorManager), + REG_JNI(register_android_hardware_UsbDevice), + REG_JNI(register_android_hardware_UsbRequest), REG_JNI(register_android_media_AudioRecord), REG_JNI(register_android_media_AudioSystem), REG_JNI(register_android_media_AudioTrack), @@ -1289,7 +1289,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_server_BluetoothEventLoop), REG_JNI(register_android_server_BluetoothA2dpService), REG_JNI(register_android_server_Watchdog), - REG_JNI(register_android_message_digest_sha1), REG_JNI(register_android_ddm_DdmHandleNativeHeap), REG_JNI(register_android_backup_BackupDataInput), REG_JNI(register_android_backup_BackupDataOutput), diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 48ff5ed2769b..6fedde2e8139 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -502,6 +502,8 @@ void AndroidPixelRef::globalUnref() { /////////////////////////////////////////////////////////////////////////////// +extern "C" jbyte* jniGetNonMovableArrayElements(C_JNIEnv* env, jarray arrayObj); + jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) { Sk64 size64 = bitmap->getSize64(); @@ -514,8 +516,8 @@ jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, size_t size = size64.get32(); jbyteArray arrayObj = env->NewByteArray(size); if (arrayObj) { - jbyte *addr = env->GetByteArrayElements(arrayObj, NULL); - env->ReleaseByteArrayElements(arrayObj, addr, 0); + // TODO: make this work without jniGetNonMovableArrayElements + jbyte* addr = jniGetNonMovableArrayElements(&env->functions, arrayObj); if (addr) { SkPixelRef* pr = new AndroidPixelRef(env, (void*) addr, size, arrayObj, ctable); bitmap->setPixelRef(pr)->unref(); diff --git a/core/jni/android_hardware_UsbDevice.cpp b/core/jni/android_hardware_UsbDevice.cpp new file mode 100644 index 000000000000..90144509ffb9 --- /dev/null +++ b/core/jni/android_hardware_UsbDevice.cpp @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "UsbDeviceJNI" + +#include "utils/Log.h" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <usbhost/usbhost.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +using namespace android; + +static jfieldID field_context; + +struct usb_device* get_device_from_object(JNIEnv* env, jobject javaDevice) +{ + return (struct usb_device*)env->GetIntField(javaDevice, field_context); +} + +// in android_hardware_UsbEndpoint.cpp +extern struct usb_endpoint* get_endpoint_from_object(JNIEnv* env, jobject javaEndpoint); + +static jboolean +android_hardware_UsbDevice_open(JNIEnv *env, jobject thiz, jstring deviceName, + jobject fileDescriptor) +{ + int fd = getParcelFileDescriptorFD(env, fileDescriptor); + // duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy + fd = dup(fd); + if (fd < 0) + return false; + + const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL); + struct usb_device* device = usb_device_new(deviceNameStr, fd); + if (device) { + env->SetIntField(thiz, field_context, (int)device); + } else { + LOGE("usb_device_open failed for %s", deviceNameStr); + close(fd); + } + + env->ReleaseStringUTFChars(deviceName, deviceNameStr); + return (device != NULL); +} + +static void +android_hardware_UsbDevice_close(JNIEnv *env, jobject thiz) +{ + LOGD("close\n"); + struct usb_device* device = get_device_from_object(env, thiz); + if (device) { + usb_device_close(device); + env->SetIntField(thiz, field_context, 0); + } +} + +static jint +android_hardware_UsbDevice_get_fd(JNIEnv *env, jobject thiz) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + LOGE("device is closed in native_get_fd"); + return -1; + } + return usb_device_get_fd(device); +} + +static jboolean +android_hardware_UsbDevice_claim_interface(JNIEnv *env, jobject thiz, int interfaceID, jboolean force) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + LOGE("device is closed in native_claim_interface"); + return -1; + } + + int ret = usb_device_claim_interface(device, interfaceID); + if (ret && force && errno == EBUSY) { + // disconnect kernel driver and try again + usb_device_connect_kernel_driver(device, interfaceID, false); + ret = usb_device_claim_interface(device, interfaceID); + } + return ret == 0; +} + +static jint +android_hardware_UsbDevice_release_interface(JNIEnv *env, jobject thiz, int interfaceID) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + LOGE("device is closed in native_release_interface"); + return -1; + } + int ret = usb_device_release_interface(device, interfaceID); + if (ret == 0) { + // allow kernel to reconnect its driver + usb_device_connect_kernel_driver(device, interfaceID, true); + } + return ret; +} + +static jint +android_hardware_UsbDevice_control_request(JNIEnv *env, jobject thiz, + jint requestType, jint request, jint value, jint index, + jbyteArray buffer, jint length, jint timeout) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + LOGE("device is closed in native_control_request"); + return -1; + } + + jbyte* bufferBytes = NULL; + if (buffer) { + if (env->GetArrayLength(buffer) < length) { + env->ThrowNew(env->FindClass("java/lang/ArrayIndexOutOfBoundsException"), NULL); + return -1; + } + bufferBytes = env->GetByteArrayElements(buffer, 0); + } + + jint result = usb_device_control_transfer(device, requestType, request, + value, index, bufferBytes, length, timeout); + + if (bufferBytes) + env->ReleaseByteArrayElements(buffer, bufferBytes, 0); + + return result; +} + +static jint +android_hardware_UsbDevice_bulk_request(JNIEnv *env, jobject thiz, + jint endpoint, jbyteArray buffer, jint length, jint timeout) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + LOGE("device is closed in native_control_request"); + return -1; + } + + jbyte* bufferBytes = NULL; + if (buffer) { + if (env->GetArrayLength(buffer) < length) { + env->ThrowNew(env->FindClass("java/lang/ArrayIndexOutOfBoundsException"), NULL); + return -1; + } + bufferBytes = env->GetByteArrayElements(buffer, 0); + } + + jint result = usb_device_bulk_transfer(device, endpoint, bufferBytes, length, timeout); + + if (bufferBytes) + env->ReleaseByteArrayElements(buffer, bufferBytes, 0); + + return result; +} + +static jobject +android_hardware_UsbDevice_request_wait(JNIEnv *env, jobject thiz) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + LOGE("device is closed in native_request_wait"); + return NULL; + } + + struct usb_request* request = usb_request_wait(device); + if (request) + return (jobject)request->client_data; + else + return NULL; +} + +static jstring +android_hardware_UsbDevice_get_serial(JNIEnv *env, jobject thiz) +{ + struct usb_device* device = get_device_from_object(env, thiz); + if (!device) { + LOGE("device is closed in native_request_wait"); + return NULL; + } + char* serial = usb_device_get_serial(device); + if (!serial) + return NULL; + jstring result = env->NewStringUTF(serial); + free(serial); + return result; +} + +static jint +android_hardware_UsbDevice_get_device_id(JNIEnv *env, jobject clazz, jstring name) +{ + const char *nameStr = env->GetStringUTFChars(name, NULL); + int id = usb_device_get_unique_id_from_name(nameStr); + env->ReleaseStringUTFChars(name, nameStr); + return id; +} + +static jstring +android_hardware_UsbDevice_get_device_name(JNIEnv *env, jobject clazz, jint id) +{ + char* name = usb_device_get_name_from_unique_id(id); + jstring result = env->NewStringUTF(name); + free(name); + return result; +} + +static JNINativeMethod method_table[] = { + {"native_open", "(Ljava/lang/String;Ljava/io/FileDescriptor;)Z", + (void *)android_hardware_UsbDevice_open}, + {"native_close", "()V", (void *)android_hardware_UsbDevice_close}, + {"native_get_fd", "()I", (void *)android_hardware_UsbDevice_get_fd}, + {"native_claim_interface", "(IZ)Z",(void *)android_hardware_UsbDevice_claim_interface}, + {"native_release_interface","(I)Z", (void *)android_hardware_UsbDevice_release_interface}, + {"native_control_request", "(IIII[BII)I", + (void *)android_hardware_UsbDevice_control_request}, + {"native_bulk_request", "(I[BII)I", + (void *)android_hardware_UsbDevice_bulk_request}, + {"native_request_wait", "()Landroid/hardware/UsbRequest;", + (void *)android_hardware_UsbDevice_request_wait}, + { "native_get_serial", "()Ljava/lang/String;", + (void*)android_hardware_UsbDevice_get_serial }, + + // static methods + { "native_get_device_id", "(Ljava/lang/String;)I", + (void*)android_hardware_UsbDevice_get_device_id }, + { "native_get_device_name", "(I)Ljava/lang/String;", + (void*)android_hardware_UsbDevice_get_device_name }, +}; + +int register_android_hardware_UsbDevice(JNIEnv *env) +{ + jclass clazz = env->FindClass("android/hardware/UsbDevice"); + if (clazz == NULL) { + LOGE("Can't find android/hardware/UsbDevice"); + return -1; + } + field_context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (field_context == NULL) { + LOGE("Can't find UsbDevice.mNativeContext"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, "android/hardware/UsbDevice", + method_table, NELEM(method_table)); +} + diff --git a/core/jni/android_hardware_UsbEndpoint.cpp b/core/jni/android_hardware_UsbEndpoint.cpp new file mode 100644 index 000000000000..00c82351ac12 --- /dev/null +++ b/core/jni/android_hardware_UsbEndpoint.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "UsbEndpoint" + +#include "utils/Log.h" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <usbhost/usbhost.h> + +#include <stdio.h> + +using namespace android; + +static jfieldID field_context; +static jfieldID field_address; +static jfieldID field_attributes; +static jfieldID field_max_packet_size; +static jfieldID field_interval; + +struct usb_endpoint* get_endpoint_from_object(JNIEnv* env, jobject javaEndpoint) +{ + return (struct usb_endpoint*)env->GetIntField(javaEndpoint, field_context); +} + +// in android_hardware_UsbDevice.cpp +extern struct usb_device* get_device_from_object(JNIEnv* env, jobject javaDevice); + +static jboolean +android_hardware_UsbEndpoint_init(JNIEnv *env, jobject thiz, jobject javaDevice) +{ + LOGD("open\n"); + + struct usb_device* device = get_device_from_object(env, javaDevice); + if (!device) { + LOGE("device null in native_init"); + return false; + } + + // construct an endpoint descriptor from the Java object fields + struct usb_endpoint_descriptor desc; + desc.bLength = USB_DT_ENDPOINT_SIZE; + desc.bDescriptorType = USB_DT_ENDPOINT; + desc.bEndpointAddress = env->GetIntField(thiz, field_address); + desc.bmAttributes = env->GetIntField(thiz, field_attributes); + desc.wMaxPacketSize = env->GetIntField(thiz, field_max_packet_size); + desc.bInterval = env->GetIntField(thiz, field_interval); + + struct usb_endpoint* endpoint = usb_endpoint_init(device, &desc); + if (endpoint) + env->SetIntField(thiz, field_context, (int)device); + return (endpoint != NULL); +} + +static void +android_hardware_UsbEndpoint_close(JNIEnv *env, jobject thiz) +{ + LOGD("close\n"); + struct usb_endpoint* endpoint = get_endpoint_from_object(env, thiz); + if (endpoint) { + usb_endpoint_close(endpoint); + env->SetIntField(thiz, field_context, 0); + } +} + +static JNINativeMethod method_table[] = { + {"native_init", "(Landroid/hardware/UsbDevice;)Z", + (void *)android_hardware_UsbEndpoint_init}, + {"native_close", "()V", (void *)android_hardware_UsbEndpoint_close}, +}; + +int register_android_hardware_UsbEndpoint(JNIEnv *env) +{ + jclass clazz = env->FindClass("android/hardware/UsbEndpoint"); + if (clazz == NULL) { + LOGE("Can't find android/hardware/UsbEndpoint"); + return -1; + } + field_context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (field_context == NULL) { + LOGE("Can't find UsbEndpoint.mNativeContext"); + return -1; + } + field_address = env->GetFieldID(clazz, "mAddress", "I"); + if (field_address == NULL) { + LOGE("Can't find UsbEndpoint.mAddress"); + return -1; + } + field_attributes = env->GetFieldID(clazz, "mAttributes", "I"); + if (field_attributes == NULL) { + LOGE("Can't find UsbEndpoint.mAttributes"); + return -1; + } + field_max_packet_size = env->GetFieldID(clazz, "mMaxPacketSize", "I"); + if (field_max_packet_size == NULL) { + LOGE("Can't find UsbEndpoint.mMaxPacketSize"); + return -1; + } + field_interval = env->GetFieldID(clazz, "mInterval", "I"); + if (field_interval == NULL) { + LOGE("Can't find UsbEndpoint.mInterval"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, "android/hardware/UsbEndpoint", + method_table, NELEM(method_table)); +} + diff --git a/core/jni/android_hardware_UsbRequest.cpp b/core/jni/android_hardware_UsbRequest.cpp new file mode 100644 index 000000000000..710afae64353 --- /dev/null +++ b/core/jni/android_hardware_UsbRequest.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "UsbRequestJNI" + +#include "utils/Log.h" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <usbhost/usbhost.h> + +#include <stdio.h> + +using namespace android; + +static jfieldID field_context; + +struct usb_request* get_request_from_object(JNIEnv* env, jobject java_request) +{ + return (struct usb_request*)env->GetIntField(java_request, field_context); +} + +// in android_hardware_UsbDevice.cpp +extern struct usb_device* get_device_from_object(JNIEnv* env, jobject java_device); + +static jboolean +android_hardware_UsbRequest_init(JNIEnv *env, jobject thiz, jobject java_device, + jint ep_address, jint ep_attributes, jint ep_max_packet_size, jint ep_interval) +{ + LOGD("init\n"); + + struct usb_device* device = get_device_from_object(env, java_device); + if (!device) { + LOGE("device null in native_init"); + return false; + } + + // construct an endpoint descriptor from the Java object fields + struct usb_endpoint_descriptor desc; + desc.bLength = USB_DT_ENDPOINT_SIZE; + desc.bDescriptorType = USB_DT_ENDPOINT; + desc.bEndpointAddress = ep_address; + desc.bmAttributes = ep_attributes; + desc.wMaxPacketSize = ep_max_packet_size; + desc.bInterval = ep_interval; + + struct usb_request* request = usb_request_new(device, &desc); + if (request) + env->SetIntField(thiz, field_context, (int)request); + return (request != NULL); +} + +static void +android_hardware_UsbRequest_close(JNIEnv *env, jobject thiz) +{ + LOGD("close\n"); + struct usb_request* request = get_request_from_object(env, thiz); + if (request) { + usb_request_free(request); + env->SetIntField(thiz, field_context, 0); + } +} + +static jboolean +android_hardware_UsbRequest_queue_array(JNIEnv *env, jobject thiz, + jbyteArray buffer, jint length, jboolean out) +{ + struct usb_request* request = get_request_from_object(env, thiz); + if (!request) { + LOGE("request is closed in native_queue"); + return false; + } + + if (buffer && length) { + request->buffer = malloc(length); + if (!request->buffer) + return false; + if (out) { + // copy data from Java buffer to native buffer + env->GetByteArrayRegion(buffer, 0, length, (jbyte *)request->buffer); + } + } else { + request->buffer = NULL; + } + request->buffer_length = length; + + if (usb_request_queue(request)) { + if (request->buffer) { + // free our buffer if usb_request_queue fails + free(request->buffer); + request->buffer = NULL; + } + return false; + } else { + // save a reference to ourselves so UsbDevice.waitRequest() can find us + request->client_data = (void *)env->NewGlobalRef(thiz); + return true; + } +} + +static void +android_hardware_UsbRequest_dequeue_array(JNIEnv *env, jobject thiz, + jbyteArray buffer, jint length, jboolean out) +{ + struct usb_request* request = get_request_from_object(env, thiz); + if (!request) { + LOGE("request is closed in native_dequeue"); + return; + } + + if (buffer && length && request->buffer && !out) { + // copy data from native buffer to Java buffer + env->SetByteArrayRegion(buffer, 0, length, (jbyte *)request->buffer); + } + free(request->buffer); + env->DeleteGlobalRef((jobject)request->client_data); + +} + +static jboolean +android_hardware_UsbRequest_queue_direct(JNIEnv *env, jobject thiz, + jobject buffer, jint length, jboolean out) +{ + struct usb_request* request = get_request_from_object(env, thiz); + if (!request) { + LOGE("request is closed in native_queue"); + return false; + } + + if (buffer && length) { + request->buffer = env->GetDirectBufferAddress(buffer); + if (!request->buffer) + return false; + } else { + request->buffer = NULL; + } + request->buffer_length = length; + + if (usb_request_queue(request)) { + request->buffer = NULL; + return false; + } else { + // save a reference to ourselves so UsbDevice.waitRequest() can find us + // we also need this to make sure our native buffer is not deallocated + // while IO is active + request->client_data = (void *)env->NewGlobalRef(thiz); + return true; + } +} + +static void +android_hardware_UsbRequest_dequeue_direct(JNIEnv *env, jobject thiz) +{ + struct usb_request* request = get_request_from_object(env, thiz); + if (!request) { + LOGE("request is closed in native_dequeue"); + return; + } + // all we need to do is delete our global ref + env->DeleteGlobalRef((jobject)request->client_data); +} + +static jboolean +android_hardware_UsbRequest_cancel(JNIEnv *env, jobject thiz) +{ + struct usb_request* request = get_request_from_object(env, thiz); + if (!request) { + LOGE("request is closed in native_cancel"); + return false; + } + return (usb_request_cancel(request) == 0); +} + +static JNINativeMethod method_table[] = { + {"native_init", "(Landroid/hardware/UsbDevice;IIII)Z", + (void *)android_hardware_UsbRequest_init}, + {"native_close", "()V", (void *)android_hardware_UsbRequest_close}, + {"native_queue_array", "([BIZ)Z", (void *)android_hardware_UsbRequest_queue_array}, + {"native_dequeue_array", "([BIZ)V", (void *)android_hardware_UsbRequest_dequeue_array}, + {"native_queue_direct", "(Ljava/nio/ByteBuffer;IZ)Z", + (void *)android_hardware_UsbRequest_queue_direct}, + {"native_dequeue_direct", "()V", (void *)android_hardware_UsbRequest_dequeue_direct}, + {"native_cancel", "()Z", (void *)android_hardware_UsbRequest_cancel}, +}; + +int register_android_hardware_UsbRequest(JNIEnv *env) +{ + jclass clazz = env->FindClass("android/hardware/UsbRequest"); + if (clazz == NULL) { + LOGE("Can't find android/hardware/UsbRequest"); + return -1; + } + field_context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (field_context == NULL) { + LOGE("Can't find UsbRequest.mNativeContext"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, "android/hardware/UsbRequest", + method_table, NELEM(method_table)); +} + diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 5147cfabc66b..5f3fed216744 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -192,6 +192,12 @@ android_media_AudioSystem_getStreamVolumeIndex(JNIEnv *env, jobject thiz, jint s return index; } +static jint +android_media_AudioSystem_getDevicesForStream(JNIEnv *env, jobject thiz, jint stream) +{ + return (jint) AudioSystem::getDevicesForStream(static_cast <AudioSystem::stream_type>(stream)); +} + // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { @@ -208,7 +214,8 @@ static JNINativeMethod gMethods[] = { {"getForceUse", "(I)I", (void *)android_media_AudioSystem_getForceUse}, {"initStreamVolume", "(III)I", (void *)android_media_AudioSystem_initStreamVolume}, {"setStreamVolumeIndex","(II)I", (void *)android_media_AudioSystem_setStreamVolumeIndex}, - {"getStreamVolumeIndex","(I)I", (void *)android_media_AudioSystem_getStreamVolumeIndex} + {"getStreamVolumeIndex","(I)I", (void *)android_media_AudioSystem_getStreamVolumeIndex}, + {"getDevicesForStream", "(I)I", (void *)android_media_AudioSystem_getDevicesForStream}, }; const char* const kClassPathName = "android/media/AudioSystem"; diff --git a/core/jni/android_message_digest_sha1.cpp b/core/jni/android_message_digest_sha1.cpp deleted file mode 100644 index 480bbf8a7457..000000000000 --- a/core/jni/android_message_digest_sha1.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* //device/libs/android_runtime/android_message_digest_sha1.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT 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 "jni.h" -#include <JNIHelp.h> -#include "android_runtime/AndroidRuntime.h" - -#include <openssl/sha.h> - -//#define _DEBUG 1 - -// ---------------------------------------------------------------------------- - -using namespace android; - -// ---------------------------------------------------------------------------- - -struct fields_t { - jfieldID context; -}; -static fields_t fields; - -static void native_init(JNIEnv *env, jobject clazz) -{ - SHA_CTX* context; - -#ifdef _DEBUG - printf("sha1.native_init\n"); -#endif - - context = (SHA_CTX *)malloc(sizeof(SHA_CTX)); - SHA1_Init(context); - - env->SetIntField(clazz, fields.context, (int)context); -} - -static void native_reset(JNIEnv *env, jobject clazz) -{ - SHA_CTX *context = (SHA_CTX *)env->GetIntField(clazz, fields.context); - if (context != NULL) { -#ifdef _DEBUG - printf("sha1.native_reset: free context\n"); -#endif - free(context); - env->SetIntField(clazz, fields.context, 0 ); - } -} - - -static void native_update(JNIEnv *env, jobject clazz, jbyteArray dataArray) -{ -#ifdef _DEBUG - printf("sha1.native_update\n"); -#endif - jbyte * data; - jsize dataSize; - SHA_CTX *context = (SHA_CTX *)env->GetIntField(clazz, fields.context); - - if (context == NULL) { -#ifdef _DEBUG - printf("sha1.native_update: context is NULL, call init...\n"); -#endif - native_init(env, clazz); - context = (SHA_CTX *)env->GetIntField(clazz, fields.context); - } - - data = env->GetByteArrayElements(dataArray, NULL); - if (data == NULL) { - LOGE("Unable to get byte array elements"); - jniThrowException(env, "java/lang/IllegalArgumentException", - "Invalid data array when calling MessageDigest.update()"); - return; - } - dataSize = env->GetArrayLength(dataArray); - - SHA1_Update(context, data, dataSize); - - env->ReleaseByteArrayElements(dataArray, data, 0); -} - -static jbyteArray native_digest(JNIEnv *env, jobject clazz) -{ -#ifdef _DEBUG - printf("sha1.native_digest\n"); -#endif - jbyteArray array; - jbyte md[SHA_DIGEST_LENGTH]; - SHA_CTX *context = (SHA_CTX *)env->GetIntField(clazz, fields.context); - - SHA1_Final((uint8_t*)md, context); - - array = env->NewByteArray(SHA_DIGEST_LENGTH); - LOG_ASSERT(array, "Native could not create new byte[]"); - - env->SetByteArrayRegion(array, 0, SHA_DIGEST_LENGTH, md); - - native_reset(env, clazz); - - return array; -} - - -static JNINativeMethod method_table[] = -{ - /* name, signature, funcPtr */ - {"init", "()V", (void *)native_init}, - {"update", "([B)V", (void *)native_update}, - {"digest", "()[B", (void *)native_digest}, - {"reset", "()V", (void *)native_reset}, -}; - -int register_android_message_digest_sha1(JNIEnv *env) -{ - jclass clazz; - - clazz = env->FindClass("android/security/Sha1MessageDigest"); - if (clazz == NULL) { - LOGE("Can't find android/security/Sha1MessageDigest"); - return -1; - } - - fields.context = env->GetFieldID(clazz, "mNativeSha1Context", "I"); - if (fields.context == NULL) { - LOGE("Can't find Sha1MessageDigest.mNativeSha1Context"); - return -1; - } - - return AndroidRuntime::registerNativeMethods( - env, "android/security/Sha1MessageDigest", - method_table, NELEM(method_table)); -} - diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 351f264c1a6d..3adf7705cca9 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -21,6 +21,7 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/Log.h> #include <arpa/inet.h> +#include <cutils/properties.h> extern "C" { int ifc_enable(const char *ifname); @@ -30,15 +31,14 @@ int ifc_remove_host_routes(const char *ifname); int ifc_get_default_route(const char *ifname); int ifc_remove_default_route(const char *ifname); int ifc_reset_connections(const char *ifname); -int ifc_configure(const char *ifname, in_addr_t ipaddr, in_addr_t netmask, in_addr_t gateway, in_addr_t dns1, in_addr_t dns2); int dhcp_do_request(const char *ifname, - in_addr_t *ipaddr, - in_addr_t *gateway, - in_addr_t *mask, - in_addr_t *dns1, - in_addr_t *dns2, - in_addr_t *server, + const char *ipaddr, + const char *gateway, + uint32_t *prefixLength, + const char *dns1, + const char *dns2, + const char *server, uint32_t *lease); int dhcp_stop(const char *ifname); int dhcp_release_lease(const char *ifname); @@ -55,16 +55,16 @@ namespace android { * to look them up every time. */ static struct fieldIds { - jclass dhcpInfoClass; + jclass dhcpInfoInternalClass; jmethodID constructorId; jfieldID ipaddress; jfieldID gateway; - jfieldID netmask; + jfieldID prefixLength; jfieldID dns1; jfieldID dns2; jfieldID serverAddress; jfieldID leaseDuration; -} dhcpInfoFieldIds; +} dhcpInfoInternalFieldIds; static jint android_net_utils_enableInterface(JNIEnv* env, jobject clazz, jstring ifname) { @@ -149,21 +149,29 @@ static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, jstri static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info) { int result; - in_addr_t ipaddr, gateway, mask, dns1, dns2, server; + char ipaddr[PROPERTY_VALUE_MAX]; + uint32_t prefixLength; + char gateway[PROPERTY_VALUE_MAX]; + char dns1[PROPERTY_VALUE_MAX]; + char dns2[PROPERTY_VALUE_MAX]; + char server[PROPERTY_VALUE_MAX]; uint32_t lease; const char *nameStr = env->GetStringUTFChars(ifname, NULL); - result = ::dhcp_do_request(nameStr, &ipaddr, &gateway, &mask, - &dns1, &dns2, &server, &lease); + if (nameStr == NULL) return (jboolean)false; + + result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength, + dns1, dns2, server, &lease); env->ReleaseStringUTFChars(ifname, nameStr); - if (result == 0 && dhcpInfoFieldIds.dhcpInfoClass != NULL) { - env->SetIntField(info, dhcpInfoFieldIds.ipaddress, ipaddr); - env->SetIntField(info, dhcpInfoFieldIds.gateway, gateway); - env->SetIntField(info, dhcpInfoFieldIds.netmask, mask); - env->SetIntField(info, dhcpInfoFieldIds.dns1, dns1); - env->SetIntField(info, dhcpInfoFieldIds.dns2, dns2); - env->SetIntField(info, dhcpInfoFieldIds.serverAddress, server); - env->SetIntField(info, dhcpInfoFieldIds.leaseDuration, lease); + if (result == 0 && dhcpInfoInternalFieldIds.dhcpInfoInternalClass != NULL) { + env->SetObjectField(info, dhcpInfoInternalFieldIds.ipaddress, env->NewStringUTF(ipaddr)); + env->SetObjectField(info, dhcpInfoInternalFieldIds.gateway, env->NewStringUTF(gateway)); + env->SetIntField(info, dhcpInfoInternalFieldIds.prefixLength, prefixLength); + env->SetObjectField(info, dhcpInfoInternalFieldIds.dns1, env->NewStringUTF(dns1)); + env->SetObjectField(info, dhcpInfoInternalFieldIds.dns2, env->NewStringUTF(dns2)); + env->SetObjectField(info, dhcpInfoInternalFieldIds.serverAddress, + env->NewStringUTF(server)); + env->SetIntField(info, dhcpInfoInternalFieldIds.leaseDuration, lease); } return (jboolean)(result == 0); } @@ -193,24 +201,6 @@ static jstring android_net_utils_getDhcpError(JNIEnv* env, jobject clazz) return env->NewStringUTF(::dhcp_get_errmsg()); } -static jboolean android_net_utils_configureInterface(JNIEnv* env, - jobject clazz, - jstring ifname, - jint ipaddr, - jint mask, - jint gateway, - jint dns1, - jint dns2) -{ - int result; - uint32_t lease; - - const char *nameStr = env->GetStringUTFChars(ifname, NULL); - result = ::ifc_configure(nameStr, ipaddr, mask, gateway, dns1, dns2); - env->ReleaseStringUTFChars(ifname, nameStr); - return (jboolean)(result == 0); -} - // ---------------------------------------------------------------------------- /* @@ -228,10 +218,9 @@ static JNINativeMethod gNetworkUtilMethods[] = { (void *)android_net_utils_getDefaultRoute }, { "removeDefaultRoute", "(Ljava/lang/String;)I", (void *)android_net_utils_removeDefaultRoute }, { "resetConnections", "(Ljava/lang/String;)I", (void *)android_net_utils_resetConnections }, - { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z", (void *)android_net_utils_runDhcp }, + { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z", (void *)android_net_utils_runDhcp }, { "stopDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_stopDhcp }, { "releaseDhcpLease", "(Ljava/lang/String;)Z", (void *)android_net_utils_releaseDhcpLease }, - { "configureNative", "(Ljava/lang/String;IIIII)Z", (void *)android_net_utils_configureInterface }, { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError }, }; @@ -240,16 +229,16 @@ int register_android_net_NetworkUtils(JNIEnv* env) jclass netutils = env->FindClass(NETUTILS_PKG_NAME); LOG_FATAL_IF(netutils == NULL, "Unable to find class " NETUTILS_PKG_NAME); - dhcpInfoFieldIds.dhcpInfoClass = env->FindClass("android/net/DhcpInfo"); - if (dhcpInfoFieldIds.dhcpInfoClass != NULL) { - dhcpInfoFieldIds.constructorId = env->GetMethodID(dhcpInfoFieldIds.dhcpInfoClass, "<init>", "()V"); - dhcpInfoFieldIds.ipaddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "ipAddress", "I"); - dhcpInfoFieldIds.gateway = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "gateway", "I"); - dhcpInfoFieldIds.netmask = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "netmask", "I"); - dhcpInfoFieldIds.dns1 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns1", "I"); - dhcpInfoFieldIds.dns2 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns2", "I"); - dhcpInfoFieldIds.serverAddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "serverAddress", "I"); - dhcpInfoFieldIds.leaseDuration = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "leaseDuration", "I"); + dhcpInfoInternalFieldIds.dhcpInfoInternalClass = env->FindClass("android/net/DhcpInfoInternal"); + if (dhcpInfoInternalFieldIds.dhcpInfoInternalClass != NULL) { + dhcpInfoInternalFieldIds.constructorId = env->GetMethodID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "<init>", "()V"); + dhcpInfoInternalFieldIds.ipaddress = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "ipAddress", "Ljava/lang/String;"); + dhcpInfoInternalFieldIds.gateway = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "gateway", "Ljava/lang/String;"); + dhcpInfoInternalFieldIds.prefixLength = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "prefixLength", "I"); + dhcpInfoInternalFieldIds.dns1 = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "dns1", "Ljava/lang/String;"); + dhcpInfoInternalFieldIds.dns2 = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "dns2", "Ljava/lang/String;"); + dhcpInfoInternalFieldIds.serverAddress = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "serverAddress", "Ljava/lang/String;"); + dhcpInfoInternalFieldIds.leaseDuration = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "leaseDuration", "I"); } return AndroidRuntime::registerNativeMethods(env, diff --git a/core/jni/android_net_TrafficStats.cpp b/core/jni/android_net_TrafficStats.cpp index ff46bdd2ba39..0c84f1138e7c 100644 --- a/core/jni/android_net_TrafficStats.cpp +++ b/core/jni/android_net_TrafficStats.cpp @@ -30,6 +30,17 @@ namespace android { +enum Tx_Rx { + TX, + RX +}; + +enum Tcp_Udp { + TCP, + UDP, + TCP_AND_UDP +}; + // Returns an ASCII decimal number read from the specified file, -1 on error. static jlong readNumber(char const* filename) { #ifdef HAVE_ANDROID_OS @@ -119,6 +130,33 @@ static jlong getMobileRxBytes(JNIEnv* env, jobject clazz) { "/sys/class/net/ppp0/statistics/rx_bytes"); } +static jlong getData(JNIEnv* env, char *what, jstring interface) { + char filename[80]; + jboolean isCopy; + + const char *interfaceStr = env->GetStringUTFChars(interface, &isCopy); + snprintf(filename, sizeof(filename), "/sys/class/net/%s/statistics/%s", interfaceStr, what); + + return readNumber(filename); +} + +static jlong getTxPackets(JNIEnv* env, jobject clazz, jstring interface) { + return getData(env, "tx_packets", interface); +} + +static jlong getRxPackets(JNIEnv* env, jobject clazz, jstring interface) { + return getData(env, "rx_packets", interface); +} + +static jlong getTxBytes(JNIEnv* env, jobject clazz, jstring interface) { + return getData(env, "tx_bytes", interface); +} + +static jlong getRxBytes(JNIEnv* env, jobject clazz, jstring interface) { + return getData(env, "rx_bytes", interface); +} + + // Total stats are read less often, so we're willing to put up // with listing the directory and concatenating filenames. @@ -140,16 +178,136 @@ static jlong getTotalRxBytes(JNIEnv* env, jobject clazz) { // Per-UID stats require reading from a constructed filename. +static jlong getUidBytes(JNIEnv* env, jobject clazz, jint uid, + enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) { + char tcp_filename[80], udp_filename[80]; + jlong tcp_bytes = -1, udp_bytes = -1, total_bytes = -1; + + switch (tx_or_rx) { + case TX: + sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd", uid); + sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd", uid); + break; + case RX: + sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv", uid); + sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv", uid); + break; + default: + return -1; + } + + switch (tcp_or_udp) { + case TCP: + tcp_bytes = readNumber(tcp_filename); + total_bytes = (tcp_bytes >= 0) ? tcp_bytes : -1; + break; + case UDP: + udp_bytes = readNumber(udp_filename); + total_bytes = (udp_bytes >= 0) ? udp_bytes : -1; + break; + case TCP_AND_UDP: + tcp_bytes = readNumber(tcp_filename); + total_bytes += (tcp_bytes >= 0 ? tcp_bytes : 0); + + udp_bytes = readNumber(udp_filename); + total_bytes += (udp_bytes >= 0 ? udp_bytes : 0); + break; + default: + return -1; + } + + return total_bytes; +} + +static jlong getUidPkts(JNIEnv* env, jobject clazz, jint uid, + enum Tx_Rx tx_or_rx, enum Tcp_Udp tcp_or_udp) { + char tcp_filename[80], udp_filename[80]; + jlong tcp_pkts = -1, udp_pkts = -1, total_pkts = -1; + + switch (tx_or_rx) { + case TX: + sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_snd_pkt", uid); + sprintf(udp_filename, "/proc/uid_stat/%d/udp_snd_pkt", uid); + break; + case RX: + sprintf(tcp_filename, "/proc/uid_stat/%d/tcp_rcv_pkt", uid); + sprintf(udp_filename, "/proc/uid_stat/%d/udp_rcv_pkt", uid); + break; + default: + return -1; + } + + switch (tcp_or_udp) { + case TCP: + tcp_pkts = readNumber(tcp_filename); + total_pkts = (tcp_pkts >= 0) ? tcp_pkts : -1; + break; + case UDP: + udp_pkts = readNumber(udp_filename); + total_pkts = (udp_pkts >= 0) ? udp_pkts : -1; + break; + case TCP_AND_UDP: + tcp_pkts = readNumber(tcp_filename); + total_pkts += (tcp_pkts >= 0 ? tcp_pkts : 0); + + udp_pkts = readNumber(udp_filename); + total_pkts += (udp_pkts >= 0 ? udp_pkts : 0); + break; + default: + return -1; + } + + return total_pkts; +} + static jlong getUidRxBytes(JNIEnv* env, jobject clazz, jint uid) { - char filename[80]; - sprintf(filename, "/proc/uid_stat/%d/tcp_rcv", uid); - return readNumber(filename); + return getUidBytes(env, clazz, uid, RX, TCP_AND_UDP); } static jlong getUidTxBytes(JNIEnv* env, jobject clazz, jint uid) { - char filename[80]; - sprintf(filename, "/proc/uid_stat/%d/tcp_snd", uid); - return readNumber(filename); + return getUidBytes(env, clazz, uid, TX, TCP_AND_UDP); +} + +/* TCP Segments + UDP Packets */ +static jlong getUidTxPackets(JNIEnv* env, jobject clazz, jint uid) { + return getUidPkts(env, clazz, uid, TX, TCP_AND_UDP); +} + +/* TCP Segments + UDP Packets */ +static jlong getUidRxPackets(JNIEnv* env, jobject clazz, jint uid) { + return getUidPkts(env, clazz, uid, RX, TCP_AND_UDP); +} + +static jlong getUidTcpTxBytes(JNIEnv* env, jobject clazz, jint uid) { + return getUidBytes(env, clazz, uid, TX, TCP); +} + +static jlong getUidTcpRxBytes(JNIEnv* env, jobject clazz, jint uid) { + return getUidBytes(env, clazz, uid, RX, TCP); +} + +static jlong getUidUdpTxBytes(JNIEnv* env, jobject clazz, jint uid) { + return getUidBytes(env, clazz, uid, TX, UDP); +} + +static jlong getUidUdpRxBytes(JNIEnv* env, jobject clazz, jint uid) { + return getUidBytes(env, clazz, uid, RX, UDP); +} + +static jlong getUidTcpTxSegments(JNIEnv* env, jobject clazz, jint uid) { + return getUidPkts(env, clazz, uid, TX, TCP); +} + +static jlong getUidTcpRxSegments(JNIEnv* env, jobject clazz, jint uid) { + return getUidPkts(env, clazz, uid, RX, TCP); +} + +static jlong getUidUdpTxPackets(JNIEnv* env, jobject clazz, jint uid) { + return getUidPkts(env, clazz, uid, TX, UDP); +} + +static jlong getUidUdpRxPackets(JNIEnv* env, jobject clazz, jint uid) { + return getUidPkts(env, clazz, uid, RX, UDP); } static JNINativeMethod gMethods[] = { @@ -157,12 +315,30 @@ static JNINativeMethod gMethods[] = { {"getMobileRxPackets", "()J", (void*) getMobileRxPackets}, {"getMobileTxBytes", "()J", (void*) getMobileTxBytes}, {"getMobileRxBytes", "()J", (void*) getMobileRxBytes}, + {"getTxPackets", "(Ljava/lang/String;)J", (void*) getTxPackets}, + {"getRxPackets", "(Ljava/lang/String;)J", (void*) getRxPackets}, + {"getTxBytes", "(Ljava/lang/String;)J", (void*) getTxBytes}, + {"getRxBytes", "(Ljava/lang/String;)J", (void*) getRxBytes}, {"getTotalTxPackets", "()J", (void*) getTotalTxPackets}, {"getTotalRxPackets", "()J", (void*) getTotalRxPackets}, {"getTotalTxBytes", "()J", (void*) getTotalTxBytes}, {"getTotalRxBytes", "()J", (void*) getTotalRxBytes}, + + /* Per-UID Stats */ {"getUidTxBytes", "(I)J", (void*) getUidTxBytes}, {"getUidRxBytes", "(I)J", (void*) getUidRxBytes}, + {"getUidTxPackets", "(I)J", (void*) getUidTxPackets}, + {"getUidRxPackets", "(I)J", (void*) getUidRxPackets}, + + {"getUidTcpTxBytes", "(I)J", (void*) getUidTcpTxBytes}, + {"getUidTcpRxBytes", "(I)J", (void*) getUidTcpRxBytes}, + {"getUidUdpTxBytes", "(I)J", (void*) getUidUdpTxBytes}, + {"getUidUdpRxBytes", "(I)J", (void*) getUidUdpRxBytes}, + + {"getUidTcpTxSegments", "(I)J", (void*) getUidTcpTxSegments}, + {"getUidTcpRxSegments", "(I)J", (void*) getUidTcpRxSegments}, + {"getUidUdpTxPackets", "(I)J", (void*) getUidUdpTxPackets}, + {"getUidUdpRxPackets", "(I)J", (void*) getUidUdpRxPackets}, }; int register_android_net_TrafficStats(JNIEnv* env) { diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index 3b102996adef..fc806a502f15 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -31,25 +31,6 @@ namespace android { static jboolean sScanModeActive = false; -//TODO: check general errors in addition to overflow on snprintf - -/* - * The following remembers the jfieldID's of the fields - * of the DhcpInfo Java object, so that we don't have - * to look them up every time. - */ -static struct fieldIds { - jclass dhcpInfoClass; - jmethodID constructorId; - jfieldID ipaddress; - jfieldID gateway; - jfieldID netmask; - jfieldID dns1; - jfieldID dns2; - jfieldID serverAddress; - jfieldID leaseDuration; -} dhcpInfoFieldIds; - static int doCommand(const char *cmd, char *replybuf, int replybuflen) { size_t reply_len = replybuflen - 1; @@ -411,7 +392,7 @@ static jint android_net_wifi_getRssiHelper(const char *cmd) char* lastSpace = strrchr(reply, ' '); // lastSpace should be preceded by "rssi" and followed by the value - if (lastSpace && !strncmp(lastSpace - 4, "rssi", 4)) { + if (lastSpace && !strncasecmp(lastSpace - 4, "rssi", 4)) { sscanf(lastSpace + 1, "%d", &rssi); } } @@ -575,29 +556,6 @@ static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, job return doBooleanCommand(cmdstr, "OK"); } - -static jboolean android_net_wifi_doDhcpRequest(JNIEnv* env, jobject clazz, jobject info) -{ - jint ipaddr, gateway, mask, dns1, dns2, server, lease; - jboolean succeeded = ((jboolean)::do_dhcp_request(&ipaddr, &gateway, &mask, - &dns1, &dns2, &server, &lease) == 0); - if (succeeded && dhcpInfoFieldIds.dhcpInfoClass != NULL) { - env->SetIntField(info, dhcpInfoFieldIds.ipaddress, ipaddr); - env->SetIntField(info, dhcpInfoFieldIds.gateway, gateway); - env->SetIntField(info, dhcpInfoFieldIds.netmask, mask); - env->SetIntField(info, dhcpInfoFieldIds.dns1, dns1); - env->SetIntField(info, dhcpInfoFieldIds.dns2, dns2); - env->SetIntField(info, dhcpInfoFieldIds.serverAddress, server); - env->SetIntField(info, dhcpInfoFieldIds.leaseDuration, lease); - } - return succeeded; -} - -static jstring android_net_wifi_getDhcpError(JNIEnv* env, jobject clazz) -{ - return env->NewStringUTF(::get_dhcp_error_string()); -} - // ---------------------------------------------------------------------------- /* @@ -665,8 +623,6 @@ static JNINativeMethod gWifiMethods[] = { (void*) android_net_wifi_setSuspendOptimizationsCommand}, { "setCountryCodeCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_setCountryCodeCommand}, - { "doDhcpRequest", "(Landroid/net/DhcpInfo;)Z", (void*) android_net_wifi_doDhcpRequest }, - { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_wifi_getDhcpError }, }; int register_android_net_wifi_WifiManager(JNIEnv* env) @@ -674,18 +630,6 @@ int register_android_net_wifi_WifiManager(JNIEnv* env) jclass wifi = env->FindClass(WIFI_PKG_NAME); LOG_FATAL_IF(wifi == NULL, "Unable to find class " WIFI_PKG_NAME); - dhcpInfoFieldIds.dhcpInfoClass = env->FindClass("android/net/DhcpInfo"); - if (dhcpInfoFieldIds.dhcpInfoClass != NULL) { - dhcpInfoFieldIds.constructorId = env->GetMethodID(dhcpInfoFieldIds.dhcpInfoClass, "<init>", "()V"); - dhcpInfoFieldIds.ipaddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "ipAddress", "I"); - dhcpInfoFieldIds.gateway = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "gateway", "I"); - dhcpInfoFieldIds.netmask = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "netmask", "I"); - dhcpInfoFieldIds.dns1 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns1", "I"); - dhcpInfoFieldIds.dns2 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns2", "I"); - dhcpInfoFieldIds.serverAddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "serverAddress", "I"); - dhcpInfoFieldIds.leaseDuration = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "leaseDuration", "I"); - } - return AndroidRuntime::registerNativeMethods(env, WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods)); } diff --git a/core/jni/android_security_Md5MessageDigest.cpp b/core/jni/android_security_Md5MessageDigest.cpp deleted file mode 100644 index 35335599659c..000000000000 --- a/core/jni/android_security_Md5MessageDigest.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 "jni.h" -#include <JNIHelp.h> -#include <stdlib.h> -#include <stdint.h> - -#include <openssl/md5.h> - -namespace android -{ - -struct fields_t { - jfieldID context; -}; -static fields_t fields; - -static void native_init(JNIEnv *env, jobject clazz) -{ - MD5_CTX* context = (MD5_CTX *)malloc(sizeof(MD5_CTX)); - MD5_Init(context); - - env->SetIntField(clazz, fields.context, (int)context); -} - -static void native_reset(JNIEnv *env, jobject clazz) -{ - MD5_CTX *context = (MD5_CTX *)env->GetIntField(clazz, fields.context); - if (context != NULL) { - free(context); - env->SetIntField(clazz, fields.context, 0 ); - } -} - -static void native_update(JNIEnv *env, jobject clazz, jbyteArray dataArray) -{ - jbyte * data; - jsize dataSize; - MD5_CTX *context = (MD5_CTX *)env->GetIntField(clazz, fields.context); - - if (context == NULL) { - native_init(env, clazz); - context = (MD5_CTX *)env->GetIntField(clazz, fields.context); - } - - data = env->GetByteArrayElements(dataArray, NULL); - if (data == NULL) { - LOGE("Unable to get byte array elements"); - jniThrowException(env, "java/lang/IllegalArgumentException", - "Invalid data array when calling MessageDigest.update()"); - return; - } - dataSize = env->GetArrayLength(dataArray); - - MD5_Update(context, data, dataSize); - - env->ReleaseByteArrayElements(dataArray, data, 0); -} - -static jbyteArray native_digest(JNIEnv *env, jobject clazz) -{ - jbyteArray array; - jbyte md[MD5_DIGEST_LENGTH]; - MD5_CTX *context = (MD5_CTX *)env->GetIntField(clazz, fields.context); - - MD5_Final((uint8_t*)md, context); - - array = env->NewByteArray(MD5_DIGEST_LENGTH); - LOG_ASSERT(array, "Native could not create new byte[]"); - - env->SetByteArrayRegion(array, 0, MD5_DIGEST_LENGTH, md); - - native_reset(env, clazz); - - return array; -} - - -/* - * JNI registration. - */ - -static JNINativeMethod gMethods[] = -{ - /* name, signature, funcPtr */ - {"init", "()V", (void *)native_init}, - {"update", "([B)V", (void *)native_update}, - {"digest", "()[B", (void *)native_digest}, - {"reset", "()V", (void *)native_reset}, -}; - -int register_android_security_Md5MessageDigest(JNIEnv *env) -{ - jclass clazz; - - clazz = env->FindClass("android/security/Md5MessageDigest"); - if (clazz == NULL) { - LOGE("Can't find android/security/Md5MessageDigest"); - return -1; - } - - fields.context = env->GetFieldID(clazz, "mNativeMd5Context", "I"); - if (fields.context == NULL) { - LOGE("Can't find Md5MessageDigest.mNativeMd5Context"); - return -1; - } - - return jniRegisterNativeMethods(env, "android/security/Md5MessageDigest", - gMethods, NELEM(gMethods)); -} - -}; diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index 2c398713295b..bf0504f7c8cc 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -1214,6 +1214,45 @@ static jboolean disconnectPanDeviceNative(JNIEnv *env, jobject object, return JNI_FALSE; } +static jboolean disconnectPanServerDeviceNative(JNIEnv *env, jobject object, + jstring path, jstring address, + jstring iface) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + LOGE("disconnectPanServerDeviceNative"); + native_data_t *nat = get_native_data(env, object); + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + + if (nat && eventLoopNat) { + const char *c_address = env->GetStringUTFChars(address, NULL); + const char *c_path = env->GetStringUTFChars(path, NULL); + const char *c_iface = env->GetStringUTFChars(iface, NULL); + + int len = env->GetStringLength(path) + 1; + char *context_path = (char *)calloc(len, sizeof(char)); + strlcpy(context_path, c_path, len); // for callback + + bool ret = dbus_func_args_async(env, nat->conn, -1, + onPanDeviceConnectionResult, + context_path, eventLoopNat, + get_adapter_path(env, object), + DBUS_NETWORKSERVER_IFACE, + "DisconnectDevice", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_STRING, &c_iface, + DBUS_TYPE_INVALID); + + env->ReleaseStringUTFChars(address, c_address); + env->ReleaseStringUTFChars(iface, c_iface); + env->ReleaseStringUTFChars(path, c_path); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"classInitNative", "()V", (void*)classInitNative}, @@ -1274,6 +1313,8 @@ static JNINativeMethod sMethods[] = { {"connectPanDeviceNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)connectPanDeviceNative}, {"disconnectPanDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectPanDeviceNative}, + {"disconnectPanServerDeviceNative", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z", + (void *)disconnectPanServerDeviceNative}, }; diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index f32f0ff86ae2..069e40b50e46 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -22,24 +22,10 @@ #include <utils/Log.h> #include <ui/Input.h> #include "android_view_MotionEvent.h" +#include "android_util_Binder.h" #include "android/graphics/Matrix.h" -#include <math.h> #include "SkMatrix.h" -#include "SkScalar.h" - -// Number of float items per entry in a DVM sample data array -#define NUM_SAMPLE_DATA 9 - -#define SAMPLE_X 0 -#define SAMPLE_Y 1 -#define SAMPLE_PRESSURE 2 -#define SAMPLE_SIZE 3 -#define SAMPLE_TOUCH_MAJOR 4 -#define SAMPLE_TOUCH_MINOR 5 -#define SAMPLE_TOOL_MAJOR 6 -#define SAMPLE_TOOL_MINOR 7 -#define SAMPLE_ORIENTATION 8 namespace android { @@ -52,35 +38,41 @@ static struct { jmethodID obtain; jmethodID recycle; - jfieldID mDeviceId; - jfieldID mSource; - jfieldID mDownTimeNano; - jfieldID mAction; - jfieldID mXOffset; - jfieldID mYOffset; - jfieldID mXPrecision; - jfieldID mYPrecision; - jfieldID mEdgeFlags; - jfieldID mMetaState; - jfieldID mFlags; - jfieldID mNumPointers; - jfieldID mNumSamples; - jfieldID mPointerIdentifiers; - jfieldID mDataSamples; - jfieldID mEventTimeNanoSamples; - jfieldID mLastDataSampleIndex; - jfieldID mLastEventTimeNanoSampleIndex; + jfieldID mNativePtr; } gMotionEventClassInfo; +static struct { + jclass clazz; + + jfieldID mPackedAxisBits; + jfieldID mPackedAxisValues; + jfieldID x; + jfieldID y; + jfieldID pressure; + jfieldID size; + jfieldID touchMajor; + jfieldID touchMinor; + jfieldID toolMajor; + jfieldID toolMinor; + jfieldID orientation; +} gPointerCoordsClassInfo; + // ---------------------------------------------------------------------------- -jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event) { - jint numPointers = jint(event->getPointerCount()); - jint numHistoricalSamples = jint(event->getHistorySize()); - jint numSamples = numHistoricalSamples + 1; +static MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj) { + return reinterpret_cast<MotionEvent*>( + env->GetIntField(eventObj, gMotionEventClassInfo.mNativePtr)); +} +static void android_view_MotionEvent_setNativePtr(JNIEnv* env, jobject eventObj, + MotionEvent* event) { + env->SetIntField(eventObj, gMotionEventClassInfo.mNativePtr, + reinterpret_cast<int>(event)); +} + +jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event) { jobject eventObj = env->CallStaticObjectMethod(gMotionEventClassInfo.clazz, - gMotionEventClassInfo.obtain, numPointers, numSamples); + gMotionEventClassInfo.obtain); if (env->ExceptionCheck()) { LOGE("An exception occurred while obtaining a motion event."); LOGE_EX(env); @@ -88,261 +80,648 @@ jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* even return NULL; } - env->SetIntField(eventObj, gMotionEventClassInfo.mDeviceId, - event->getDeviceId()); - env->SetIntField(eventObj, gMotionEventClassInfo.mSource, - event->getSource()); - env->SetLongField(eventObj, gMotionEventClassInfo.mDownTimeNano, - event->getDownTime()); - env->SetIntField(eventObj, gMotionEventClassInfo.mAction, - event->getAction()); - env->SetFloatField(eventObj, gMotionEventClassInfo.mXOffset, - event->getXOffset()); - env->SetFloatField(eventObj, gMotionEventClassInfo.mYOffset, - event->getYOffset()); - env->SetFloatField(eventObj, gMotionEventClassInfo.mXPrecision, - event->getXPrecision()); - env->SetFloatField(eventObj, gMotionEventClassInfo.mYPrecision, - event->getYPrecision()); - env->SetIntField(eventObj, gMotionEventClassInfo.mEdgeFlags, - event->getEdgeFlags()); - env->SetIntField(eventObj, gMotionEventClassInfo.mMetaState, - event->getMetaState()); - env->SetIntField(eventObj, gMotionEventClassInfo.mFlags, - event->getFlags()); - env->SetIntField(eventObj, gMotionEventClassInfo.mNumPointers, - numPointers); - env->SetIntField(eventObj, gMotionEventClassInfo.mNumSamples, - numSamples); - env->SetIntField(eventObj, gMotionEventClassInfo.mLastDataSampleIndex, - (numSamples - 1) * numPointers * NUM_SAMPLE_DATA); - env->SetIntField(eventObj, gMotionEventClassInfo.mLastEventTimeNanoSampleIndex, - numSamples - 1); - - jintArray pointerIdentifierArray = jintArray(env->GetObjectField(eventObj, - gMotionEventClassInfo.mPointerIdentifiers)); - jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj, - gMotionEventClassInfo.mDataSamples)); - jlongArray eventTimeNanoSampleArray = jlongArray(env->GetObjectField(eventObj, - gMotionEventClassInfo.mEventTimeNanoSamples)); - - jint* pointerIdentifiers = (jint*)env->GetPrimitiveArrayCritical(pointerIdentifierArray, NULL); - jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL); - jlong* eventTimeNanoSamples = (jlong*)env->GetPrimitiveArrayCritical( - eventTimeNanoSampleArray, NULL); - - const int32_t* srcPointerIdentifiers = event->getPointerIds(); - jint* destPointerIdentifiers = pointerIdentifiers; - for (jint i = 0; i < numPointers; i++) { - *(destPointerIdentifiers++) = *(srcPointerIdentifiers++); - } - - const nsecs_t* srcSampleEventTimes = event->getSampleEventTimes(); - jlong* destEventTimeNanoSamples = eventTimeNanoSamples; - for (jint i = 0; i < numSamples; i++) { - *(destEventTimeNanoSamples++) = *(srcSampleEventTimes++); - } - - const PointerCoords* srcSamplePointerCoords = event->getSamplePointerCoords(); - jfloat* destDataSamples = dataSamples; - jint numItems = numSamples * numPointers; - for (jint i = 0; i < numItems; i++) { - *(destDataSamples++) = srcSamplePointerCoords->x; - *(destDataSamples++) = srcSamplePointerCoords->y; - *(destDataSamples++) = srcSamplePointerCoords->pressure; - *(destDataSamples++) = srcSamplePointerCoords->size; - *(destDataSamples++) = srcSamplePointerCoords->touchMajor; - *(destDataSamples++) = srcSamplePointerCoords->touchMinor; - *(destDataSamples++) = srcSamplePointerCoords->toolMajor; - *(destDataSamples++) = srcSamplePointerCoords->toolMinor; - *(destDataSamples++) = srcSamplePointerCoords->orientation; - srcSamplePointerCoords += 1; - } - - env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, 0); - env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, 0); - env->ReleasePrimitiveArrayCritical(eventTimeNanoSampleArray, eventTimeNanoSamples, 0); - - env->DeleteLocalRef(pointerIdentifierArray); - env->DeleteLocalRef(dataSampleArray); - env->DeleteLocalRef(eventTimeNanoSampleArray); + MotionEvent* destEvent = android_view_MotionEvent_getNativePtr(env, eventObj); + if (!destEvent) { + destEvent = new MotionEvent(); + android_view_MotionEvent_setNativePtr(env, eventObj, destEvent); + } + + destEvent->copyFrom(event, true); return eventObj; } status_t android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, MotionEvent* event) { - jint deviceId = env->GetIntField(eventObj, gMotionEventClassInfo.mDeviceId); - jint source = env->GetIntField(eventObj, gMotionEventClassInfo.mSource); - jlong downTimeNano = env->GetLongField(eventObj, gMotionEventClassInfo.mDownTimeNano); - jint action = env->GetIntField(eventObj, gMotionEventClassInfo.mAction); - jfloat xOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mXOffset); - jfloat yOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mYOffset); - jfloat xPrecision = env->GetFloatField(eventObj, gMotionEventClassInfo.mXPrecision); - jfloat yPrecision = env->GetFloatField(eventObj, gMotionEventClassInfo.mYPrecision); - jint edgeFlags = env->GetIntField(eventObj, gMotionEventClassInfo.mEdgeFlags); - jint metaState = env->GetIntField(eventObj, gMotionEventClassInfo.mMetaState); - jint flags = env->GetIntField(eventObj, gMotionEventClassInfo.mFlags); - jint numPointers = env->GetIntField(eventObj, gMotionEventClassInfo.mNumPointers); - jint numSamples = env->GetIntField(eventObj, gMotionEventClassInfo.mNumSamples); - - if (numPointers == 0) { - LOGE("Malformed MotionEvent: mNumPointers was zero"); + MotionEvent* srcEvent = android_view_MotionEvent_getNativePtr(env, eventObj); + if (!srcEvent) { + LOGE("MotionEvent was finalized"); return BAD_VALUE; } - if (numSamples == 0) { - LOGE("Malformed MotionEvent: mNumSamples was zero"); - return BAD_VALUE; + + event->copyFrom(srcEvent, true); + return OK; +} + +status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) { + env->CallVoidMethod(eventObj, gMotionEventClassInfo.recycle); + if (env->ExceptionCheck()) { + LOGW("An exception occurred while recycling a motion event."); + LOGW_EX(env); + env->ExceptionClear(); + return UNKNOWN_ERROR; + } + return OK; +} + +// ---------------------------------------------------------------------------- + +static const jint HISTORY_CURRENT = -0x80000000; + +static bool validatePointerCount(JNIEnv* env, jint pointerCount) { + if (pointerCount < 1) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "pointerCount must be at least 1"); + return false; + } + return true; +} + +static bool validatePointerIdsArray(JNIEnv* env, jintArray pointerIdsArray, + size_t pointerCount) { + if (!pointerIdsArray) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "pointerIds array must not be null"); + return false; + } + size_t length = size_t(env->GetArrayLength(pointerIdsArray)); + if (length < pointerCount) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "pointerIds array must be large enough to hold all pointers"); + return false; + } + return true; +} + +static bool validatePointerCoordsObjArray(JNIEnv* env, jobjectArray pointerCoordsObjArray, + size_t pointerCount) { + if (!pointerCoordsObjArray) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "pointerCoords array must not be null"); + return false; + } + size_t length = size_t(env->GetArrayLength(pointerCoordsObjArray)); + if (length < pointerCount) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "pointerCoords array must be large enough to hold all pointers"); + return false; + } + return true; +} + +static bool validatePointerIndex(JNIEnv* env, jint pointerIndex, size_t pointerCount) { + if (pointerIndex < 0 || size_t(pointerIndex) >= pointerCount) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "pointerIndex out of range"); + return false; + } + return true; +} + +static bool validateHistoryPos(JNIEnv* env, jint historyPos, size_t historySize) { + if (historyPos < 0 || size_t(historyPos) >= historySize) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "historyPos out of range"); + return false; + } + return true; +} + +static bool validatePointerCoords(JNIEnv* env, jobject pointerCoordsObj) { + if (!pointerCoordsObj) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "pointerCoords must not be null"); + return false; + } + return true; +} + +static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj, + float xOffset, float yOffset, PointerCoords* outRawPointerCoords) { + outRawPointerCoords->clear(); + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_X, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x) - xOffset); + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_Y, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y) - yOffset); + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure)); + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_SIZE, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size)); + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor)); + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor)); + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor)); + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor)); + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation)); + + uint32_t bits = env->GetIntField(pointerCoordsObj, + gPointerCoordsClassInfo.mPackedAxisBits); + if (bits) { + jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj, + gPointerCoordsClassInfo.mPackedAxisValues)); + if (valuesArray) { + jfloat* values = static_cast<jfloat*>( + env->GetPrimitiveArrayCritical(valuesArray, NULL)); + + uint32_t index = 0; + do { + uint32_t axis = __builtin_ctz(bits); + uint32_t axisBit = 1 << axis; + bits &= ~axisBit; + outRawPointerCoords->setAxisValue(axis, values[index++]); + } while (bits); + + env->ReleasePrimitiveArrayCritical(valuesArray, values, JNI_ABORT); + env->DeleteLocalRef(valuesArray); + } + } +} + +static jfloatArray obtainPackedAxisValuesArray(JNIEnv* env, uint32_t minSize, + jobject outPointerCoordsObj) { + jfloatArray outValuesArray = jfloatArray(env->GetObjectField(outPointerCoordsObj, + gPointerCoordsClassInfo.mPackedAxisValues)); + if (outValuesArray) { + uint32_t size = env->GetArrayLength(outValuesArray); + if (minSize <= size) { + return outValuesArray; + } + env->DeleteLocalRef(outValuesArray); + } + uint32_t size = 8; + while (size < minSize) { + size *= 2; + } + outValuesArray = env->NewFloatArray(size); + env->SetObjectField(outPointerCoordsObj, + gPointerCoordsClassInfo.mPackedAxisValues, outValuesArray); + return outValuesArray; +} + +static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointerCoords, + float xOffset, float yOffset, jobject outPointerCoordsObj) { + env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.x, + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset); + env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.y, + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset); + env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.pressure, + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.size, + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_SIZE)); + env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.touchMajor, + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR)); + env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.touchMinor, + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR)); + env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.toolMajor, + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR)); + env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.toolMinor, + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR)); + env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation, + rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); + + const uint32_t unpackedAxisBits = 0 + | (1 << AMOTION_EVENT_AXIS_X) + | (1 << AMOTION_EVENT_AXIS_Y) + | (1 << AMOTION_EVENT_AXIS_PRESSURE) + | (1 << AMOTION_EVENT_AXIS_SIZE) + | (1 << AMOTION_EVENT_AXIS_TOUCH_MAJOR) + | (1 << AMOTION_EVENT_AXIS_TOUCH_MINOR) + | (1 << AMOTION_EVENT_AXIS_TOOL_MAJOR) + | (1 << AMOTION_EVENT_AXIS_TOOL_MINOR) + | (1 << AMOTION_EVENT_AXIS_ORIENTATION); + + uint32_t outBits = 0; + uint32_t remainingBits = rawPointerCoords->bits & ~unpackedAxisBits; + if (remainingBits) { + uint32_t packedAxesCount = __builtin_popcount(remainingBits); + jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount, + outPointerCoordsObj); + if (!outValuesArray) { + return; // OOM + } + + jfloat* outValues = static_cast<jfloat*>(env->GetPrimitiveArrayCritical( + outValuesArray, NULL)); + + const float* values = rawPointerCoords->values; + uint32_t index = 0; + do { + uint32_t axis = __builtin_ctz(remainingBits); + uint32_t axisBit = 1 << axis; + remainingBits &= ~axisBit; + outBits |= axisBit; + outValues[index++] = rawPointerCoords->getAxisValue(axis); + } while (remainingBits); + + env->ReleasePrimitiveArrayCritical(outValuesArray, outValues, 0); + env->DeleteLocalRef(outValuesArray); } + env->SetIntField(outPointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits, outBits); +} - jintArray pointerIdentifierArray = jintArray(env->GetObjectField(eventObj, - gMotionEventClassInfo.mPointerIdentifiers)); - jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj, - gMotionEventClassInfo.mDataSamples)); - jlongArray eventTimeNanoSampleArray = jlongArray(env->GetObjectField(eventObj, - gMotionEventClassInfo.mEventTimeNanoSamples)); - jint* pointerIdentifiers = (jint*)env->GetPrimitiveArrayCritical(pointerIdentifierArray, NULL); - jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL); - jlong* eventTimeNanoSamples = (jlong*)env->GetPrimitiveArrayCritical( - eventTimeNanoSampleArray, NULL); +// ---------------------------------------------------------------------------- - jfloat* srcDataSamples = dataSamples; - jlong* srcEventTimeNanoSamples = eventTimeNanoSamples; +static jint android_view_MotionEvent_nativeInitialize(JNIEnv* env, jclass clazz, + jint nativePtr, + jint deviceId, jint source, jint action, jint flags, jint edgeFlags, jint metaState, + jfloat xOffset, jfloat yOffset, jfloat xPrecision, jfloat yPrecision, + jlong downTimeNanos, jlong eventTimeNanos, + jint pointerCount, jintArray pointerIdsArray, jobjectArray pointerCoordsObjArray) { + if (!validatePointerCount(env, pointerCount) + || !validatePointerIdsArray(env, pointerIdsArray, pointerCount) + || !validatePointerCoordsObjArray(env, pointerCoordsObjArray, pointerCount)) { + return 0; + } - jlong sampleEventTime = *(srcEventTimeNanoSamples++); - PointerCoords samplePointerCoords[MAX_POINTERS]; - for (jint j = 0; j < numPointers; j++) { - samplePointerCoords[j].x = *(srcDataSamples++); - samplePointerCoords[j].y = *(srcDataSamples++); - samplePointerCoords[j].pressure = *(srcDataSamples++); - samplePointerCoords[j].size = *(srcDataSamples++); - samplePointerCoords[j].touchMajor = *(srcDataSamples++); - samplePointerCoords[j].touchMinor = *(srcDataSamples++); - samplePointerCoords[j].toolMajor = *(srcDataSamples++); - samplePointerCoords[j].toolMinor = *(srcDataSamples++); - samplePointerCoords[j].orientation = *(srcDataSamples++); + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + if (!event) { + event = new MotionEvent(); } + PointerCoords rawPointerCoords[pointerCount]; + + for (jint i = 0; i < pointerCount; i++) { + jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i); + if (!pointerCoordsObj) { + jniThrowNullPointerException(env, "pointerCoords"); + if (!nativePtr) { + delete event; + } + return 0; + } + pointerCoordsToNative(env, pointerCoordsObj, xOffset, yOffset, &rawPointerCoords[i]); + env->DeleteLocalRef(pointerCoordsObj); + } + + int* pointerIds = static_cast<int*>(env->GetPrimitiveArrayCritical(pointerIdsArray, NULL)); + event->initialize(deviceId, source, action, flags, edgeFlags, metaState, - xOffset, yOffset, xPrecision, yPrecision, downTimeNano, sampleEventTime, - numPointers, pointerIdentifiers, samplePointerCoords); - - for (jint i = 1; i < numSamples; i++) { - sampleEventTime = *(srcEventTimeNanoSamples++); - for (jint j = 0; j < numPointers; j++) { - samplePointerCoords[j].x = *(srcDataSamples++); - samplePointerCoords[j].y = *(srcDataSamples++); - samplePointerCoords[j].pressure = *(srcDataSamples++); - samplePointerCoords[j].size = *(srcDataSamples++); - samplePointerCoords[j].touchMajor = *(srcDataSamples++); - samplePointerCoords[j].touchMinor = *(srcDataSamples++); - samplePointerCoords[j].toolMajor = *(srcDataSamples++); - samplePointerCoords[j].toolMinor = *(srcDataSamples++); - samplePointerCoords[j].orientation = *(srcDataSamples++); + xOffset, yOffset, xPrecision, yPrecision, + downTimeNanos, eventTimeNanos, pointerCount, pointerIds, rawPointerCoords); + + env->ReleasePrimitiveArrayCritical(pointerIdsArray, pointerIds, JNI_ABORT); + return reinterpret_cast<jint>(event); +} + +static jint android_view_MotionEvent_nativeCopy(JNIEnv* env, jclass clazz, + jint destNativePtr, jint sourceNativePtr, jboolean keepHistory) { + MotionEvent* destEvent = reinterpret_cast<MotionEvent*>(destNativePtr); + if (!destEvent) { + destEvent = new MotionEvent(); + } + MotionEvent* sourceEvent = reinterpret_cast<MotionEvent*>(sourceNativePtr); + destEvent->copyFrom(sourceEvent, keepHistory); + return reinterpret_cast<jint>(destEvent); +} + +static void android_view_MotionEvent_nativeDispose(JNIEnv* env, jclass clazz, + jint nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + delete event; +} + +static void android_view_MotionEvent_nativeAddBatch(JNIEnv* env, jclass clazz, + jint nativePtr, jlong eventTimeNanos, jobjectArray pointerCoordsObjArray, + jint metaState) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + size_t pointerCount = event->getPointerCount(); + if (!validatePointerCoordsObjArray(env, pointerCoordsObjArray, pointerCount)) { + return; + } + + PointerCoords rawPointerCoords[pointerCount]; + + for (size_t i = 0; i < pointerCount; i++) { + jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i); + if (!pointerCoordsObj) { + jniThrowNullPointerException(env, "pointerCoords"); + return; } - event->addSample(sampleEventTime, samplePointerCoords); + pointerCoordsToNative(env, pointerCoordsObj, + event->getXOffset(), event->getYOffset(), &rawPointerCoords[i]); + env->DeleteLocalRef(pointerCoordsObj); } - env->ReleasePrimitiveArrayCritical(eventTimeNanoSampleArray, eventTimeNanoSamples, JNI_ABORT); - env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, JNI_ABORT); - env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, JNI_ABORT); + event->addSample(eventTimeNanos, rawPointerCoords); + event->setMetaState(event->getMetaState() | metaState); +} - env->DeleteLocalRef(eventTimeNanoSampleArray); - env->DeleteLocalRef(dataSampleArray); - env->DeleteLocalRef(pointerIdentifierArray); - return OK; +static jint android_view_MotionEvent_nativeGetDeviceId(JNIEnv* env, jclass clazz, + jint nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return event->getDeviceId(); } -status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) { - env->CallVoidMethod(eventObj, gMotionEventClassInfo.recycle); - if (env->ExceptionCheck()) { - LOGW("An exception occurred while recycling a motion event."); - LOGW_EX(env); - env->ExceptionClear(); - return UNKNOWN_ERROR; +static jint android_view_MotionEvent_nativeGetSource(JNIEnv* env, jclass clazz, + jint nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return event->getSource(); +} + +static void android_view_MotionEvent_nativeSetSource(JNIEnv* env, jclass clazz, + jint nativePtr, jint source) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + event->setSource(source); +} + +static jint android_view_MotionEvent_nativeGetAction(JNIEnv* env, jclass clazz, + jint nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return event->getAction(); +} + +static void android_view_MotionEvent_nativeSetAction(JNIEnv* env, jclass clazz, + jint nativePtr, jint action) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + event->setAction(action); +} + +static jint android_view_MotionEvent_nativeGetFlags(JNIEnv* env, jclass clazz, + jint nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return event->getFlags(); +} + +static jint android_view_MotionEvent_nativeGetEdgeFlags(JNIEnv* env, jclass clazz, + jint nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return event->getEdgeFlags(); +} + +static void android_view_MotionEvent_nativeSetEdgeFlags(JNIEnv* env, jclass clazz, + jint nativePtr, jint edgeFlags) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + event->setEdgeFlags(edgeFlags); +} + +static jint android_view_MotionEvent_nativeGetMetaState(JNIEnv* env, jclass clazz, + jint nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return event->getMetaState(); +} + +static void android_view_MotionEvent_nativeOffsetLocation(JNIEnv* env, jclass clazz, + jint nativePtr, jfloat deltaX, jfloat deltaY) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return event->offsetLocation(deltaX, deltaY); +} + +static jfloat android_view_MotionEvent_nativeGetXPrecision(JNIEnv* env, jclass clazz, + jint nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return event->getXPrecision(); +} + +static jfloat android_view_MotionEvent_nativeGetYPrecision(JNIEnv* env, jclass clazz, + jint nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return event->getYPrecision(); +} + +static jlong android_view_MotionEvent_nativeGetDownTimeNanos(JNIEnv* env, jclass clazz, + jint nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return event->getDownTime(); +} + +static jint android_view_MotionEvent_nativeGetPointerCount(JNIEnv* env, jclass clazz, + jint nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return jint(event->getPointerCount()); +} + +static jint android_view_MotionEvent_nativeGetPointerId(JNIEnv* env, jclass clazz, + jint nativePtr, jint pointerIndex) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + size_t pointerCount = event->getPointerCount(); + if (!validatePointerIndex(env, pointerIndex, pointerCount)) { + return -1; } - return OK; + return event->getPointerId(pointerIndex); } -static inline float transformAngle(const SkMatrix* matrix, float angleRadians) { - // Construct and transform a vector oriented at the specified clockwise angle from vertical. - // Coordinate system: down is increasing Y, right is increasing X. - SkPoint vector; - vector.fX = SkFloatToScalar(sinf(angleRadians)); - vector.fY = SkFloatToScalar(- cosf(angleRadians)); - matrix->mapVectors(& vector, 1); +static jint android_view_MotionEvent_nativeFindPointerIndex(JNIEnv* env, jclass clazz, + jint nativePtr, jint pointerId) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + size_t pointerCount = event->getPointerCount(); + for (size_t i = 0; i < pointerCount; i++) { + if (event->getPointerId(i) == pointerId) { + return i; + } + } + return -1; +} + +static jint android_view_MotionEvent_nativeGetHistorySize(JNIEnv* env, jclass clazz, + jint nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return jint(event->getHistorySize()); +} - // Derive the transformed vector's clockwise angle from vertical. - float result = atan2f(SkScalarToFloat(vector.fX), SkScalarToFloat(- vector.fY)); - if (result < - M_PI_2) { - result += M_PI; - } else if (result > M_PI_2) { - result -= M_PI; +static jlong android_view_MotionEvent_nativeGetEventTimeNanos(JNIEnv* env, jclass clazz, + jint nativePtr, jint historyPos) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + if (historyPos == HISTORY_CURRENT) { + return event->getEventTime(); + } else { + size_t historySize = event->getHistorySize(); + if (!validateHistoryPos(env, historyPos, historySize)) { + return 0; + } + return event->getHistoricalEventTime(historyPos); } - return result; } -static void android_view_MotionEvent_nativeTransform(JNIEnv* env, - jobject eventObj, jobject matrixObj) { - SkMatrix* matrix = android_graphics_Matrix_getSkMatrix(env, matrixObj); +static jfloat android_view_MotionEvent_nativeGetRawAxisValue(JNIEnv* env, jclass clazz, + jint nativePtr, jint axis, jint pointerIndex, jint historyPos) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + size_t pointerCount = event->getPointerCount(); + if (!validatePointerIndex(env, pointerIndex, pointerCount)) { + return 0; + } - jfloat oldXOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mXOffset); - jfloat oldYOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mYOffset); - jint numPointers = env->GetIntField(eventObj, gMotionEventClassInfo.mNumPointers); - jint numSamples = env->GetIntField(eventObj, gMotionEventClassInfo.mNumSamples); - jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj, - gMotionEventClassInfo.mDataSamples)); - jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL); - - // The tricky part of this implementation is to preserve the value of - // rawX and rawY. So we apply the transformation to the first point - // then derive an appropriate new X/Y offset that will preserve rawX and rawY. - SkPoint point; - jfloat rawX = dataSamples[SAMPLE_X]; - jfloat rawY = dataSamples[SAMPLE_Y]; - matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset), - & point); - jfloat newX = SkScalarToFloat(point.fX); - jfloat newY = SkScalarToFloat(point.fY); - jfloat newXOffset = newX - rawX; - jfloat newYOffset = newY - rawY; - - dataSamples[SAMPLE_ORIENTATION] = transformAngle(matrix, dataSamples[SAMPLE_ORIENTATION]); - - // Apply the transformation to all samples. - jfloat* currentDataSample = dataSamples; - jfloat* endDataSample = dataSamples + numPointers * numSamples * NUM_SAMPLE_DATA; - for (;;) { - currentDataSample += NUM_SAMPLE_DATA; - if (currentDataSample == endDataSample) { - break; + if (historyPos == HISTORY_CURRENT) { + return event->getRawAxisValue(axis, pointerIndex); + } else { + size_t historySize = event->getHistorySize(); + if (!validateHistoryPos(env, historyPos, historySize)) { + return 0; } + return event->getHistoricalRawAxisValue(axis, pointerIndex, historyPos); + } +} - jfloat x = currentDataSample[SAMPLE_X] + oldXOffset; - jfloat y = currentDataSample[SAMPLE_Y] + oldYOffset; - matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), & point); - currentDataSample[SAMPLE_X] = SkScalarToFloat(point.fX) - newXOffset; - currentDataSample[SAMPLE_Y] = SkScalarToFloat(point.fY) - newYOffset; +static jfloat android_view_MotionEvent_nativeGetAxisValue(JNIEnv* env, jclass clazz, + jint nativePtr, jint axis, jint pointerIndex, jint historyPos) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + size_t pointerCount = event->getPointerCount(); + if (!validatePointerIndex(env, pointerIndex, pointerCount)) { + return 0; + } - currentDataSample[SAMPLE_ORIENTATION] = transformAngle(matrix, - currentDataSample[SAMPLE_ORIENTATION]); + if (historyPos == HISTORY_CURRENT) { + return event->getAxisValue(axis, pointerIndex); + } else { + size_t historySize = event->getHistorySize(); + if (!validateHistoryPos(env, historyPos, historySize)) { + return 0; + } + return event->getHistoricalAxisValue(axis, pointerIndex, historyPos); } +} - env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, 0); +static void android_view_MotionEvent_nativeGetPointerCoords(JNIEnv* env, jclass clazz, + jint nativePtr, jint pointerIndex, jint historyPos, jobject outPointerCoordsObj) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + size_t pointerCount = event->getPointerCount(); + if (!validatePointerIndex(env, pointerIndex, pointerCount) + || !validatePointerCoords(env, outPointerCoordsObj)) { + return; + } - env->SetFloatField(eventObj, gMotionEventClassInfo.mXOffset, newXOffset); - env->SetFloatField(eventObj, gMotionEventClassInfo.mYOffset, newYOffset); + const PointerCoords* rawPointerCoords; + if (historyPos == HISTORY_CURRENT) { + rawPointerCoords = event->getRawPointerCoords(pointerIndex); + } else { + size_t historySize = event->getHistorySize(); + if (!validateHistoryPos(env, historyPos, historySize)) { + return; + } + rawPointerCoords = event->getHistoricalRawPointerCoords(pointerIndex, historyPos); + } + pointerCoordsFromNative(env, rawPointerCoords, event->getXOffset(), event->getYOffset(), + outPointerCoordsObj); +} + +static void android_view_MotionEvent_nativeScale(JNIEnv* env, jclass clazz, + jint nativePtr, jfloat scale) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + event->scale(scale); +} + +static void android_view_MotionEvent_nativeTransform(JNIEnv* env, jclass clazz, + jint nativePtr, jobject matrixObj) { + SkMatrix* matrix = android_graphics_Matrix_getSkMatrix(env, matrixObj); + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + event->transform(matrix); +} + +static jint android_view_MotionEvent_nativeReadFromParcel(JNIEnv* env, jclass clazz, + jint nativePtr, jobject parcelObj) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + if (!event) { + event = new MotionEvent(); + } + + Parcel* parcel = parcelForJavaObject(env, parcelObj); - env->DeleteLocalRef(dataSampleArray); + status_t status = event->readFromParcel(parcel); + if (status) { + if (!nativePtr) { + delete event; + } + jniThrowRuntimeException(env, "Failed to read MotionEvent parcel."); + return 0; + } + return reinterpret_cast<jint>(event); +} + +static void android_view_MotionEvent_nativeWriteToParcel(JNIEnv* env, jclass clazz, + jint nativePtr, jobject parcelObj) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + Parcel* parcel = parcelForJavaObject(env, parcelObj); + + status_t status = event->writeToParcel(parcel); + if (status) { + jniThrowRuntimeException(env, "Failed to write MotionEvent parcel."); + } } // ---------------------------------------------------------------------------- static JNINativeMethod gMotionEventMethods[] = { /* name, signature, funcPtr */ + { "nativeInitialize", + "(IIIIIIIFFFFJJI[I[Landroid/view/MotionEvent$PointerCoords;)I", + (void*)android_view_MotionEvent_nativeInitialize }, + { "nativeCopy", + "(IIZ)I", + (void*)android_view_MotionEvent_nativeCopy }, + { "nativeDispose", + "(I)V", + (void*)android_view_MotionEvent_nativeDispose }, + { "nativeAddBatch", + "(IJ[Landroid/view/MotionEvent$PointerCoords;I)V", + (void*)android_view_MotionEvent_nativeAddBatch }, + { "nativeGetDeviceId", + "(I)I", + (void*)android_view_MotionEvent_nativeGetDeviceId }, + { "nativeGetSource", + "(I)I", + (void*)android_view_MotionEvent_nativeGetSource }, + { "nativeSetSource", + "(II)I", + (void*)android_view_MotionEvent_nativeSetSource }, + { "nativeGetAction", + "(I)I", + (void*)android_view_MotionEvent_nativeGetAction }, + { "nativeSetAction", + "(II)V", + (void*)android_view_MotionEvent_nativeSetAction }, + { "nativeGetFlags", + "(I)I", + (void*)android_view_MotionEvent_nativeGetFlags }, + { "nativeGetEdgeFlags", + "(I)I", + (void*)android_view_MotionEvent_nativeGetEdgeFlags }, + { "nativeSetEdgeFlags", + "(II)V", + (void*)android_view_MotionEvent_nativeSetEdgeFlags }, + { "nativeGetMetaState", + "(I)I", + (void*)android_view_MotionEvent_nativeGetMetaState }, + { "nativeOffsetLocation", + "(IFF)V", + (void*)android_view_MotionEvent_nativeOffsetLocation }, + { "nativeGetXPrecision", + "(I)F", + (void*)android_view_MotionEvent_nativeGetXPrecision }, + { "nativeGetYPrecision", + "(I)F", + (void*)android_view_MotionEvent_nativeGetYPrecision }, + { "nativeGetDownTimeNanos", + "(I)J", + (void*)android_view_MotionEvent_nativeGetDownTimeNanos }, + { "nativeGetPointerCount", + "(I)I", + (void*)android_view_MotionEvent_nativeGetPointerCount }, + { "nativeGetPointerId", + "(II)I", + (void*)android_view_MotionEvent_nativeGetPointerId }, + { "nativeFindPointerIndex", + "(II)I", + (void*)android_view_MotionEvent_nativeFindPointerIndex }, + { "nativeGetHistorySize", + "(I)I", + (void*)android_view_MotionEvent_nativeGetHistorySize }, + { "nativeGetEventTimeNanos", + "(II)J", + (void*)android_view_MotionEvent_nativeGetEventTimeNanos }, + { "nativeGetRawAxisValue", + "(IIII)F", + (void*)android_view_MotionEvent_nativeGetRawAxisValue }, + { "nativeGetAxisValue", + "(IIII)F", + (void*)android_view_MotionEvent_nativeGetAxisValue }, + { "nativeGetPointerCoords", + "(IIILandroid/view/MotionEvent$PointerCoords;)V", + (void*)android_view_MotionEvent_nativeGetPointerCoords }, + { "nativeScale", + "(IF)V", + (void*)android_view_MotionEvent_nativeScale }, { "nativeTransform", - "(Landroid/graphics/Matrix;)V", + "(ILandroid/graphics/Matrix;)V", (void*)android_view_MotionEvent_nativeTransform }, + { "nativeReadFromParcel", + "(ILandroid/os/Parcel;)I", + (void*)android_view_MotionEvent_nativeReadFromParcel }, + { "nativeWriteToParcel", + "(ILandroid/os/Parcel;)V", + (void*)android_view_MotionEvent_nativeWriteToParcel }, }; #define FIND_CLASS(var, className) \ @@ -370,46 +749,36 @@ int register_android_view_MotionEvent(JNIEnv* env) { FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent"); GET_STATIC_METHOD_ID(gMotionEventClassInfo.obtain, gMotionEventClassInfo.clazz, - "obtain", "(II)Landroid/view/MotionEvent;"); + "obtain", "()Landroid/view/MotionEvent;"); GET_METHOD_ID(gMotionEventClassInfo.recycle, gMotionEventClassInfo.clazz, "recycle", "()V"); - - GET_FIELD_ID(gMotionEventClassInfo.mDeviceId, gMotionEventClassInfo.clazz, - "mDeviceId", "I"); - GET_FIELD_ID(gMotionEventClassInfo.mSource, gMotionEventClassInfo.clazz, - "mSource", "I"); - GET_FIELD_ID(gMotionEventClassInfo.mDownTimeNano, gMotionEventClassInfo.clazz, - "mDownTimeNano", "J"); - GET_FIELD_ID(gMotionEventClassInfo.mAction, gMotionEventClassInfo.clazz, - "mAction", "I"); - GET_FIELD_ID(gMotionEventClassInfo.mXOffset, gMotionEventClassInfo.clazz, - "mXOffset", "F"); - GET_FIELD_ID(gMotionEventClassInfo.mYOffset, gMotionEventClassInfo.clazz, - "mYOffset", "F"); - GET_FIELD_ID(gMotionEventClassInfo.mXPrecision, gMotionEventClassInfo.clazz, - "mXPrecision", "F"); - GET_FIELD_ID(gMotionEventClassInfo.mYPrecision, gMotionEventClassInfo.clazz, - "mYPrecision", "F"); - GET_FIELD_ID(gMotionEventClassInfo.mEdgeFlags, gMotionEventClassInfo.clazz, - "mEdgeFlags", "I"); - GET_FIELD_ID(gMotionEventClassInfo.mMetaState, gMotionEventClassInfo.clazz, - "mMetaState", "I"); - GET_FIELD_ID(gMotionEventClassInfo.mFlags, gMotionEventClassInfo.clazz, - "mFlags", "I"); - GET_FIELD_ID(gMotionEventClassInfo.mNumPointers, gMotionEventClassInfo.clazz, - "mNumPointers", "I"); - GET_FIELD_ID(gMotionEventClassInfo.mNumSamples, gMotionEventClassInfo.clazz, - "mNumSamples", "I"); - GET_FIELD_ID(gMotionEventClassInfo.mPointerIdentifiers, gMotionEventClassInfo.clazz, - "mPointerIdentifiers", "[I"); - GET_FIELD_ID(gMotionEventClassInfo.mDataSamples, gMotionEventClassInfo.clazz, - "mDataSamples", "[F"); - GET_FIELD_ID(gMotionEventClassInfo.mEventTimeNanoSamples, gMotionEventClassInfo.clazz, - "mEventTimeNanoSamples", "[J"); - GET_FIELD_ID(gMotionEventClassInfo.mLastDataSampleIndex, gMotionEventClassInfo.clazz, - "mLastDataSampleIndex", "I"); - GET_FIELD_ID(gMotionEventClassInfo.mLastEventTimeNanoSampleIndex, gMotionEventClassInfo.clazz, - "mLastEventTimeNanoSampleIndex", "I"); + GET_FIELD_ID(gMotionEventClassInfo.mNativePtr, gMotionEventClassInfo.clazz, + "mNativePtr", "I"); + + FIND_CLASS(gPointerCoordsClassInfo.clazz, "android/view/MotionEvent$PointerCoords"); + + GET_FIELD_ID(gPointerCoordsClassInfo.mPackedAxisBits, gPointerCoordsClassInfo.clazz, + "mPackedAxisBits", "I"); + GET_FIELD_ID(gPointerCoordsClassInfo.mPackedAxisValues, gPointerCoordsClassInfo.clazz, + "mPackedAxisValues", "[F"); + GET_FIELD_ID(gPointerCoordsClassInfo.x, gPointerCoordsClassInfo.clazz, + "x", "F"); + GET_FIELD_ID(gPointerCoordsClassInfo.y, gPointerCoordsClassInfo.clazz, + "y", "F"); + GET_FIELD_ID(gPointerCoordsClassInfo.pressure, gPointerCoordsClassInfo.clazz, + "pressure", "F"); + GET_FIELD_ID(gPointerCoordsClassInfo.size, gPointerCoordsClassInfo.clazz, + "size", "F"); + GET_FIELD_ID(gPointerCoordsClassInfo.touchMajor, gPointerCoordsClassInfo.clazz, + "touchMajor", "F"); + GET_FIELD_ID(gPointerCoordsClassInfo.touchMinor, gPointerCoordsClassInfo.clazz, + "touchMinor", "F"); + GET_FIELD_ID(gPointerCoordsClassInfo.toolMajor, gPointerCoordsClassInfo.clazz, + "toolMajor", "F"); + GET_FIELD_ID(gPointerCoordsClassInfo.toolMinor, gPointerCoordsClassInfo.clazz, + "toolMinor", "F"); + GET_FIELD_ID(gPointerCoordsClassInfo.orientation, gPointerCoordsClassInfo.clazz, + "orientation", "F"); return 0; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7ac7ad883b5a..1df6fe5d8dec 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -82,9 +82,11 @@ <protected-broadcast android:name="android.bluetooth.device.action.PAIRING_REQUEST" /> <protected-broadcast android:name="android.bluetooth.device.action.PAIRING_CANCEL" /> - <protected-broadcast android:name="android.hardware.action.USB_CONNECTED" /> - <protected-broadcast android:name="android.hardware.action.USB_DISCONNECTED" /> <protected-broadcast android:name="android.hardware.action.USB_STATE" /> + <protected-broadcast android:name="android.hardware.action.USB_ACCESSORY_ATTACHED" /> + <protected-broadcast android:name="android.hardware.action.USB_ACCESSORY_ATTACHED" /> + <protected-broadcast android:name="android.hardware.action.USB_DEVICE_ATTACHED" /> + <protected-broadcast android:name="android.hardware.action.USB_DEVICE_DETACHED" /> <protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" /> <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" /> @@ -462,11 +464,10 @@ android:label="@string/permlab_flashlight" android:description="@string/permdesc_flashlight" /> - <!-- Allows an application to access USB devices - @hide --> + <!-- Allows an application to access USB devices --> <permission android:name="android.permission.ACCESS_USB" android:permissionGroup="android.permission-group.HARDWARE_CONTROLS" - android:protectionLevel="signatureOrSystem" + android:protectionLevel="normal" android:label="@string/permlab_accessUsb" android:description="@string/permdesc_accessUsb" /> diff --git a/core/res/assets/webkit/incognito_mode_start_page.html b/core/res/assets/webkit/incognito_mode_start_page.html index b070c6d0e428..5d7a3fbf98c0 100644 --- a/core/res/assets/webkit/incognito_mode_start_page.html +++ b/core/res/assets/webkit/incognito_mode_start_page.html @@ -6,12 +6,12 @@ <body> <p><strong>You've gone incognito</strong>. Pages you view in this window won't appear in your browser history or search history, and they won't - leave other traces, like cookies, on your computer after you close the + leave other traces, like cookies, on your device after you close the incognito window. Any files you download or bookmarks you create will be preserved, however.</p> <p><strong>Going incognito doesn't affect the behavior of other people, - servers, or software. Be wary of:</strong></p> + servers, or software. Be wary of:</strong></p> <ul> <li>Websites that collect or share information about you</li> diff --git a/core/res/res/anim/wallpaper_intra_close_enter.xml b/core/res/res/anim/wallpaper_intra_close_enter.xml index e05345dc16cb..a499a0972c84 100644 --- a/core/res/res/anim/wallpaper_intra_close_enter.xml +++ b/core/res/res/anim/wallpaper_intra_close_enter.xml @@ -19,16 +19,16 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:detachWallpaper="true" android:shareInterpolator="false"> - <scale android:fromXScale="1.0" android:toXScale="1.0" - android:fromYScale=".9" android:toYScale="1.0" + <scale android:fromXScale=".95" android:toXScale="1.0" + android:fromYScale=".95" android:toYScale="1.0" android:pivotX="50%p" android:pivotY="50%p" android:fillEnabled="true" android:fillBefore="true" android:interpolator="@interpolator/decelerate_quint" - android:startOffset="200" + android:startOffset="160" android:duration="300" /> <alpha android:fromAlpha="0" android:toAlpha="1.0" android:fillEnabled="true" android:fillBefore="true" - android:interpolator="@interpolator/decelerate_quint" - android:startOffset="200" + android:interpolator="@interpolator/decelerate_cubic" + android:startOffset="160" android:duration="300"/> -</set> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/wallpaper_intra_close_exit.xml b/core/res/res/anim/wallpaper_intra_close_exit.xml index df7acc9e0a5a..12a8df52985b 100644 --- a/core/res/res/anim/wallpaper_intra_close_exit.xml +++ b/core/res/res/anim/wallpaper_intra_close_exit.xml @@ -19,14 +19,14 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:detachWallpaper="true" android:shareInterpolator="false"> - <scale android:fromXScale="1.0" android:toXScale="0.9" - android:fromYScale="1.0" android:toYScale="0.9" + <scale android:fromXScale="1.0" android:toXScale="1.0" + android:fromYScale="1.0" android:toYScale="0.0" android:pivotX="50%p" android:pivotY="50%p" android:fillEnabled="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quint" + android:interpolator="@interpolator/linear" android:duration="300" /> <alpha android:fromAlpha="1.0" android:toAlpha="0" - android:fillEnabled="true" android:fillAfter="true" + android:fillEnabled="true" android:fillAfter="true" android:interpolator="@interpolator/decelerate_cubic" - android:duration="150"/> -</set> + android:duration="120"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/wallpaper_intra_open_enter.xml b/core/res/res/anim/wallpaper_intra_open_enter.xml index ff310a16b845..a499a0972c84 100644 --- a/core/res/res/anim/wallpaper_intra_open_enter.xml +++ b/core/res/res/anim/wallpaper_intra_open_enter.xml @@ -19,14 +19,16 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:detachWallpaper="true" android:shareInterpolator="false"> - <scale android:fromXScale="0.95" android:toXScale="1.0" - android:fromYScale="0.95" android:toYScale="1.0" + <scale android:fromXScale=".95" android:toXScale="1.0" + android:fromYScale=".95" android:toYScale="1.0" android:pivotX="50%p" android:pivotY="50%p" + android:fillEnabled="true" android:fillBefore="true" android:interpolator="@interpolator/decelerate_quint" - android:startOffset="200" + android:startOffset="160" android:duration="300" /> <alpha android:fromAlpha="0" android:toAlpha="1.0" + android:fillEnabled="true" android:fillBefore="true" android:interpolator="@interpolator/decelerate_cubic" - android:startOffset="200" + android:startOffset="160" android:duration="300"/> -</set> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/wallpaper_intra_open_exit.xml b/core/res/res/anim/wallpaper_intra_open_exit.xml index 47ea0b462a03..12a8df52985b 100644 --- a/core/res/res/anim/wallpaper_intra_open_exit.xml +++ b/core/res/res/anim/wallpaper_intra_open_exit.xml @@ -22,9 +22,11 @@ <scale android:fromXScale="1.0" android:toXScale="1.0" android:fromYScale="1.0" android:toYScale="0.0" android:pivotX="50%p" android:pivotY="50%p" + android:fillEnabled="true" android:fillAfter="true" android:interpolator="@interpolator/linear" android:duration="300" /> <alpha android:fromAlpha="1.0" android:toAlpha="0" + android:fillEnabled="true" android:fillAfter="true" android:interpolator="@interpolator/decelerate_cubic" - android:duration="160"/> -</set> + android:duration="120"/> +</set>
\ No newline at end of file diff --git a/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png b/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png Binary files differnew file mode 100644 index 000000000000..b9435b645117 --- /dev/null +++ b/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png b/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png Binary files differnew file mode 100644 index 000000000000..477d820cc965 --- /dev/null +++ b/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png Binary files differnew file mode 100644 index 000000000000..b9435b645117 --- /dev/null +++ b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png Binary files differnew file mode 100644 index 000000000000..477d820cc965 --- /dev/null +++ b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png diff --git a/core/res/res/menu/webview_copy.xml b/core/res/res/menu/webview_copy.xml index 2761fec1487c..ecb82b33b412 100644 --- a/core/res/res/menu/webview_copy.xml +++ b/core/res/res/menu/webview_copy.xml @@ -15,6 +15,10 @@ --> <menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/select_all" + android:title="@string/selectAll" + android:showAsAction="ifRoom|withText" + /> <item android:id="@+id/copy" android:icon="?android:attr/actionModeCopyDrawable" android:title="@string/copy" @@ -25,10 +29,6 @@ android:title="@string/share" android:showAsAction="ifRoom|withText" /> - <item android:id="@+id/select_all" - android:title="@string/selectAll" - android:showAsAction="ifRoom|withText" - /> <item android:id="@+id/find" android:icon="@drawable/ic_menu_find" android:title="@string/find" diff --git a/core/res/res/raw-es/incognito_mode_start_page.html b/core/res/res/raw-es/incognito_mode_start_page.html new file mode 100644 index 000000000000..43fd37f48840 --- /dev/null +++ b/core/res/res/raw-es/incognito_mode_start_page.html @@ -0,0 +1,26 @@ +<html> + <head> + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/> + <title>Nueva ventana de incógnito</title> + </head> + <body> + <p><strong>Has entrado en el modo "Navegar de incógnito"</strong>. Las páginas + que consultes a través de esta ventana no quedarán registradas en el historial del navegador + ni en el historial de búsquedas, y tampoco dejarán otros rastros en el equipo (como cookies) + una vez cerrada. Aunque sà quedarán almacenados los archivos que descargues y los marcadores + que guardes durante la sesión.</p> + + <p><strong>La función "Navegar de incógnito" no afecta al comportamiento de + otros usuarios, servidores o programas. Atención con:</strong></p> + + <ul> + <li>sitios web que recopilan o comparten información personal,</li> + <li>proveedores de servicios de Internet o trabajadores de estas empresas que + supervisan las páginas que visita el usuario,</li> + <li>software malicioso que realiza un seguimiento de las teclas que pulsa el usuario a + cambio de unos emoticones gratuitos,</li> + <li>el seguimiento por parte de detectives privados,</li> + <li>personas merodeando cerca de tu equipo.</li> + </ul> + </body> +</html> diff --git a/core/res/res/raw/incognito_mode_start_page.html b/core/res/res/raw/incognito_mode_start_page.html new file mode 100644 index 000000000000..5d7a3fbf98c0 --- /dev/null +++ b/core/res/res/raw/incognito_mode_start_page.html @@ -0,0 +1,24 @@ +<html> + <head> + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/> + <title>New incognito window</title> + </head> + <body> + <p><strong>You've gone incognito</strong>. Pages you view in this window + won't appear in your browser history or search history, and they won't + leave other traces, like cookies, on your device after you close the + incognito window. Any files you download or bookmarks you create will be + preserved, however.</p> + + <p><strong>Going incognito doesn't affect the behavior of other people, + servers, or software. Be wary of:</strong></p> + + <ul> + <li>Websites that collect or share information about you</li> + <li>Internet service providers or employers that track the pages you visit</li> + <li>Malicious software that tracks your keystrokes in exchange for free smileys</li> + <li>Surveillance by secret agents</li> + <li>People standing behind you</li> + </ul> + </body> +</html> diff --git a/core/res/res/values-es-rUS-xlarge/strings.xml b/core/res/res/values-es-rUS-xlarge/strings.xml new file mode 100644 index 000000000000..b1409baaf16c --- /dev/null +++ b/core/res/res/values-es-rUS-xlarge/strings.xml @@ -0,0 +1,337 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- XL --> + <string name="fileSizeSuffix" msgid="3468563433835560758">"Segmento <xliff:g id="NUMBER">%1$s</xliff:g><xliff:g id="UNIT">%2$s</xliff:g>"</string> + <!-- XL --> + <string name="unknownName" msgid="3202822008051920747">"(Desconocido)"</string> + <!-- XL --> + <string name="defaultVoiceMailAlphaTag" msgid="3668436100965334106">"Buzón de voz"</string> + <!-- XL --> + <string name="serviceClassVoice" msgid="7086876533404179039">"Google Voice"</string> + <!-- XL --> + <string name="cfTemplateNotForwarded" msgid="8534356655497306518">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no se ha reenviado"</string> + <!-- XL --> + <string name="cfTemplateRegistered" msgid="1255841210142514510">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no se ha reenviado"</string> + <!-- XL --> + <string name="cfTemplateRegisteredTime" msgid="7798907169190952367">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no se ha reenviado"</string> + <!-- XL --> + <string name="notification_title" msgid="5210128823045542445">"Error al acceder a <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string> + <!-- XL --> + <string name="low_memory" product="tablet" msgid="4855646606241379548">"¡El espacio de almacenamiento de la tableta está completo! Elimina algunos archivos para liberar espacio."</string> + <string name="low_memory" product="default" msgid="9195238880281578473">"¡El espacio de almacenamiento está completo! Elimina algunos archivos para liberar espacio."</string> + <!-- XL --> + <string name="power_dialog" product="tablet" msgid="6884163545695410971">"Opciones de tableta"</string> + <string name="power_dialog" product="default" msgid="8882103237148972564">"Opciones de teléfono"</string> + <!-- XL --> + <string name="silent_mode" msgid="5687977677409351252">"Modo silencio"</string> + <!-- XL --> + <string name="shutdown_confirm" product="tablet" msgid="5776903973889956395">"Tu tableta se apagará."</string> + <string name="shutdown_confirm" product="default" msgid="3040950969577046278">"Tu teléfono se apagará."</string> + <!-- XL --> + <string name="global_actions" product="tablet" msgid="110297659383505180">"Opciones de tableta"</string> + <string name="global_actions" product="default" msgid="2108237350837066773">"Opciones de teléfono"</string> + <!-- XL --> + <string name="global_action_toggle_silent_mode" msgid="4538951049191334644">"Modo silencio"</string> + <!-- XL --> + <string name="global_action_silent_mode_off_status" msgid="9045822172493147761">"El sonido está ENCENDIDO"</string> + <!-- XL --> + <string name="global_actions_airplane_mode_on_status" msgid="7272433204482202219">"El modo avión está ENCENDIDO"</string> + <!-- XL --> + <string name="android_system_label" msgid="844561213652704593">"Sistema Androide"</string> + <!-- XL --> + <string name="permgroupdesc_costMoney" msgid="4836624191696189469">"Admitir que las aplicaciones realicen actividades que se cobran."</string> + <!-- XL --> + <string name="permgroupdesc_developmentTools" msgid="5514251182135739578">"Las funciones sólo son necesarias para los programadores de aplicaciones."</string> + <!-- XL --> + <string name="permgrouplab_storage" msgid="746210798053836644">"Almacenamiento"</string> + <!-- XL --> + <string name="permdesc_readSms" product="tablet" msgid="3026416194429353337">"Permite que la aplicación lea los mensajes SMS almacenados en tu tableta o tarjeta SIM. Las aplicaciones maliciosas pueden leer tus mensajes confidenciales."</string> + <string name="permdesc_readSms" product="default" msgid="191875931331016383">"Admite que la aplicación lea los mensajes SMS almacenados en tu teléfono o tarjeta SIM. Las aplicaciones maliciosas pueden leer tus mensajes confidenciales."</string> + <!-- XL --> + <string name="permdesc_writeSms" product="tablet" msgid="692041754996169941">"Permite que la aplicación escriba a los mensajes SMS almacenados en tu tableta o tarjeta SIM. Las aplicaciones maliciosas pueden borrar tus mensajes."</string> + <string name="permdesc_writeSms" product="default" msgid="1659315878254882599">"Admite que la aplicación escriba a los mensajes SMS almacenados en tu teléfono o tarjeta SIM. Las aplicaciones maliciosas pueden borrar tus mensajes."</string> + <!-- XL --> + <string name="permlab_forceStopPackages" msgid="1277034765943155677">"provocar la detención de otras aplicaciones"</string> + <!-- XL --> + <string name="permlab_forceBack" msgid="4272218642115232597">"cerrar la aplicación a la fuerza"</string> + <!-- XL --> + <string name="permdesc_injectEvents" product="tablet" msgid="6096352450860864899">"Permite que una aplicación ofrezca sus propios eventos de entrada (presionar teclas, etc.) a otras aplicaciones. Las aplicaciones maliciosas pueden utilizarlo para tomar el control de la tableta."</string> + <string name="permdesc_injectEvents" product="default" msgid="2842435693076075109">"Admite una aplicación que ofrece sus propios eventos de entrada (presionar teclas, etc.) a otras aplicaciones. Las aplicaciones maliciosas pueden utilizarlo para tomar el control del teléfono."</string> + <!-- XL --> + <string name="permdesc_clearAppCache" product="tablet" msgid="1147333973960547529">"Permite que una aplicación libere espacio de almacenamiento en la tableta eliminando archivos del directorio de memoria caché de la aplicación. En general, el acceso es muy restringido para el proceso del sistema."</string> + <string name="permdesc_clearAppCache" product="default" msgid="5790679870501740958">"Admite una aplicación que libera espacio de almacenamiento en el teléfono al eliminar archivos del directorio de memoria caché de la aplicación. En general, el acceso es muy restringido para el proceso del sistema."</string> + <!-- XL --> + <string name="permdesc_readLogs" product="tablet" msgid="3701009088710926065">"Permite que una aplicación lea diversos archivos de registro del sistema. Esto le permite descubrir información general acerca de lo que haces con la tableta, y puede potencialmente incluir información personal o privada."</string> + <string name="permdesc_readLogs" product="default" msgid="8520101632251038537">"Admite una aplicación que lee diversos archivos de registro del sistema. Esto te permite descubrir información general acerca de lo que haces con el teléfono, y puede potencialmente incluir información personal o privada."</string> + <!-- XL --> + <string name="permdesc_changeComponentState" product="tablet" msgid="1791075936446230356">"Permite que una aplicación cambie si se debe activar o no un componente de otra aplicación. Las aplicaciones maliciosas pueden utilizarlo para desactivar funciones importantes de la tableta. Se debe tener cuidado con el permiso, ya que es posible que los componentes de la aplicación alcancen un estado inservible, imperfecto e inestable."</string> + <string name="permdesc_changeComponentState" product="default" msgid="587130297076242796">"Permite que una aplicación cambie si se debe activar o no un componente de otra aplicación. Las aplicaciones maliciosas pueden utilizarlo para desactivar funciones importantes del teléfono. Se debe tener cuidado con el permiso, ya que es posible que los componentes de la aplicación alcancen un estado inservible, imperfecto e inestable."</string> + <!-- XL --> + <string name="permdesc_receiveBootCompleted" product="tablet" msgid="8660405432665162821">"Permite que una aplicación se inicie en cuanto el sistema haya finalizado la inicialización. Esto puede ocasionar que la tableta demore más en inicializar y que la aplicación retarde el funcionamiento total de la tableta al estar en ejecución constante."</string> + <string name="permdesc_receiveBootCompleted" product="default" msgid="1827765096700833418">"Admite una aplicación que se inicia cuando el sistema haya finalizado la inicialización. Esto puede ocasionar que se demore más tiempo en inicializar el teléfono y que la aplicación retarde el funcionamiento total del teléfono al estar en ejecución constante."</string> + <!-- XL --> + <string name="permdesc_readContacts" product="tablet" msgid="1611730857475623952">"Permite que una aplicación lea todos los datos de de contacto (direcciones) almacenados en tu tableta. Las aplicaciones maliciosas pueden utilizarlo para enviar tus datos a otras personas."</string> + <string name="permdesc_readContacts" product="default" msgid="6610535719925788049">"Admite una aplicación que lee todos los datos de (direcciones) de contactos almacenados en tu teléfono. Las aplicaciones maliciosas pueden utilizarlo para enviar tus eventos de calendario a otras personas."</string> + <!-- XL --> + <string name="permdesc_writeContacts" product="tablet" msgid="4572703488642353934">"Permite que una aplicación modifique los datos de (dirección) guardados en tu tableta. Las aplicaciones maliciosas pueden utilizarlo para borrar o modificar los datos de contacto."</string> + <string name="permdesc_writeContacts" product="default" msgid="714397557711969040">"Admite una aplicación que modifica los datos de (dirección de) contacto guardados en tu teléfono. Las aplicaciones maliciosas pueden utilizarlo para borrar o modificar los datos de contacto."</string> + <!-- XL --> + <string name="permdesc_readCalendar" product="tablet" msgid="2991522150157238929">"Permite que una aplicación lea todos los eventos de calendario almacenados en tu tableta. Las aplicaciones maliciosas pueden utilizarlo para enviar tus eventos de calendario a otras personas."</string> + <string name="permdesc_readCalendar" product="default" msgid="2618681024074734985">"Admite que una aplicación lea todos los eventos de calendario almacenados en tu teléfono. Las aplicaciones maliciosas pueden utilizarlo para enviar tus eventos de calendario a otras personas."</string> + <!-- XL --> + <string name="permdesc_accessFineLocation" product="tablet" msgid="9186984659787705379">"Accede a las fuentes de ubicación precisa, como el Sistema de posicionamiento global en la tableta, si está disponible. Las aplicaciones maliciosas pueden utilizarlo para determinar donde te encuentras y puede consumir energÃa adicional de la baterÃa."</string> + <string name="permdesc_accessFineLocation" product="default" msgid="7130852247133907221">"Accede a las fuentes de ubicación precisa, como el Sistema de posicionamiento global en el teléfono, si está disponible. Las aplicaciones maliciosas pueden utilizarlo para determinar donde te encuentras y puede consumir energÃa adicional de la baterÃa."</string> + <!-- XL --> + <string name="permdesc_accessCoarseLocation" product="tablet" msgid="2943949975553225591">"Accede a las fuentes de ubicación aproximada, como la base de datos de la red de celulares, para determinar la ubicación aproximada de un tableta, si está disponible. Las aplicaciones maliciosas pueden utilizarlo para determinar aproximadamente dónde te encuentras."</string> + <string name="permdesc_accessCoarseLocation" product="default" msgid="7474972764638621839">"Accede a las fuentes de ubicación aproximada, como la base de datos de la red de celulares, para determinar una ubicación telefónica aproximada, si está disponible. Las aplicaciones maliciosas pueden utilizarlo para determinar aproximadamente donde te encuentras."</string> + <!-- XL --> + <string name="permlab_brick" product="tablet" msgid="6967130388106614085">"inhabilitar tableta de forma permanente"</string> + <string name="permlab_brick" product="default" msgid="3120283238813720510">"desactivar teléfono de manera permanente"</string> + <!-- XL --> + <string name="permdesc_brick" product="tablet" msgid="8506097851567246888">"Permite que la aplicación desactive todo la tableta de manera permanente. Esto es muy peligroso."</string> + <string name="permdesc_brick" product="default" msgid="6696459767254028146">"Admite que la aplicación desactive todo el teléfono de manera permanente. Esto es muy peligroso."</string> + <!-- XL --> + <string name="permlab_reboot" product="tablet" msgid="8299304590708874992">"forzar reinicio de la tableta"</string> + <string name="permlab_reboot" product="default" msgid="7761230490609718232">"provocar el reinicio del teléfono"</string> + <!-- XL --> + <string name="permdesc_reboot" product="tablet" msgid="8289402537687518137">"Permite que la aplicación provoque el reinicio de la tableta."</string> + <string name="permdesc_reboot" product="default" msgid="2425170170087532554">"Admite que la aplicación provoque que el teléfono se reinicie."</string> + <!-- XL --> + <string name="permlab_performCdmaProvisioning" product="tablet" msgid="1602175938040327630">"iniciar directamente la configuración CDMA de la tableta"</string> + <string name="permlab_performCdmaProvisioning" product="default" msgid="2364447039211144234">"comienza directamente la configuración CDMA del teléfono"</string> + <!-- XL --> + <string name="permlab_checkinProperties" msgid="8770356116386811264">"acceder a las propiedades de registro"</string> + <!-- XL --> + <string name="permlab_bindGadget" msgid="2772444448613501375">"elegir controles"</string> + <!-- XL --> + <string name="permdesc_bindGadget" msgid="5172327215211875807">"Admite que la aplicación indique al sistema cuáles controles puede utilizar cada aplicación. Con este permiso, las aplicaciones pueden brindar acceso a los datos personales a otras aplicaciones. Las aplicaciones normales no deben utilizarlo."</string> + <!-- XL --> + <string name="permlab_wakeLock" product="tablet" msgid="8548785337425173690">"evitar que la tableta entre en estado de inactividad"</string> + <string name="permlab_wakeLock" product="default" msgid="7590534090355174805">"evitar que el teléfono entre en estado de inactividad"</string> + <!-- XL --> + <string name="permdesc_wakeLock" product="tablet" msgid="6871828582124115814">"Permite que una aplicación evite que la tableta entre en estado de inactividad."</string> + <string name="permdesc_wakeLock" product="default" msgid="1200311528451468554">"Admite una aplicación que evita que el teléfono entre en estado de inactividad."</string> + <!-- XL --> + <string name="permlab_devicePower" product="tablet" msgid="4737873025369971061">"apagar o encender la tableta"</string> + <string name="permlab_devicePower" product="default" msgid="6879460773734563850">"apagar o encender el teléfono"</string> + <!-- XL --> + <string name="permdesc_devicePower" product="tablet" msgid="5930342678996327905">"Permite que una aplicación encienda o apague la tableta."</string> + <string name="permdesc_devicePower" product="default" msgid="6653901512148320818">"Admite que la aplicación encienda o apague el teléfono."</string> + <!-- XL --> + <string name="permdesc_factoryTest" product="tablet" msgid="396653994609190055">"Se ejecuta como una prueba de fábrica de bajo nivel que permite un acceso completo al hardware de la tableta. Sólo disponible cuando la tableta se ejecuta en el modo de prueba de fábrica."</string> + <string name="permdesc_factoryTest" product="default" msgid="4581239666568781766">"Se ejecuta como una prueba de fábrica de bajo nivel que permite un acceso completo al hardware del teléfono. Sólo disponible cuando un teléfono se ejecuta en el modo de prueba de fábrica."</string> + <!-- XL --> + <string name="permlab_setWallpaper" msgid="845032615203772571">"establecer fondo de pantalla"</string> + <!-- XL --> + <string name="permdesc_setWallpaper" msgid="3378501759667797259">"Admite que la aplicación establezca el fondo de pantalla del sistema."</string> + <!-- XL --> + <string name="permlab_setWallpaperHints" msgid="4995885499848128983">"establecer sugerencias de tamaño del fondo de pantalla"</string> + <!-- XL --> + <string name="permdesc_setWallpaperHints" msgid="8857901708691279048">"Admite que la aplicación establezca las sugerencias de tamaño del fondo de pantalla del sistema."</string> + <!-- XL --> + <string name="permdesc_setTime" product="tablet" msgid="7329574196603775554">"Permite que una aplicación cambie la hora de la tableta."</string> + <string name="permdesc_setTime" product="default" msgid="7787175369529849526">"Permite a una aplicación cambiar la hora del teléfono."</string> + <!-- XL --> + <string name="permdesc_setTimeZone" product="tablet" msgid="3851480395450283316">"Permite que una aplicación cambie la zona horaria de la tableta."</string> + <string name="permdesc_setTimeZone" product="default" msgid="3231143515254577541">"Admite una aplicación que cambia la zona horaria del teléfono."</string> + <!-- XL --> + <string name="permdesc_getAccounts" product="tablet" msgid="374861616407073729">"Permite que una aplicación obtenga una la lista de cuentas conocidas por la tableta."</string> + <string name="permdesc_getAccounts" product="default" msgid="6356501268884684429">"Admite una aplicación que obtiene la lista de cuentas conocidas del teléfono."</string> + <!-- XL --> + <string name="permdesc_bluetoothAdmin" product="tablet" msgid="8034248164659819866">"Permite que una aplicación configure el Bluetooth local de la tableta, y descubra y se vincule con dispositivos remotos."</string> + <string name="permdesc_bluetoothAdmin" product="default" msgid="2555370145147752776">"Admite una aplicación que configura el teléfono Bluetooth local y descubre y se vincula con dispositivos remotos."</string> + <!-- XL --> + <string name="permdesc_bluetooth" product="tablet" msgid="4631562404621086816">"Permite que una aplicación vea la configuración de la tableta Bluetooth local, y que realice y acepte conexiones con dispositivos vinculados."</string> + <string name="permdesc_bluetooth" product="default" msgid="1202135959389935958">"Admite una aplicación que ve la configuración del teléfono Bluetooth local, y realiza y acepta conexiones con dispositivos vinculados."</string> + <!-- XL --> + <string name="policydesc_watchLogin" product="tablet" msgid="7927990389488709968">"Supervisar el número de contraseñas incorrectas ingresadas al desbloquear la pantalla, y bloquear la tableta o eliminar todos los datos del teléfono si se ingresan demasiadas contraseñas incorrectas."</string> + <string name="policydesc_watchLogin" product="default" msgid="4998594853332798741">"Supervisa el número de contraseñas incorrectas ingresadas al desbloquear la pantalla, y bloquee el teléfono o elimine todos los datos del teléfono si se ingresan demasiadas contraseñas incorrectas."</string> + <!-- XL --> + <string name="policydesc_wipeData" product="tablet" msgid="7871059407132175855">"Borrar los datos de la tableta sin advertencias, restableciendo la configuración de fábrica"</string> + <string name="policydesc_wipeData" product="default" msgid="6003127471292136411">"Borrar los datos del teléfono sin advertencias al restablecer la configuración original"</string> + <!-- XL --> + <string-array name="phoneTypes"> + <item msgid="7066790683658405096">"Pantalla principal"</item> + <item msgid="5813675571320075289">"Teléfono móvil"</item> + <item msgid="1236863745322977021">"Trabajo"</item> + <item msgid="7018038125868933566">"Fax laboral"</item> + <item msgid="4280105707643078852">"Fax personal"</item> + <item msgid="6527083287534782580">"Localizador"</item> + <item msgid="706618935041239888">"Otro"</item> + <item msgid="8099625332540070724">"Personalizado"</item> + </string-array> + <!-- XL --> + <string-array name="emailAddressTypes"> + <item msgid="8080673853442355385">"Pantalla principal"</item> + <item msgid="924798042157989715">"Trabajo"</item> + <item msgid="1959796935508361158">"Otro"</item> + <item msgid="756534161520555926">"Personalizado"</item> + </string-array> + <!-- XL --> + <string-array name="postalAddressTypes"> + <item msgid="1166454994471190496">"Pantalla principal"</item> + <item msgid="3602955376664951787">"Trabajo"</item> + <item msgid="4646105398231575508">"Otro"</item> + <item msgid="8191179302220976184">"Personalizado"</item> + </string-array> + <!-- XL --> + <string-array name="imAddressTypes"> + <item msgid="2528436635522549040">"Pantalla principal"</item> + <item msgid="5834207144511084508">"Trabajo"</item> + <item msgid="3796683891024584813">"Otro"</item> + <item msgid="6644316676098098833">"Personalizado"</item> + </string-array> + <!-- XL --> + <string-array name="organizationTypes"> + <item msgid="6571823895277482483">"Trabajo"</item> + <item msgid="4013674940836786104">"Otro"</item> + <item msgid="8549998141814637453">"Personalizado"</item> + </string-array> + <!-- XL --> + <string name="phoneTypeHome" msgid="2087652870939635038">"Pantalla principal"</string> + <!-- XL --> + <string name="phoneTypeMobile" msgid="7084573626440935140">"Teléfono móvil"</string> + <!-- XL --> + <string name="emailTypeHome" msgid="1298773522695936612">"Pantalla principal"</string> + <!-- XL --> + <string name="emailTypeMobile" msgid="5515624509217674980">"Teléfono móvil"</string> + <!-- XL --> + <string name="postalTypeHome" msgid="7553888805834710738">"Pantalla principal"</string> + <!-- XL --> + <string name="imTypeHome" msgid="3732426015472142690">"Pantalla principal"</string> + <!-- XL --> + <string name="sipAddressTypeHome" msgid="8212230577724692911">"Pantalla principal"</string> + <!-- XL --> + <string name="lockscreen_pattern_instructions" msgid="9171665895877154059">"Extraer el patrón para desbloquear"</string> + <!-- XL --> + <string name="lockscreen_battery_short" msgid="891372653127247039">"Segmento <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string> + <!-- XL --> + <string name="lockscreen_missing_sim_message" product="tablet" msgid="3961770350078423154">"No hay tarjeta SIM en la tableta."</string> + <string name="lockscreen_missing_sim_message" product="default" msgid="5997031739677800758">"No hay tarjeta SIM en el teléfono."</string> + <!-- XL --> + <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="2429599468920598896">"Has establecido incorrectamente tu gráfico de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Vuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> + <!-- XL --> + <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3211267232692817092">"Has establecido incorrectamente tu gráfico de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, se te solicitará que desbloquees tu tableta al acceder a Google."\n\n" Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string> + <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="7097890594752816076">"Has establecido incorrectamente tu gráfico de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos, se te solicitará que desbloquees tu teléfono al acceder a Google. "\n\n" Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string> + <!-- XL --> + <string name="lockscreen_glogin_submit_button" msgid="4760302858316749698">"Acceder"</string> + <!-- XL --> + <string name="lockscreen_glogin_invalid_input" msgid="7265806099449246244">"Nombre de usuario o contraseña no válidos."</string> + <!-- XL --> + <string name="hour_ampm" msgid="6161399724998500216">"Segmento <xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string> + <!-- XL --> + <string name="hour_cap_ampm" msgid="724197720606114012">"Segmento <xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string> + <!-- XL --> + <string name="double_tap_toast" msgid="2893001600485832537">"Sugerencia: presiona dos veces para acercar y alejar"</string> + <!-- XL --> + <string name="autofill_address_name_separator" msgid="5171727678145785075">" Segmento "</string> + <!-- XL --> + <string name="permlab_readHistoryBookmarks" msgid="6148149152792104516">"leer historial y favoritos del navegador"</string> + <!-- XL --> + <string name="permdesc_readHistoryBookmarks" msgid="7371336472744100059">"Permite a la aplicación leer todas las URL que ha visitado el navegador y todos los favoritos del navegador."</string> + <!-- XL --> + <string name="permlab_writeHistoryBookmarks" msgid="1369319390968848231">"escribir historial y favoritos del navegador"</string> + <!-- XL --> + <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="3870229397949634482">"Permite que una aplicación modifique el historial de navegación y los favoritos del navegador almacenados en tu tableta. Las aplicaciones maliciosas pueden utilizarlo para borrar o modificar los datos en tu navegador."</string> + <string name="permdesc_writeHistoryBookmarks" product="default" msgid="6845659334691579933">"Permite a una aplicación modificar el historial y los favoritos del navegador almacenados en tu teléfono. Las aplicaciones maliciosas pueden utilizarlo para borrar o modificar tus datos."</string> + <!-- XL --> + <string name="permlab_setAlarm" msgid="8112208516527103653">"fija la alarma en el reloj de alarma"</string> + <!-- XL --> + <string name="permdesc_setAlarm" msgid="5454386032150297784">"Permite a la aplicación fijar una alarma en una aplicación de alarma. Es posible que algunas aplicaciones de alarma no implementen esta función."</string> + <!-- XL --> + <string name="menu_delete_shortcut_label" msgid="8482704027019632634">"eliminar"</string> + <!-- XL --> + <plurals name="num_minutes_ago"> + <item quantity="one" msgid="468685153446407901">"hace 1 minuto"</item> + <item quantity="other" msgid="211907662145171054">"Hace <xliff:g id="COUNT">%d</xliff:g> minutos"</item> + </plurals> + <!-- XL --> + <plurals name="num_hours_ago"> + <item quantity="one" msgid="2172827344495633666">"hace 1 hora"</item> + <item quantity="other" msgid="6094391999921908511">"Hace <xliff:g id="COUNT">%d</xliff:g> horas"</item> + </plurals> + <!-- XL --> + <plurals name="num_days_ago"> + <item quantity="one" msgid="3766494702684657165">"ayer"</item> + <item quantity="other" msgid="5030316952487658828">"Hace <xliff:g id="COUNT">%d</xliff:g> dÃas"</item> + </plurals> + <!-- XL --> + <plurals name="abbrev_num_seconds_ago"> + <item quantity="one" msgid="1441918190525197797">"hace 1 s"</item> + <item quantity="other" msgid="3958332340802316933">"hace <xliff:g id="COUNT">%d</xliff:g> segundos"</item> + </plurals> + <!-- XL --> + <plurals name="abbrev_num_minutes_ago"> + <item quantity="one" msgid="3404245071272952255">"hace 1 min"</item> + <item quantity="other" msgid="6004808520903389765">"hace <xliff:g id="COUNT">%d</xliff:g> min"</item> + </plurals> + <!-- XL --> + <plurals name="abbrev_num_hours_ago"> + <item quantity="one" msgid="806010152744475654">"hace 1 hora"</item> + <item quantity="other" msgid="7553525762196895290">"Hace <xliff:g id="COUNT">%d</xliff:g> horas"</item> + </plurals> + <!-- XL --> + <plurals name="abbrev_num_days_ago"> + <item quantity="one" msgid="5819444260187611238">"ayer"</item> + <item quantity="other" msgid="1069986768190052012">"Hace <xliff:g id="COUNT">%d</xliff:g> dÃas"</item> + </plurals> + <!-- XL --> + <string name="preposition_for_time" msgid="3606608741888559522">"a la/s <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- XL --> + <string name="minutes" msgid="1486240209627391507">"min"</string> + <!-- XL --> + <string name="selectAll" msgid="847570914566450966">"Seleccionar todos"</string> + <!-- XL --> + <string name="low_internal_storage_view_text" product="tablet" msgid="6497548813789342134">"Está quedando poco espacio de almacenamiento en la tableta."</string> + <string name="low_internal_storage_view_text" product="default" msgid="2901569701336868928">"Hay poco espacio de almacenamiento en el teléfono."</string> + <!-- XL --> + <string name="capital_on" msgid="5705918046896729554">"ENCENDIDO"</string> + <!-- XL --> + <string name="wait" msgid="8036803866051401072">"Espera"</string> + <!-- XL --> + <string name="heavy_weight_notification" msgid="5762367358298413602">"<xliff:g id="APP">%1$s</xliff:g> se está ejecutando"</string> + <!-- XL --> + <string name="ext_media_checking_notification_title" product="nosdcard" msgid="103298639852047758">"Preparando almacenamiento USB"</string> + <string name="ext_media_checking_notification_title" product="default" msgid="2111086053471573248">"Preparando la tarjeta SD"</string> + <!-- XL --> + <string name="ime_action_done" msgid="7200237418945571897">"Listo"</string> + <!-- XL --> + <string name="wallpaper_binding_label" msgid="6966627494441714436">"Fondo de pantalla"</string> + <!-- XL --> + <string name="websearch" msgid="904596193450917688">"Búsqueda web"</string> + <!-- XL --> + <string name="status_bar_notification_info_overflow" msgid="1081154808901480710">"100+"</string> + <!-- XL --> + <string name="permlab_accessMtp" msgid="2385215229145694622">"implementar protocolo MTP"</string> + <!-- XL --> + <string name="permdesc_accessMtp" msgid="4707854877711083465">"Permite acceso al driver kernel MTP para implementar el protocolo MTP USB."</string> + <!-- XL --> + <string name="permlab_mediaStorageWrite" product="default" msgid="5585262071354704256">"modificar/eliminar los contenidos del almacenamientos de medios internos"</string> + <!-- XL --> + <string name="permdesc_mediaStorageWrite" product="default" msgid="2372999661142345443">"Permite que una aplicación modifique los contenidos del almacenamiento interno de medios."</string> + <!-- XL --> + <string name="autofill_address_summary_name_format" msgid="7531610259426153850">"$1$2$3"</string> + <!-- XL --> + <string name="autofill_address_summary_format" msgid="8398158823767723887">"$1$2$3"</string> + <!-- XL --> + <string name="gpsNotifTicker" msgid="6612390321359669319">"Solicitud de ubicación de <xliff:g id="NAME">%s</xliff:g>"</string> + <!-- XL --> + <string name="gpsNotifTitle" msgid="7533028619350196545">"Solicitud de ubicación"</string> + <!-- XL --> + <string name="gpsNotifMessage" msgid="5592972401593755530">"Solicitado por <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string> + <!-- XL --> + <string name="gpsVerifYes" msgid="1511016393202739483">"SÃ"</string> + <!-- XL --> + <string name="gpsVerifNo" msgid="661731239940896232">"No"</string> + <!-- XL --> + <string name="sync_too_many_deletes" msgid="6088394702274114202">"Eliminar el lÃmite excedido"</string> + <!-- XL --> + <string name="sync_too_many_deletes_desc" msgid="4794082462774743277">"Existen <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> artÃculos eliminados para <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, cuenta <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. ¿Qué te gustarÃa hacer?"</string> + <!-- XL --> + <string name="sync_really_delete" msgid="7782215155483034729">"Eliminar artÃculos."</string> + <!-- XL --> + <string name="sync_undo_deletes" msgid="6501390120900825477">"Deshacer eliminaciones."</string> + <!-- XL --> + <string name="sync_do_nothing" msgid="612038572646360281">"No hagas nada por el momento."</string> +</resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index f9809709fa55..6f37dc097010 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -778,6 +778,9 @@ <!-- Color of link text (URLs). --> <attr name="textColorLink" format="reference|color" /> + <!-- Reference to a drawable that will be drawn under the insertion cursor. --> + <attr name="textCursorDrawable" format="reference" /> + <!-- Indicates that the content of a non-editable TextView can be selected. Default value is false. EditText content is always selectable. --> <attr name="textIsSelectable" format="boolean" /> @@ -1345,6 +1348,22 @@ <enum name="KEYCODE_PROG_YELLOW" value="185" /> <enum name="KEYCODE_PROG_BLUE" value="186" /> <enum name="KEYCODE_APP_SWITCH" value="187" /> + <enum name="KEYCODE_BUTTON_1" value="188" /> + <enum name="KEYCODE_BUTTON_2" value="189" /> + <enum name="KEYCODE_BUTTON_3" value="190" /> + <enum name="KEYCODE_BUTTON_4" value="191" /> + <enum name="KEYCODE_BUTTON_5" value="192" /> + <enum name="KEYCODE_BUTTON_6" value="193" /> + <enum name="KEYCODE_BUTTON_7" value="194" /> + <enum name="KEYCODE_BUTTON_8" value="195" /> + <enum name="KEYCODE_BUTTON_9" value="196" /> + <enum name="KEYCODE_BUTTON_10" value="197" /> + <enum name="KEYCODE_BUTTON_11" value="198" /> + <enum name="KEYCODE_BUTTON_12" value="199" /> + <enum name="KEYCODE_BUTTON_13" value="200" /> + <enum name="KEYCODE_BUTTON_14" value="201" /> + <enum name="KEYCODE_BUTTON_15" value="202" /> + <enum name="KEYCODE_BUTTON_16" value="203" /> </attr> <!-- ***************************************************************** --> @@ -2767,6 +2786,9 @@ <!-- Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. --> <attr name="textEditSideNoPasteWindowLayout" /> + <!-- Reference to a drawable that will be drawn under the insertion cursor. --> + <attr name="textCursorDrawable" /> + <!-- Indicates that the content of a non-editable text can be selected. --> <attr name="textIsSelectable" /> </declare-styleable> @@ -3130,11 +3152,6 @@ <attr name="layout" /> </declare-styleable> - <declare-styleable name="DatePicker"> - <!-- @hide The layout of the time picker. --> - <attr name="layout" /> - </declare-styleable> - <!-- ========================= --> <!-- Drawable class attributes --> <!-- ========================= --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 713fa8e96284..0edd33e35c86 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -284,6 +284,14 @@ <!-- Indicate whether the device has USB host support. --> <bool name="config_hasUsbHostSupport">false</bool> + <!-- List of file paths for USB host busses to exclude from USB host support. + For example, if the first USB bus on the device is used to communicate + with the modem or some other restricted hardware, add "/dev/bus/usb/001/" + to this list. If this is empty, no parts of the host USB bus will be excluded. + --> + <string-array name="config_usbHostBlacklist"> + </string-array> + <!-- Vibrator pattern for feedback about a long screen/key press --> <integer-array name="config_longPressVibePattern"> <item>0</item> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index 7a0fede01dd9..d05685c6cecd 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -73,4 +73,5 @@ <item type="id" name="fillInIntent" /> <item type="id" name="rowTypeId" /> <item type="id" name="up" /> + <item type="id" name="action_menu_divider" /> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index aaf071b329ec..454257522a09 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1642,4 +1642,7 @@ <!-- Default icon for applications that don't specify an icon. --> <public type="mipmap" name="sym_def_app_icon" id="0x010d0000" /> + <!-- Theme attribute used to customize the text insertion cursor --> + <!-- Commented out for HC MR1 to prevent an API change --> + <!-- <public type="attr" name="textCursorDrawable" id="0x01010362" /> --> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index d09210e0fc46..361bb6c58e3a 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1413,10 +1413,10 @@ to be used while policy is enabled. Only the first device admin sets the effective global proxy.</string> <!-- Title of policy access to enforce password expiration [CHAR LIMIT=30]--> - <string name="policylab_expirePassword">Set password expiration</string> + <string name="policylab_expirePassword">Set lock-screen password expiration</string> <!-- Description of policy access to enforce password expiration [CHAR LIMIT=110]--> - <string name="policydesc_expirePassword">Control how long before lockscreen password needs to be - changed</string> + <string name="policydesc_expirePassword">Control how frequently the lock-screen password must be + changed</string> <!-- Title of policy access to require encrypted storage [CHAR LIMIT=30]--> <string name="policylab_encryptedStorage">Set storage encryption</string> <!-- Description of policy access to require encrypted storage [CHAR LIMIT=110]--> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 5700641f3986..8cc5944dc058 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -426,6 +426,7 @@ <item name="android:textEditNoPasteWindowLayout">?android:attr/textEditNoPasteWindowLayout</item> <item name="android:textEditSidePasteWindowLayout">?android:attr/textEditSidePasteWindowLayout</item> <item name="android:textEditSideNoPasteWindowLayout">?android:attr/textEditSideNoPasteWindowLayout</item> + <item name="android:textCursorDrawable">?android:attr/textCursorDrawable</item> </style> <style name="Widget.TextView.ListSeparator"> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 6d5b4822b1dc..927a668ac857 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -180,6 +180,7 @@ <item name="textEditNoPasteWindowLayout">@android:layout/text_edit_no_paste_window</item> <item name="textEditSidePasteWindowLayout">@android:layout/text_edit_side_paste_window</item> <item name="textEditSideNoPasteWindowLayout">@android:layout/text_edit_side_no_paste_window</item> + <item name="textCursorDrawable">@null</item> <!-- Widget styles --> <item name="absListViewStyle">@android:style/Widget.AbsListView</item> @@ -906,6 +907,7 @@ <item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item> <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item> <item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item> + <item name="textCursorDrawable">@android:drawable/text_cursor_holo_dark</item> <!-- Widget styles --> <item name="absListViewStyle">@android:style/Widget.Holo.AbsListView</item> @@ -1181,6 +1183,7 @@ <item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item> <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item> <item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item> + <item name="textCursorDrawable">@android:drawable/text_cursor_holo_light</item> <!-- Widget styles --> <item name="absListViewStyle">@android:style/Widget.Holo.Light.AbsListView</item> diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java index 21f1bfc29d51..3667c7be304b 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java @@ -46,8 +46,8 @@ import java.util.List; * <accesspoint></accesspoint>. The supported configuration includes: ssid, * security, eap, phase2, identity, password, anonymousidentity, cacert, usercert, * in which each is included in the corresponding tags. Static IP setting is also supported. - * Tags that can be used include: ip, gateway, netmask, dns1, dns2. All access points have to be - * enclosed in tags of <resources></resources>. + * Tags that can be used include: ip, gateway, networkprefixlength, dns1, dns2. All access points + * have to be enclosed in tags of <resources></resources>. * * The following is a sample configuration file for an access point using EAP-PEAP with MSCHAP2. * <resources> @@ -62,7 +62,8 @@ import java.util.List; * </resources> * * Note:ssid and security have to be the first two tags - * for static ip setting, tag "ip" should be listed before other fields: dns, gateway, netmask. + * for static ip setting, tag "ip" should be listed before other fields: dns, gateway, + * networkprefixlength. */ public class AccessPointParserHelper { private static final String KEYSTORE_SPACE = "keystore://"; @@ -106,7 +107,6 @@ public class AccessPointParserHelper { boolean ip = false; boolean gateway = false; boolean networkprefix = false; - boolean netmask = false; boolean dns1 = false; boolean dns2 = false; boolean eap = false; @@ -163,9 +163,6 @@ public class AccessPointParserHelper { if (tagName.equalsIgnoreCase("networkprefixlength")) { networkprefix = true; } - if (tagName.equalsIgnoreCase("netmask")) { - netmask = true; - } if (tagName.equalsIgnoreCase("dns1")) { dns1 = true; } @@ -303,7 +300,7 @@ public class AccessPointParserHelper { if (!InetAddress.isNumeric(gwAddr)) { throw new SAXException(); } - mLinkProperties.setGateway(InetAddress.getByName(gwAddr)); + mLinkProperties.addGateway(InetAddress.getByName(gwAddr)); } catch (UnknownHostException e) { throw new SAXException(); } @@ -321,19 +318,6 @@ public class AccessPointParserHelper { } networkprefix = false; } - if (netmask) { - try { - String netMaskStr = new String(ch, start, length); - if (!InetAddress.isNumeric(netMaskStr)) { - throw new SAXException(); - } - InetAddress netMaskAddr = InetAddress.getByName(netMaskStr); - mLinkProperties.addLinkAddress(new LinkAddress(mInetAddr, netMaskAddr)); - } catch (UnknownHostException e) { - throw new SAXException(); - } - netmask = false; - } if (dns1) { try { String dnsAddr = new String(ch, start, length); diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java index fb8b5ce2b916..775613504aaf 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java @@ -30,7 +30,9 @@ import android.provider.Settings; import android.util.Log; import android.view.KeyEvent; +import java.io.IOException; import java.io.InputStream; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import android.widget.LinearLayout; @@ -485,6 +487,44 @@ public class ConnectivityManagerTestActivity extends Activity { } /** + * @param pingServerList a list of servers that can be used for ping test, can be null + * @return true if the ping test is successful, false otherwise. + */ + public boolean pingTest(String[] pingServerList) { + boolean result = false; + String[] hostList = {"www.google.com", "www.yahoo.com", + "www.bing.com", "www.facebook.com", "www.ask.com"}; + if (pingServerList != null) { + hostList = pingServerList; + } + try { + // assume the chance that all servers are down is very small + for (int i = 0; i < hostList.length; i++ ) { + String host = hostList[i]; + log("Start ping test, ping " + host); + Process p = Runtime.getRuntime().exec("ping -c 10 -w 100 " + host); + int status = p.waitFor(); + if (status == 0) { + // if any of the ping test is successful, return true + result = true; + break; + } else { + result = false; + log("ping " + host + " failed."); + } + } + } catch (UnknownHostException e) { + log("Ping test Fail: Unknown Host"); + } catch (IOException e) { + log("Ping test Fail: IOException"); + } catch (InterruptedException e) { + log("Ping test Fail: InterruptedException"); + } + log("return"); + return result; + } + + /** * Associate the device to given SSID * If the device is already associated with a WiFi, disconnect and forget it, * We don't verify whether the connection is successful or not, leave this to the test diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java index 1655e2752b1e..b87021aeaab6 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java @@ -101,29 +101,39 @@ public class ConnectivityManagerMobileTest assertTrue("not connected to cellular network", extraNetInfo.isConnected()); } - // Test case 1: Test enabling Wifi without associating with any AP + // Test case 1: Test enabling Wifi without associating with any AP, no broadcast on network + // event should be expected. @LargeTest public void test3GToWifiNotification() { + // Enable Wi-Fi to avoid initial UNKNOWN state cmActivity.enableWifi(); try { Thread.sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT); } catch (Exception e) { Log.v(LOG_TAG, "exception: " + e.toString()); } - + // Wi-Fi is disabled cmActivity.disableWifi(); - cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, - State.DISCONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT); - // As Wifi stays in DISCONNETED, the connectivity manager will not broadcast - // any network connectivity event for Wifi + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, + State.DISCONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, + State.CONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); + // Wait for 10 seconds for broadcasts to be sent out + try { + Thread.sleep(10 * 1000); + } catch (Exception e) { + fail("thread in sleep is interrupted."); + } + // As Wifi stays in DISCONNETED, Mobile statys in CONNECTED, + // the connectivity manager will not broadcast any network connectivity event for Wifi NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(), NetworkState.DO_NOTHING, State.CONNECTED); networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), NetworkState.DO_NOTHING, State.DISCONNECTED); - // Eanble Wifi + // Eanble Wifi without associating with any AP cmActivity.enableWifi(); try { Thread.sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT); diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java index ea79f8c50c93..1374e7f8afef 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java @@ -107,7 +107,26 @@ public class WifiApStress } catch (Exception e) { fail("thread in sleep is interrupted"); } + assertTrue("no uplink data connection after Wi-Fi tethering", mAct.pingTest(null)); + // Wait for 5 minutes, and verify the data connection again. + // bug id: 3400027 + try { + Thread.sleep(5 * 60 * 1000); + } catch (Exception e) { + fail("thread in sleep is interrupted"); + } + // Verify the uplink data connection + assertTrue("no uplink data connection", mAct.pingTest(null)); + // Disable soft AP assertTrue(mAct.mWifiManager.setWifiApEnabled(config, false)); + // Wait for 30 seconds until Wi-Fi tethering is stopped + try { + Thread.sleep(30 * 1000); + Log.v(TAG, "wait for Wi-Fi tethering to be disabled."); + } catch (Exception e) { + fail("thread in sleep is interrupted"); + } + assertFalse("Wi-Fi AP disable failed", mAct.mWifiManager.isWifiApEnabled()); } if (i == iterations) { mLastIteration = iterations; diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java index ae009ca62460..2f2a28302dca 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java @@ -243,6 +243,9 @@ public class WifiStressTest ConnectivityManagerTestActivity.SHORT_TIMEOUT)); assertTrue(mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); + // Run ping test to verify the data connection + assertTrue("Wi-Fi is connected, but no data connection.", mAct.pingTest(null)); + int i; for (i = 0; i < mReconnectIterations; i++) { // 1. Put device into sleep mode @@ -271,6 +274,9 @@ public class WifiStressTest mAct.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState()); assertEquals("Cellular connection is down", State.CONNECTED, mAct.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState()); + + assertTrue("Mobile is connected, but no data connection.", mAct.pingTest(null)); + // Turn screen on again mAct.turnScreenOn(); assertTrue("Wait for Wi-Fi enable timeout after wake up", @@ -279,6 +285,7 @@ public class WifiStressTest assertTrue("Wait for Wi-Fi connection timeout after wake up", mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue("Reconnect to Wi-Fi network, but no data connection.", mAct.pingTest(null)); } if (i == mReconnectIterations) { writeOutput(String.format("iteration %d out of %d", diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 72120a89d499..01734f2f755a 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1210,6 +1210,13 @@ android:authorities="android.app.SuggestionProvider"> </provider> + <activity android:name="android.webkit.AccessibilityInjectorTestActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> + </intent-filter> + </activity> + </application> <instrumentation android:name="android.test.InstrumentationTestRunner" diff --git a/core/tests/coretests/res/layout/accessibility_injector_test.xml b/core/tests/coretests/res/layout/accessibility_injector_test.xml new file mode 100644 index 000000000000..7a34c9a465ac --- /dev/null +++ b/core/tests/coretests/res/layout/accessibility_injector_test.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<WebView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/webview" + android:layout_width="match_parent" + android:layout_height="match_parent" /> diff --git a/core/tests/coretests/src/android/database/DatabaseErrorHandlerTest.java b/core/tests/coretests/src/android/database/DatabaseErrorHandlerTest.java index 48d25b93d735..1cfd96003275 100644 --- a/core/tests/coretests/src/android/database/DatabaseErrorHandlerTest.java +++ b/core/tests/coretests/src/android/database/DatabaseErrorHandlerTest.java @@ -18,8 +18,10 @@ package android.database; import android.content.Context; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDiskIOException; import android.database.sqlite.SQLiteException; import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.Suppress; import android.util.Log; import java.io.BufferedWriter; @@ -60,6 +62,7 @@ public class DatabaseErrorHandlerTest extends AndroidTestCase { assertTrue(mDatabaseFile.exists()); } + public void testDatabaseIsCorrupt() throws IOException { mDatabase.execSQL("create table t (i int);"); // write junk into the database file @@ -72,9 +75,21 @@ public class DatabaseErrorHandlerTest extends AndroidTestCase { try { mDatabase.execSQL("select * from t;"); fail("expected exception"); - } catch (SQLiteException e) { + } catch (SQLiteDiskIOException e) { + /** + * this test used to produce a corrupted db. but with new sqlite it instead reports + * Disk I/O error. meh.. + * need to figure out how to cause corruption in db + */ // expected + if (mDatabaseFile.exists()) { + mDatabaseFile.delete(); + } + } catch (SQLiteException e) { + } + // database file should be gone + assertFalse(mDatabaseFile.exists()); // after corruption handler is called, the database file should be free of // database corruption SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null, diff --git a/core/tests/coretests/src/android/util/LruCacheTest.java b/core/tests/coretests/src/android/util/LruCacheTest.java new file mode 100644 index 000000000000..cf252e694496 --- /dev/null +++ b/core/tests/coretests/src/android/util/LruCacheTest.java @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import junit.framework.TestCase; + +public final class LruCacheTest extends TestCase { + private int expectedCreateCount; + private int expectedPutCount; + private int expectedHitCount; + private int expectedMissCount; + private int expectedEvictionCount; + + public void testStatistics() { + LruCache<String, String> cache = new LruCache<String, String>(3); + assertStatistics(cache); + + assertEquals(null, cache.put("a", "A")); + expectedPutCount++; + assertStatistics(cache); + assertHit(cache, "a", "A"); + assertSnapshot(cache, "a", "A"); + + assertEquals(null, cache.put("b", "B")); + expectedPutCount++; + assertStatistics(cache); + assertHit(cache, "a", "A"); + assertHit(cache, "b", "B"); + assertSnapshot(cache, "a", "A", "b", "B"); + + assertEquals(null, cache.put("c", "C")); + expectedPutCount++; + assertStatistics(cache); + assertHit(cache, "a", "A"); + assertHit(cache, "b", "B"); + assertHit(cache, "c", "C"); + assertSnapshot(cache, "a", "A", "b", "B", "c", "C"); + + assertEquals(null, cache.put("d", "D")); + expectedPutCount++; + expectedEvictionCount++; // a should have been evicted + assertStatistics(cache); + assertMiss(cache, "a"); + assertHit(cache, "b", "B"); + assertHit(cache, "c", "C"); + assertHit(cache, "d", "D"); + assertHit(cache, "b", "B"); + assertHit(cache, "c", "C"); + assertSnapshot(cache, "d", "D", "b", "B", "c", "C"); + + assertEquals(null, cache.put("e", "E")); + expectedPutCount++; + expectedEvictionCount++; // d should have been evicted + assertStatistics(cache); + assertMiss(cache, "d"); + assertMiss(cache, "a"); + assertHit(cache, "e", "E"); + assertHit(cache, "b", "B"); + assertHit(cache, "c", "C"); + assertSnapshot(cache, "e", "E", "b", "B", "c", "C"); + } + + public void testStatisticsWithCreate() { + LruCache<String, String> cache = newCreatingCache(); + assertStatistics(cache); + + assertCreated(cache, "aa", "created-aa"); + assertHit(cache, "aa", "created-aa"); + assertSnapshot(cache, "aa", "created-aa"); + + assertCreated(cache, "bb", "created-bb"); + assertMiss(cache, "c"); + assertSnapshot(cache, "aa", "created-aa", "bb", "created-bb"); + + assertCreated(cache, "cc", "created-cc"); + assertSnapshot(cache, "aa", "created-aa", "bb", "created-bb", "cc", "created-cc"); + + expectedEvictionCount++; // aa will be evicted + assertCreated(cache, "dd", "created-dd"); + assertSnapshot(cache, "bb", "created-bb", "cc", "created-cc", "dd", "created-dd"); + + expectedEvictionCount++; // bb will be evicted + assertCreated(cache, "aa", "created-aa"); + assertSnapshot(cache, "cc", "created-cc", "dd", "created-dd", "aa", "created-aa"); + } + + public void testCreateOnCacheMiss() { + LruCache<String, String> cache = newCreatingCache(); + String created = cache.get("aa"); + assertEquals("created-aa", created); + } + + public void testNoCreateOnCacheHit() { + LruCache<String, String> cache = newCreatingCache(); + cache.put("aa", "put-aa"); + assertEquals("put-aa", cache.get("aa")); + } + + public void testConstructorDoesNotAllowZeroCacheSize() { + try { + new LruCache<String, String>(0); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + public void testCannotPutNullKey() { + LruCache<String, String> cache = new LruCache<String, String>(3); + try { + cache.put(null, "A"); + fail(); + } catch (NullPointerException expected) { + } + } + + public void testCannotPutNullValue() { + LruCache<String, String> cache = new LruCache<String, String>(3); + try { + cache.put("a", null); + fail(); + } catch (NullPointerException expected) { + } + } + + public void testToString() { + LruCache<String, String> cache = new LruCache<String, String>(3); + assertEquals("LruCache[maxSize=3,hits=0,misses=0,hitRate=0%]", cache.toString()); + + cache.put("a", "A"); + cache.put("b", "B"); + cache.put("c", "C"); + cache.put("d", "D"); + + cache.get("a"); // miss + cache.get("b"); // hit + cache.get("c"); // hit + cache.get("d"); // hit + cache.get("e"); // miss + + assertEquals("LruCache[maxSize=3,hits=3,misses=2,hitRate=60%]", cache.toString()); + } + + public void testEvictionWithSingletonCache() { + LruCache<String, String> cache = new LruCache<String, String>(1); + cache.put("a", "A"); + cache.put("b", "B"); + assertSnapshot(cache, "b", "B"); + } + + public void testEntryEvictedWhenFull() { + List<String> expectedEvictionLog = new ArrayList<String>(); + final List<String> evictionLog = new ArrayList<String>(); + LruCache<String, String> cache = new LruCache<String, String>(3) { + @Override protected void entryEvicted(String key, String value) { + evictionLog.add(key + "=" + value); + } + }; + + cache.put("a", "A"); + cache.put("b", "B"); + cache.put("c", "C"); + assertEquals(expectedEvictionLog, evictionLog); + + cache.put("d", "D"); + expectedEvictionLog.add("a=A"); + assertEquals(expectedEvictionLog, evictionLog); + } + + /** + * Replacing the value for a key doesn't cause an eviction but it does bring + * the replaced entry to the front of the queue. + */ + public void testPutDoesNotCauseEviction() { + final List<String> evictionLog = new ArrayList<String>(); + List<String> expectedEvictionLog = new ArrayList<String>(); + LruCache<String, String> cache = new LruCache<String, String>(3) { + @Override protected void entryEvicted(String key, String value) { + evictionLog.add(key + "=" + value); + } + }; + + cache.put("a", "A"); + cache.put("b", "B"); + cache.put("c", "C"); + cache.put("b", "B2"); + assertEquals(expectedEvictionLog, evictionLog); + assertSnapshot(cache, "a", "A", "c", "C", "b", "B2"); + } + + public void testCustomSizesImpactsSize() { + LruCache<String, String> cache = new LruCache<String, String>(10) { + @Override protected int sizeOf(String key, String value) { + return key.length() + value.length(); + } + }; + + assertEquals(0, cache.size()); + cache.put("a", "AA"); + assertEquals(3, cache.size()); + cache.put("b", "BBBB"); + assertEquals(8, cache.size()); + cache.put("a", ""); + assertEquals(6, cache.size()); + } + + public void testEvictionWithCustomSizes() { + LruCache<String, String> cache = new LruCache<String, String>(4) { + @Override protected int sizeOf(String key, String value) { + return value.length(); + } + }; + + cache.put("a", "AAAA"); + assertSnapshot(cache, "a", "AAAA"); + cache.put("b", "BBBB"); // should evict a + assertSnapshot(cache, "b", "BBBB"); + cache.put("c", "CC"); // should evict b + assertSnapshot(cache, "c", "CC"); + cache.put("d", "DD"); + assertSnapshot(cache, "c", "CC", "d", "DD"); + cache.put("e", "E"); // should evict c + assertSnapshot(cache, "d", "DD", "e", "E"); + cache.put("f", "F"); + assertSnapshot(cache, "d", "DD", "e", "E", "f", "F"); + cache.put("g", "G"); // should evict d + assertSnapshot(cache, "e", "E", "f", "F", "g", "G"); + cache.put("h", "H"); + assertSnapshot(cache, "e", "E", "f", "F", "g", "G", "h", "H"); + cache.put("i", "III"); // should evict e, f, and g + assertSnapshot(cache, "h", "H", "i", "III"); + cache.put("j", "JJJ"); // should evict h and i + assertSnapshot(cache, "j", "JJJ"); + } + + public void testEvictionThrowsWhenSizesAreInconsistent() { + LruCache<String, int[]> cache = new LruCache<String, int[]>(4) { + @Override protected int sizeOf(String key, int[] value) { + return value[0]; + } + }; + + int[] a = { 4 }; + cache.put("a", a); + + // get the cache size out of sync + a[0] = 1; + assertEquals(4, cache.size()); + + // evict something + try { + cache.put("b", new int[] { 2 }); + fail(); + } catch (IllegalStateException expected) { + } + } + + public void testEvictionThrowsWhenSizesAreNegative() { + LruCache<String, String> cache = new LruCache<String, String>(4) { + @Override protected int sizeOf(String key, String value) { + return -1; + } + }; + + try { + cache.put("a", "A"); + fail(); + } catch (IllegalStateException expected) { + } + } + + /** + * Naive caches evict at most one element at a time. This is problematic + * because evicting a small element may be insufficient to make room for a + * large element. + */ + public void testDifferentElementSizes() { + LruCache<String, String> cache = new LruCache<String, String>(10) { + @Override protected int sizeOf(String key, String value) { + return value.length(); + } + }; + + cache.put("a", "1"); + cache.put("b", "12345678"); + cache.put("c", "1"); + assertSnapshot(cache, "a", "1", "b", "12345678", "c", "1"); + cache.put("d", "12345678"); // should evict a and b + assertSnapshot(cache, "c", "1", "d", "12345678"); + cache.put("e", "12345678"); // should evict c and d + assertSnapshot(cache, "e", "12345678"); + } + + public void testEvictAll() { + final List<String> evictionLog = new ArrayList<String>(); + LruCache<String, String> cache = new LruCache<String, String>(10) { + @Override protected void entryEvicted(String key, String value) { + evictionLog.add(key + "=" + value); + } + }; + + cache.put("a", "A"); + cache.put("b", "B"); + cache.put("c", "C"); + cache.evictAll(); + assertEquals(0, cache.size()); + assertEquals(Arrays.asList("a=A", "b=B", "c=C"), evictionLog); + } + + public void testEvictAllEvictsSizeZeroElements() { + LruCache<String, String> cache = new LruCache<String, String>(10) { + @Override protected int sizeOf(String key, String value) { + return 0; + } + }; + + cache.put("a", "A"); + cache.put("b", "B"); + cache.evictAll(); + assertSnapshot(cache); + } + + public void testRemoveDoesNotCallEntryEvicted() { + LruCache<String, String> cache = new LruCache<String, String>(10) { + @Override protected void entryEvicted(String key, String value) { + fail(); + } + }; + cache.put("a", "A"); + assertEquals("A", cache.remove("a")); + } + + public void testRemoveWithCustomSizes() { + LruCache<String, String> cache = new LruCache<String, String>(10) { + @Override protected int sizeOf(String key, String value) { + return value.length(); + } + }; + cache.put("a", "123456"); + cache.put("b", "1234"); + cache.remove("a"); + assertEquals(4, cache.size()); + } + + public void testRemoveAbsentElement() { + LruCache<String, String> cache = new LruCache<String, String>(10); + cache.put("a", "A"); + cache.put("b", "B"); + assertEquals(null, cache.remove("c")); + assertEquals(2, cache.size()); + } + + public void testRemoveNullThrows() { + LruCache<String, String> cache = new LruCache<String, String>(10); + try { + cache.remove(null); + fail(); + } catch (NullPointerException expected) { + } + } + + private LruCache<String, String> newCreatingCache() { + return new LruCache<String, String>(3) { + @Override protected String create(String key) { + return (key.length() > 1) ? ("created-" + key) : null; + } + }; + } + + private void assertHit(LruCache<String, String> cache, String key, String value) { + assertEquals(value, cache.get(key)); + expectedHitCount++; + assertStatistics(cache); + } + + private void assertMiss(LruCache<String, String> cache, String key) { + assertEquals(null, cache.get(key)); + expectedMissCount++; + assertStatistics(cache); + } + + private void assertCreated(LruCache<String, String> cache, String key, String value) { + assertEquals(value, cache.get(key)); + expectedMissCount++; + expectedCreateCount++; + assertStatistics(cache); + } + + private void assertStatistics(LruCache<?, ?> cache) { + assertEquals("create count", expectedCreateCount, cache.createCount()); + assertEquals("put count", expectedPutCount, cache.putCount()); + assertEquals("hit count", expectedHitCount, cache.hitCount()); + assertEquals("miss count", expectedMissCount, cache.missCount()); + assertEquals("eviction count", expectedEvictionCount, cache.evictionCount()); + } + + private <T> void assertSnapshot(LruCache<T, T> cache, T... keysAndValues) { + List<T> actualKeysAndValues = new ArrayList<T>(); + for (Map.Entry<T, T> entry : cache.snapshot().entrySet()) { + actualKeysAndValues.add(entry.getKey()); + actualKeysAndValues.add(entry.getValue()); + } + + // assert using lists because order is important for LRUs + assertEquals(Arrays.asList(keysAndValues), actualKeysAndValues); + } +} diff --git a/core/tests/coretests/src/android/webkit/AccessibilityInjectorTest.java b/core/tests/coretests/src/android/webkit/AccessibilityInjectorTest.java index 16108e62392a..aedfbad59ca9 100644 --- a/core/tests/coretests/src/android/webkit/AccessibilityInjectorTest.java +++ b/core/tests/coretests/src/android/webkit/AccessibilityInjectorTest.java @@ -25,13 +25,11 @@ import android.os.Handler; import android.os.Looper; import android.os.SystemClock; import android.provider.Settings; -import android.test.AndroidTestCase; +import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.LargeTest; import android.view.KeyEvent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; -import android.webkit.WebView; -import android.webkit.WebViewClient; /** * This is a test for the behavior of the {@link AccessibilityInjector} @@ -42,7 +40,8 @@ import android.webkit.WebViewClient; * to so it also checks if the test for announcing navigation axis and * status messages as appropriate. */ -public class AccessibilityInjectorTest extends AndroidTestCase { +public class AccessibilityInjectorTest + extends ActivityInstrumentationTestCase2<AccessibilityInjectorTestActivity> { /** The timeout to wait for the expected selection. */ private static final long TIMEOUT_WAIT_FOR_SELECTION_STRING = 1000; @@ -51,12 +50,18 @@ public class AccessibilityInjectorTest extends AndroidTestCase { private static final long TIMEOUT_ENABLE_ACCESSIBILITY_AND_MOCK_SERVICE = 1000; /** The count of tests to detect when to shut down the service. */ - private static final int TEST_CASE_COUNT = 16; + private static final int TEST_CASE_COUNT = 19; /** The meta state for pressed left ALT. */ private static final int META_STATE_ALT_LEFT_ON = KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON; + /** Prefix for the CSS style span appended by WebKit. */ + private static final String APPLE_SPAN_PREFIX = "<span class=\"Apple-style-span\""; + + /** Suffix for the CSS style span appended by WebKit. */ + private static final String APPLE_SPAN_SUFFIX = "</span>"; + /** The value for not specified selection string since null is a valid value. */ private static final String SELECTION_STRING_UNKNOWN = "Unknown"; @@ -95,6 +100,10 @@ public class AccessibilityInjectorTest extends AndroidTestCase { /** The received selection string for assertion checking. */ private static String sReceivedSelectionString = SELECTION_STRING_UNKNOWN; + public AccessibilityInjectorTest() { + super(AccessibilityInjectorTestActivity.class); + } + @Override protected void setUp() throws Exception { super.setUp(); @@ -136,17 +145,16 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</head>" + "<body>" + "<p>" + - "a <b>b</b> c" + + "a<b>b</b>c" + "</p>" + "<p>" + "d" + - "<p/>" + - "e" + - "</p>" + + "<p/>" + + "e" + "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // change navigation axis to word sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON); @@ -241,7 +249,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // change navigation axis to word sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON); @@ -368,7 +376,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // Sentence axis is the default @@ -471,7 +479,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // change navigation axis to heading sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON); @@ -559,7 +567,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // change navigation axis to heading sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON); @@ -631,7 +639,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // change navigation axis to document sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON); @@ -689,7 +697,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // change navigation axis to document sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON); @@ -733,7 +741,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // change navigation axis to word sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON); @@ -792,11 +800,11 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // go to the first sentence sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); - assertSelectionString("First"); + assertSelectionString("<div>First</div>"); // go to the second sentence sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); @@ -828,7 +836,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { // go to the first sentence sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); - assertSelectionString("First"); + assertSelectionString("<div>First</div>"); } /** @@ -855,7 +863,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // go to the first sentence sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); @@ -931,7 +939,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // go to the first sentence sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); @@ -991,7 +999,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // change navigation axis to word sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON); @@ -1047,7 +1055,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // change navigation axis to word sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON); @@ -1107,7 +1115,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // go to the first sentence sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); @@ -1179,7 +1187,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // go to the first sentence sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); @@ -1250,7 +1258,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { "</body>" + "</html>"; - WebView webView = createWebVewWithHtml(html); + WebView webView = loadHTML(html); // go to the first sentence sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); @@ -1296,24 +1304,258 @@ public class AccessibilityInjectorTest extends AndroidTestCase { sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); assertSelectionString("<input type=\"text\">"); + // go to the second sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString("<input type=\"text\">"); + // go to the first sentence sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); assertSelectionString("First"); } /** + * Tests traversing of input controls. + */ + @LargeTest + public void testSelectionOfInputElements3() throws Exception { + // a bit ugly but helps detect beginning and end of all tests so accessibility + // and the mock service are not toggled on every test (expensive) + sExecutedTestCount++; + + String html = + "<!DOCTYPE html>" + + "<html>" + + "<head>" + + "</head>" + + "<body>" + + "<input type=\"text\"/>" + + "<button type=\"button\">Click Me!</button>" + + "<select>" + + "<option value=\"volvo\">Volvo</option>" + + "<option value=\"saab\">Saab</option>" + + "</select>" + + "</body>" + + "</html>"; + + WebView webView = loadHTML(html); + + // go to the first sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("<input type=\"text\">"); + + // go to the second sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("<button type=\"button\">Click Me!</button>"); + + // go to the third sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("<select><option value=\"volvo\">Volvo</option>" + + "<option value=\"saab\">Saab</option></select>"); + + // go to past the last sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString(null); + + // go to the third sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString("<select><option value=\"volvo\">Volvo</option>" + + "<option value=\"saab\">Saab</option></select>"); + + // go to the second sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString("<button type=\"button\">Click Me!</button>"); + + // go to the first sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString("<input type=\"text\">"); + + // go to before the first sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString(null); + + // go to the first sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("<input type=\"text\">"); + } + + /** + * Tests traversing of input controls. + */ + @LargeTest + public void testSelectionOfInputElements4() throws Exception { + // a bit ugly but helps detect beginning and end of all tests so accessibility + // and the mock service are not toggled on every test (expensive) + sExecutedTestCount++; + + String html = + "<!DOCTYPE html>" + + "<html>" + + "<head>" + + "</head>" + + "<body>" + + "Start" + + "<span>" + + "<span>" + + "<input type=\"submit\">" + + "</span>" + + "</span>" + + "<input type=\"text\" size=\"30\">" + + "<span>" + + "<span>" + + "<input type=\"submit\" size=\"30\">" + + "</span>" + + "</span>" + + "End" + + "</body>" + + "</html>"; + + WebView webView = loadHTML(html); + + // go to the first sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("Start"); + + // go to the second sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("<input type=\"submit\">"); + + // go to the third sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("<input type=\"text\" size=\"30\">"); + + // go to the fourth sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("<input type=\"submit\" size=\"30\">"); + + // go to the fifth sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("End"); + + // go to past the last sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString(null); + + // go to the fifth sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString("End"); + + // go to the fourth sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString("<input type=\"submit\" size=\"30\">"); + + // go to the third sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString("<input type=\"text\" size=\"30\">"); + + // go to the second sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString("<input type=\"submit\">"); + + // go to the first sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString("Start"); + + // go to before the first sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString(null); + + // go to the first sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("Start"); + } + + /** + * Tests traversing of input controls. + */ + @LargeTest + public void testSelectionOfInputElements5() throws Exception { + // a bit ugly but helps detect beginning and end of all tests so accessibility + // and the mock service are not toggled on every test (expensive) + sExecutedTestCount++; + + String html = + "<!DOCTYPE html>" + + "<html>" + + "<head>" + + "</head>" + + "<body>" + + "<div>" + + "First" + + "<input type=\"hidden\">" + + "<input type=\"hidden\">" + + "<input type=\"hidden\">" + + "<input type=\"hidden\">" + + "<input type=\"text\">" + + "<span>" + + "<span>" + + "<input type=\"submit\">" + + "</span>" + + "</span>" + + "</div>" + + "</body>" + + "Second" + + "</html>"; + + WebView webView = loadHTML(html); + + // go to the first sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("First"); + + // go to the second sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("<input type=\"text\">"); + + // go to the third sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("<input type=\"submit\">"); + + // go to the fourth sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("Second"); + + // go to past the last sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString(null); + + // go to the fourth sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString("Second"); + + // go to the third sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString("<input type=\"submit\">"); + + // go to the second sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString("<input type=\"text\">"); + + // go to the first sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString("First"); + + // go to before the first sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0); + assertSelectionString(null); + + // go to the first sentence + sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0); + assertSelectionString("First"); + } + + /** * Enable accessibility and the mock accessibility service. */ private void enableAccessibilityAndMockAccessibilityService() { // make sure the manager is instantiated so the system initializes it - AccessibilityManager.getInstance(getContext()); + AccessibilityManager.getInstance(getActivity()); // enable accessibility and the mock accessibility service - Settings.Secure.putInt(getContext().getContentResolver(), + Settings.Secure.putInt(getActivity().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 1); - String enabledServices = new ComponentName(getContext().getPackageName(), + String enabledServices = new ComponentName(getActivity().getPackageName(), MockAccessibilityService.class.getName()).flattenToShortString(); - Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.putString(getActivity().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, enabledServices); // poll within a timeout and let be interrupted in case of success @@ -1342,13 +1584,34 @@ public class AccessibilityInjectorTest extends AndroidTestCase { } /** + * Strips the apple span appended by WebKit while generating + * the selection markup. + * + * @param markup The markup. + * @return Stripped from apple spans markup. + */ + private static String stripAppleSpanFromMarkup(String markup) { + StringBuilder stripped = new StringBuilder(markup); + int prefixBegIdx = stripped.indexOf(APPLE_SPAN_PREFIX); + while (prefixBegIdx >= 0) { + int prefixEndIdx = stripped.indexOf(">", prefixBegIdx) + 1; + stripped.replace(prefixBegIdx, prefixEndIdx, ""); + int suffixBegIdx = stripped.lastIndexOf(APPLE_SPAN_SUFFIX); + int suffixEndIdx = suffixBegIdx + APPLE_SPAN_SUFFIX.length(); + stripped.replace(suffixBegIdx, suffixEndIdx, ""); + prefixBegIdx = stripped.indexOf(APPLE_SPAN_PREFIX); + } + return stripped.toString(); + } + + /** * Disables accessibility and the mock accessibility service. */ private void disableAccessibilityAndMockAccessibilityService() { // disable accessibility and the mock accessibility service - Settings.Secure.putInt(getContext().getContentResolver(), + Settings.Secure.putInt(getActivity().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0); - Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.putString(getActivity().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, ""); } @@ -1391,28 +1654,30 @@ public class AccessibilityInjectorTest extends AndroidTestCase { } /** - * Creates a {@link WebView} with with a given HTML content. + * Loads HTML content in a {@link WebView}. * * @param html The HTML content; - * @return The created view. + * @return The {@link WebView} view. */ - private WebView createWebVewWithHtml(final String html) { + private WebView loadHTML(final String html) { mWorker.getHandler().post(new Runnable() { public void run() { - mWebView = new WebView(getContext()); - mWebView.loadData(html, "text/html", "utf-8"); - mWebView.setWebViewClient(new WebViewClient() { - @Override - public void onPageFinished(WebView view, String url) { - mWorker.getHandler().post(new Runnable() { - public void run() { - synchronized (sTestLock) { - sTestLock.notifyAll(); + if (mWebView == null) { + mWebView = getActivity().getWebView(); + mWebView.setWebViewClient(new WebViewClient() { + @Override + public void onPageFinished(WebView view, String url) { + mWorker.getHandler().post(new Runnable() { + public void run() { + synchronized (sTestLock) { + sTestLock.notifyAll(); + } } - } - }); - } - }); + }); + } + }); + } + mWebView.loadData(html, "text/html", "utf-8"); } }); synchronized (sTestLock) { @@ -1430,7 +1695,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { * to ensure that this test will be agnostic to changes of the bindings. */ private void injectTestWebContentKeyBindings() { - ContentResolver contentResolver = getContext().getContentResolver(); + ContentResolver contentResolver = getActivity().getContentResolver(); mDefaultKeyBindings = Settings.Secure.getString(contentResolver, Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS); Settings.Secure.putString(contentResolver, @@ -1441,7 +1706,7 @@ public class AccessibilityInjectorTest extends AndroidTestCase { * Restores the default web content key bindings. */ private void restoreDefaultWebContentKeyBindings() { - Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.putString(getActivity().getContentResolver(), Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS, mDefaultKeyBindings); } @@ -1519,7 +1784,11 @@ public class AccessibilityInjectorTest extends AndroidTestCase { } if (!event.getText().isEmpty()) { CharSequence text = event.getText().get(0); - sReceivedSelectionString = (text != null) ? text.toString() : null; + if (text != null) { + sReceivedSelectionString = stripAppleSpanFromMarkup(text.toString()); + } else { + sReceivedSelectionString = null; + } } synchronized (sTestLock) { sTestLock.notifyAll(); diff --git a/core/java/android/security/Md5MessageDigest.java b/core/tests/coretests/src/android/webkit/AccessibilityInjectorTestActivity.java index 4fe0cb0e62fe..3842df796ee0 100644 --- a/core/java/android/security/Md5MessageDigest.java +++ b/core/tests/coretests/src/android/webkit/AccessibilityInjectorTestActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,29 +14,25 @@ * limitations under the License. */ -package android.security; +package android.webkit; -/** - * Provides the MD5 hash encryption. - */ -public class Md5MessageDigest extends MessageDigest -{ - // ptr to native context - private int mNativeMd5Context; - - public Md5MessageDigest() - { - init(); - } - - public byte[] digest(byte[] input) - { - update(input); - return digest(); +import com.android.frameworks.coretests.R; + +import android.app.Activity; +import android.os.Bundle; + +public class AccessibilityInjectorTestActivity extends Activity { + + private WebView mWebView; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.accessibility_injector_test); + mWebView = (WebView) findViewById(R.id.webview); } - private native void init(); - public native void update(byte[] input); - public native byte[] digest(); - native public void reset(); + public WebView getWebView() { + return mWebView; + } } diff --git a/data/etc/platform.xml b/data/etc/platform.xml index ea91c218b605..df80546688fa 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -58,10 +58,6 @@ <group gid="sdcard_rw" /> </permission> - <permission name="android.permission.ACCESS_USB" > - <group gid="usb" /> - </permission> - <permission name="android.permission.ACCESS_MTP" > <group gid="mtp" /> </permission> diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index 1ee121e977d9..0aefc3114679 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -262,6 +262,24 @@ key 233 HEADSETHOOK # key 240 "KEY_UNKNOWN" +key 288 BUTTON_1 +key 289 BUTTON_2 +key 290 BUTTON_3 +key 291 BUTTON_4 +key 292 BUTTON_5 +key 293 BUTTON_6 +key 294 BUTTON_7 +key 295 BUTTON_8 +key 296 BUTTON_9 +key 297 BUTTON_10 +key 298 BUTTON_11 +key 299 BUTTON_12 +key 300 BUTTON_13 +key 301 BUTTON_14 +key 302 BUTTON_15 +key 303 BUTTON_16 + + key 304 BUTTON_A key 305 BUTTON_B key 306 BUTTON_C diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index b5f1c84ae674..92230a954774 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -564,7 +564,6 @@ <li><a href="<?cs var:toroot ?>guide/developing/tools/layoutopt.html">layoutopt</a></li> <li><a href="<?cs var:toroot ?>guide/developing/tools/logcat.html">logcat</a></li> <li><a href="<?cs var:toroot ?>guide/developing/tools/mksdcard.html">mksdcard</a></li> - <li><a href="<?cs var:toroot ?>guide/developing/tools/monkey.html">Monkey</a></li> <li class="toggle-list"> <div><a href="<?cs var:toroot ?>guide/developing/tools/monkeyrunner_concepts.html"> diff --git a/drm/common/Android.mk b/drm/common/Android.mk index c79a91a3ec33..f1136c916615 100644 --- a/drm/common/Android.mk +++ b/drm/common/Android.mk @@ -26,7 +26,6 @@ LOCAL_SRC_FILES:= \ DrmInfoStatus.cpp \ DrmRights.cpp \ DrmSupportInfo.cpp \ - IDrmIOService.cpp \ IDrmManagerService.cpp \ IDrmServiceListener.cpp \ DrmInfoEvent.cpp \ diff --git a/drm/common/IDrmIOService.cpp b/drm/common/IDrmIOService.cpp deleted file mode 100644 index e44ca55e5631..000000000000 --- a/drm/common/IDrmIOService.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdint.h> -#include <sys/types.h> -#include <binder/Parcel.h> -#include <binder/IPCThreadState.h> -#include <drm/drm_framework_common.h> -#include "IDrmIOService.h" - -using namespace android; - -void BpDrmIOService::writeToFile(const String8& filePath, const String8& dataBuffer) { - Parcel data, reply; - - data.writeInterfaceToken(IDrmIOService::getInterfaceDescriptor()); - data.writeString8(filePath); - data.writeString8(dataBuffer); - - remote()->transact(WRITE_TO_FILE, data, &reply); -} - -String8 BpDrmIOService::readFromFile(const String8& filePath) { - - Parcel data, reply; - - data.writeInterfaceToken(IDrmIOService::getInterfaceDescriptor()); - data.writeString8(filePath); - - remote()->transact(READ_FROM_FILE, data, &reply); - return reply.readString8(); -} - -IMPLEMENT_META_INTERFACE(DrmIOService, "drm.IDrmIOService"); - -status_t BnDrmIOService::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - - switch (code) { - case WRITE_TO_FILE: - { - CHECK_INTERFACE(IDrmIOService, data, reply); - - writeToFile(data.readString8(), data.readString8()); - return DRM_NO_ERROR; - } - - case READ_FROM_FILE: - { - CHECK_INTERFACE(IDrmIOService, data, reply); - - String8 dataBuffer = readFromFile(data.readString8()); - reply->writeString8(dataBuffer); - return DRM_NO_ERROR; - } - - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - diff --git a/drm/drmioserver/DrmIOService.cpp b/drm/drmioserver/DrmIOService.cpp deleted file mode 100644 index 60e6e70daacf..000000000000 --- a/drm/drmioserver/DrmIOService.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "DrmIOService" -#include <utils/Log.h> - -#include <binder/IServiceManager.h> -#include "DrmIOService.h" -#include "ReadWriteUtils.h" - -using namespace android; - -void DrmIOService::instantiate() { - LOGV("instantiate"); - defaultServiceManager()->addService(String16("drm.drmIOService"), new DrmIOService()); -} - -DrmIOService::DrmIOService() { - LOGV("created"); -} - -DrmIOService::~DrmIOService() { - LOGV("Destroyed"); -} - -void DrmIOService::writeToFile(const String8& filePath, const String8& dataBuffer) { - LOGV("Entering writeToFile"); - ReadWriteUtils::writeToFile(filePath, dataBuffer); -} - -String8 DrmIOService::readFromFile(const String8& filePath) { - LOGV("Entering readFromFile"); - return ReadWriteUtils::readBytes(filePath); -} - diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp index b6e0c30fd65e..ef7d274cc12b 100644 --- a/drm/drmserver/DrmManager.cpp +++ b/drm/drmserver/DrmManager.cpp @@ -87,7 +87,7 @@ void DrmManager::removeUniqueId(int uniqueId) { } status_t DrmManager::loadPlugIns() { - String8 pluginDirPath("/system/lib/drm/plugins/native"); + String8 pluginDirPath("/system/lib/drm"); return loadPlugIns(pluginDirPath); } diff --git a/drm/libdrmframework/include/IDrmIOService.h b/drm/libdrmframework/include/IDrmIOService.h deleted file mode 100644 index 5e0d907e6794..000000000000 --- a/drm/libdrmframework/include/IDrmIOService.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __IDRM_IO_SERVICE_H__ -#define __IDRM_IO_SERVICE_H__ - -#include <utils/RefBase.h> -#include <binder/IInterface.h> -#include <binder/Parcel.h> - -namespace android { - -/** - * This is the interface class for DRM IO service. - * - */ -class IDrmIOService : public IInterface -{ -public: - enum { - WRITE_TO_FILE = IBinder::FIRST_CALL_TRANSACTION, - READ_FROM_FILE - }; - -public: - DECLARE_META_INTERFACE(DrmIOService); - -public: - /** - * Writes the data into the file path provided - * - * @param[in] filePath Path of the file - * @param[in] dataBuffer Data to write - */ - virtual void writeToFile(const String8& filePath, const String8& dataBuffer) = 0; - - /** - * Reads the data from the file path provided - * - * @param[in] filePath Path of the file - * @return Data read from the file - */ - virtual String8 readFromFile(const String8& filePath) = 0; -}; - -/** - * This is the Binder implementation class for DRM IO service. - */ -class BpDrmIOService: public BpInterface<IDrmIOService> -{ -public: - BpDrmIOService(const sp<IBinder>& impl) - : BpInterface<IDrmIOService>(impl) {} - - virtual void writeToFile(const String8& filePath, const String8& dataBuffer); - - virtual String8 readFromFile(const String8& filePath); -}; - -/** - * This is the Binder implementation class for DRM IO service. - */ -class BnDrmIOService: public BnInterface<IDrmIOService> -{ -public: - virtual status_t onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); -}; - -}; - -#endif /* __IDRM_IO_SERVICE_H__ */ - diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk index d4a6f18dbbe1..af67aa3b50dd 100644 --- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk +++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk @@ -60,7 +60,7 @@ LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/include \ external/openssl/include -LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/drm/plugins/native +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/drm LOCAL_MODULE_TAGS := optional diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index c7ae2cf91fd4..12dc93c3c07d 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -19,7 +19,6 @@ package android.graphics; import android.os.Parcel; import android.os.Parcelable; import android.util.DisplayMetrics; - import java.io.OutputStream; import java.nio.Buffer; import java.nio.ByteBuffer; @@ -200,8 +199,6 @@ public final class Bitmap implements Parcelable { * check if a bitmap has changed. * * @return The current generation ID for this bitmap. - * - * @hide */ public int getGenerationId() { return nativeGenerationId(mNativeBitmap); @@ -247,25 +244,80 @@ public final class Bitmap implements Parcelable { } } + /** + * Possible bitmap configurations. A bitmap configuration describes + * how pixels are stored. This affects the quality (color depth) as + * well as the ability to display transparent/translucent colors. + */ public enum Config { // these native values must match up with the enum in SkBitmap.h + + /** + * Each pixel is stored as a single translucency (alpha) channel. + * This is very useful to efficiently store masks for instance. + * No color information is stored. + * With this configuration, each pixel requires 1 byte of memory. + */ ALPHA_8 (2), + + /** + * Each pixel is stored on 2 bytes and only the RGB channels are + * encoded: red is stored with 5 bits of precision (32 possible + * values), green is stored with 6 bits of precision (64 possible + * values) and blue is stored with 5 bits of precision. + * + * This configuration can produce slight visual artifacts depending + * on the configuration of the source. For instance, without + * dithering, the result might show a greenish tint. To get better + * results dithering should be applied. + * + * This configuration may be useful when using opaque bitmaps + * that do not require high color fidelity. + */ RGB_565 (4), + + /** + * Each pixel is stored on 2 bytes. The three RGB color channels + * and the alpha channel (translucency) are stored with a 4 bits + * precision (16 possible values.) + * + * This configuration is mostly useful if the application needs + * to store translucency information but also needs to save + * memory. + * + * It is recommended to use {@link #ARGB_8888} instead of this + * configuration. + * + * @deprecated Because of the poor quality of this configuration, + * it is advised to use {@link #ARGB_8888} instead. + */ + @Deprecated ARGB_4444 (5), + + /** + * Each pixel is stored on 4 bytes. Each channel (RGB and alpha + * for translucency) is stored with 8 bits of precision (256 + * possible values.) + * + * This configuration is very flexible and offers the best + * quality. It should be used whenever possible. + */ ARGB_8888 (6); + final int nativeInt; + + @SuppressWarnings({"deprecation"}) + private static Config sConfigs[] = { + null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888 + }; + Config(int ni) { this.nativeInt = ni; } - final int nativeInt; - /* package */ static Config nativeToConfig(int ni) { + static Config nativeToConfig(int ni) { return sConfigs[ni]; } - - private static Config sConfigs[] = { - null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888 - }; } /** @@ -289,7 +341,7 @@ public final class Bitmap implements Parcelable { } long bufferSize = (long)elements << shift; - long pixelSize = (long)getRowBytes() * getHeight(); + long pixelSize = getByteCount(); if (bufferSize < pixelSize) { throw new RuntimeException("Buffer not large enough for pixels"); @@ -325,7 +377,7 @@ public final class Bitmap implements Parcelable { } long bufferBytes = (long)elements << shift; - long bitmapBytes = (long)getRowBytes() * getHeight(); + long bitmapBytes = getByteCount(); if (bufferBytes < bitmapBytes) { throw new RuntimeException("Buffer not large enough for pixels"); @@ -475,6 +527,7 @@ public final class Bitmap implements Parcelable { case ALPHA_8: newConfig = Config.ALPHA_8; break; + //noinspection deprecation case ARGB_4444: case ARGB_8888: default: @@ -752,7 +805,7 @@ public final class Bitmap implements Parcelable { } // Scale by tdensity / sdensity, rounding up. - return ( (size * tdensity) + (sdensity >> 1) ) / sdensity; + return ((size * tdensity) + (sdensity >> 1)) / sdensity; } /** @@ -768,6 +821,14 @@ public final class Bitmap implements Parcelable { } /** + * Returns the number of bytes used to store this bitmap's pixels. + */ + public final int getByteCount() { + // int result permits bitmaps up to 46,340 x 46,340 + return getRowBytes() * getHeight(); + } + + /** * If the bitmap's internal config is in one of the public formats, return * that config, otherwise return null. */ @@ -790,14 +851,12 @@ public final class Bitmap implements Parcelable { /** * Tell the bitmap if all of the pixels are known to be opaque (false) * or if some of the pixels may contain non-opaque alpha values (true). - * Note, for some configs (e.g. RGB_565) this call is ignore, since it does - * not support per-pixel alpha values. + * Note, for some configs (e.g. RGB_565) this call is ignored, since it + * does not support per-pixel alpha values. * * This is meant as a drawing hint, as in some cases a bitmap that is known * to be opaque can take a faster drawing case than one that may have * non-opaque per-pixel alpha values. - * - * @hide */ public void setHasAlpha(boolean hasAlpha) { nativeSetHasAlpha(mNativeBitmap, hasAlpha); @@ -1066,13 +1125,9 @@ public final class Bitmap implements Parcelable { * Given another bitmap, return true if it has the same dimensions, config, * and pixel data as this bitmap. If any of those differ, return false. * If other is null, return false. - * - * @hide (just needed internally right now) */ public boolean sameAs(Bitmap other) { - return this == other || - (other != null && - nativeSameAs(mNativeBitmap, other.mNativeBitmap)); + return this == other || (other != null && nativeSameAs(mNativeBitmap, other.mNativeBitmap)); } /** @@ -1099,7 +1154,13 @@ public final class Bitmap implements Parcelable { @Override public void finalize() { - nativeDestructor(mNativeBitmap); + try { + super.finalize(); + } catch (Throwable t) { + // Ignore + } finally { + nativeDestructor(mNativeBitmap); + } } } diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index d8a7f9d57c7a..0a23bae18e87 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1254,9 +1254,7 @@ public class Paint { * @param text The text to measure * @param index The offset into text to begin measuring at * @param count The number of maximum number of entries to measure. If count - * is negative, then the characters before index are measured - * in reverse order. This allows for measuring the end of - * string. + * is negative, then the characters are measured in reverse order. * @param maxWidth The maximum width to accumulate. * @param measuredWidth Optional. If not null, returns the actual width * measured. diff --git a/graphics/tests/graphicstests/src/android/graphics/BitmapTest.java b/graphics/tests/graphicstests/src/android/graphics/BitmapTest.java index 6734bb798c6f..685a9980fdc4 100644 --- a/graphics/tests/graphicstests/src/android/graphics/BitmapTest.java +++ b/graphics/tests/graphicstests/src/android/graphics/BitmapTest.java @@ -16,8 +16,6 @@ package android.graphics; -import android.graphics.Bitmap; -import android.graphics.Color; import android.test.suitebuilder.annotation.SmallTest; import junit.framework.TestCase; @@ -42,6 +40,10 @@ public class BitmapTest extends TestCase { assertEquals("rowbytes", 200, bm2.getRowBytes()); assertEquals("rowbytes", 200, bm3.getRowBytes()); + assertEquals("byteCount", 80000, bm1.getByteCount()); + assertEquals("byteCount", 40000, bm2.getByteCount()); + assertEquals("byteCount", 40000, bm3.getByteCount()); + assertEquals("height", 200, bm1.getHeight()); assertEquals("height", 200, bm2.getHeight()); assertEquals("height", 200, bm3.getHeight()); diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index 03f894404a17..2dc4beb74d53 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -392,6 +392,7 @@ public: static status_t getStreamVolumeIndex(stream_type stream, int *index); static uint32_t getStrategyForStream(stream_type stream); + static uint32_t getDevicesForStream(stream_type stream); static audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc); static status_t registerEffect(effect_descriptor_t *desc, diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h index 5afceaa4bc4f..720a562265ce 100644 --- a/include/media/IAudioPolicyService.h +++ b/include/media/IAudioPolicyService.h @@ -74,6 +74,7 @@ public: virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index) = 0; virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index) = 0; virtual uint32_t getStrategyForStream(AudioSystem::stream_type stream) = 0; + virtual uint32_t getDevicesForStream(AudioSystem::stream_type stream) = 0; virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc) = 0; virtual status_t registerEffect(effect_descriptor_t *desc, audio_io_handle_t output, diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index 4599d70d1faf..a9697962e3ca 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -109,7 +109,7 @@ private: status_t allocateOutputBuffersFromNativeWindow(); status_t cancelBufferToNativeWindow(BufferInfo *info); - status_t freeOutputBuffersOwnedByNativeWindow(); + status_t freeOutputBuffersNotOwnedByComponent(); BufferInfo *dequeueBufferFromNativeWindow(); BufferInfo *findBufferByID( diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h index d484d60b31db..9e6f0e227792 100644 --- a/include/media/stagefright/AudioSource.h +++ b/include/media/stagefright/AudioSource.h @@ -18,15 +18,17 @@ #define AUDIO_SOURCE_H_ +#include <media/AudioRecord.h> #include <media/AudioSystem.h> #include <media/stagefright/MediaSource.h> +#include <media/stagefright/MediaBuffer.h> +#include <utils/List.h> namespace android { class AudioRecord; -struct MediaBufferGroup; -struct AudioSource : public MediaSource { +struct AudioSource : public MediaSource, public MediaBufferObserver { // Note that the "channels" parameter is _not_ the number of channels, // but a bitmask of AudioSystem::audio_channels constants. AudioSource( @@ -45,6 +47,9 @@ struct AudioSource : public MediaSource { virtual status_t read( MediaBuffer **buffer, const ReadOptions *options = NULL); + status_t dataCallbackTimestamp(const AudioRecord::Buffer& buffer, int64_t timeUs); + virtual void signalBufferReturned(MediaBuffer *buffer); + protected: virtual ~AudioSource(); @@ -54,27 +59,31 @@ private: // After the initial mute, we raise the volume linearly // over kAutoRampDurationUs. - kAutoRampDurationUs = 700000, + kAutoRampDurationUs = 300000, // This is the initial mute duration to suppress // the video recording signal tone - kAutoRampStartUs = 1000000, - }; + kAutoRampStartUs = 0, + }; + + Mutex mLock; + Condition mFrameAvailableCondition; + Condition mFrameEncodingCompletionCondition; AudioRecord *mRecord; status_t mInitCheck; bool mStarted; + int32_t mSampleRate; - bool mCollectStats; bool mTrackMaxAmplitude; int64_t mStartTimeUs; int16_t mMaxAmplitude; int64_t mPrevSampleTimeUs; - int64_t mTotalLostFrames; - int64_t mPrevLostBytes; int64_t mInitialReadTimeUs; + int64_t mNumFramesReceived; + int64_t mNumClientOwnedBuffers; - MediaBufferGroup *mGroup; + List<MediaBuffer * > mBuffersReceived; void trackMaxAmplitude(int16_t *data, int nSamples); @@ -84,6 +93,9 @@ private: int32_t startFrame, int32_t rampDurationFrames, uint8_t *data, size_t bytes); + void releaseQueuedFrames_l(); + void waitOutstandingEncodingFrames_l(); + AudioSource(const AudioSource &); AudioSource &operator=(const AudioSource &); }; diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h index d4f1733b391c..f95e56afd79b 100644 --- a/include/media/stagefright/DataSource.h +++ b/include/media/stagefright/DataSource.h @@ -75,7 +75,7 @@ public: static void RegisterDefaultSniffers(); // for DRM - virtual DecryptHandle* DrmInitialization(DrmManagerClient *client) { + virtual DecryptHandle* DrmInitialization() { return NULL; } virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {}; diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h index 72a0403e69ff..51a434324a15 100644 --- a/include/media/stagefright/FileSource.h +++ b/include/media/stagefright/FileSource.h @@ -38,7 +38,7 @@ public: virtual status_t getSize(off64_t *size); - virtual DecryptHandle* DrmInitialization(DrmManagerClient *client); + virtual DecryptHandle* DrmInitialization(); virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client); diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h index f7618e99c5db..5c5229d3a8ab 100644 --- a/include/media/stagefright/MPEG4Writer.h +++ b/include/media/stagefright/MPEG4Writer.h @@ -98,6 +98,8 @@ private: List<MediaBuffer *> mSamples; // Sample data // Convenient constructor + Chunk(): mTrack(NULL), mTimeStampUs(0) {} + Chunk(Track *track, int64_t timeUs, List<MediaBuffer *> samples) : mTrack(track), mTimeStampUs(timeUs), mSamples(samples) { } @@ -124,13 +126,14 @@ private: void bufferChunk(const Chunk& chunk); // Write all buffered chunks from all tracks - void writeChunks(); + void writeAllChunks(); - // Write a chunk if there is one - status_t writeOneChunk(); + // Retrieve the proper chunk to write if there is one + // Return true if a chunk is found; otherwise, return false. + bool findChunkToWrite(Chunk *chunk); - // Write the first chunk from the given ChunkInfo. - void writeFirstChunk(ChunkInfo* info); + // Actually write the given chunk to the file. + void writeChunkToFile(Chunk* chunk); // Adjust other track media clock (presumably wall clock) // based on audio track media clock with the drift time. diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h index 2d50ca578489..66dfff69f266 100644 --- a/include/media/stagefright/MediaDefs.h +++ b/include/media/stagefright/MediaDefs.h @@ -37,6 +37,8 @@ extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS; extern const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW; extern const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW; extern const char *MEDIA_MIMETYPE_AUDIO_RAW; +extern const char *MEDIA_MIMETYPE_AUDIO_FLAC; +extern const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS; extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4; extern const char *MEDIA_MIMETYPE_CONTAINER_WAV; diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index 18fd90efbb7d..f7f2235eee08 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -48,6 +48,7 @@ enum { kKeyBitRate = 'brte', // int32_t (bps) kKeyESDS = 'esds', // raw data kKeyAVCC = 'avcc', // raw data + kKeyD263 = 'd263', // raw data kKeyVorbisInfo = 'vinf', // raw data kKeyVorbisBooks = 'vboo', // raw data kKeyWantsNALFragments = 'NALf', @@ -118,6 +119,7 @@ enum { enum { kTypeESDS = 'esds', kTypeAVCC = 'avcc', + kTypeD263 = 'd263', }; class MetaData : public RefBase { diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 82948cb400c5..f7d837ab42c6 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -175,6 +175,7 @@ private: int64_t mSeekTimeUs; ReadOptions::SeekMode mSeekMode; int64_t mTargetTimeUs; + bool mOutputPortSettingsChangedPending; MediaBuffer *mLeftOverBuffer; diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h index 56ed3a4cf84c..361e7dc28d28 100644 --- a/include/surfaceflinger/ISurfaceComposer.h +++ b/include/surfaceflinger/ISurfaceComposer.h @@ -44,6 +44,8 @@ public: eSecure = 0x00000080, eNonPremultiplied = 0x00000100, eOpaque = 0x00000400, + eProtectedByApp = 0x00000800, + eProtectedByDRM = 0x00001000, eFXSurfaceNormal = 0x00000000, eFXSurfaceBlur = 0x00010000, diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h index 8b256f426590..02d6f8ffd20e 100644 --- a/include/ui/GraphicBuffer.h +++ b/include/ui/GraphicBuffer.h @@ -54,9 +54,11 @@ public: USAGE_SW_WRITE_RARELY = GRALLOC_USAGE_SW_WRITE_RARELY, USAGE_SW_WRITE_OFTEN = GRALLOC_USAGE_SW_WRITE_OFTEN, USAGE_SW_WRITE_MASK = GRALLOC_USAGE_SW_WRITE_MASK, - + USAGE_SOFTWARE_MASK = USAGE_SW_READ_MASK|USAGE_SW_WRITE_MASK, - + + USAGE_PROTECTED = GRALLOC_USAGE_PROTECTED, + USAGE_HW_TEXTURE = GRALLOC_USAGE_HW_TEXTURE, USAGE_HW_RENDER = GRALLOC_USAGE_HW_RENDER, USAGE_HW_2D = GRALLOC_USAGE_HW_2D, diff --git a/include/ui/Input.h b/include/ui/Input.h index 2012fccf85fb..cb9327e1210c 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -28,6 +28,10 @@ #include <utils/RefBase.h> #include <utils/String8.h> +#ifdef HAVE_ANDROID_OS +class SkMatrix; +#endif + /* * Additional private constants not defined in ndk/ui/input.h. */ @@ -79,6 +83,10 @@ struct AInputDevice { namespace android { +#ifdef HAVE_ANDROID_OS +class Parcel; +#endif + /* * Flags that flow alongside events in the input dispatch system to help with certain * policy decisions such as waking from device sleep. @@ -162,15 +170,62 @@ struct InputConfiguration { * Pointer coordinate data. */ struct PointerCoords { - float x; - float y; - float pressure; - float size; - float touchMajor; - float touchMinor; - float toolMajor; - float toolMinor; - float orientation; + enum { MAX_AXES = 15 }; // 15 so that sizeof(PointerCoords) == 16 * 4 == 64 + + // Bitfield of axes that are present in this structure. + uint32_t bits; // 32bits are enough for now, can raise to 64bit when needed + + // Values of axes that are stored in this structure packed in order by axis id + // for each axis that is present in the structure according to 'bits'. + float values[MAX_AXES]; + + inline void clear() { + bits = 0; + } + + inline float getAxisValue(int32_t axis) const { + uint32_t axisBit = 1 << axis; + if (!(bits & axisBit)) { + return 0; + } + uint32_t index = __builtin_popcount(bits & (axisBit - 1)); + return values[index]; + } + + inline status_t setAxisValue(int32_t axis, float value) { + uint32_t axisBit = 1 << axis; + uint32_t index = __builtin_popcount(bits & (axisBit - 1)); + if (!(bits & axisBit)) { + uint32_t count = __builtin_popcount(bits); + if (count >= MAX_AXES) { + tooManyAxes(axis); + return NO_MEMORY; + } + bits |= axisBit; + for (uint32_t i = count; i > index; i--) { + values[i] = values[i - 1]; + } + } + values[index] = value; + return OK; + } + + inline float* editAxisValue(int32_t axis) { + uint32_t axisBit = 1 << axis; + if (!(bits & axisBit)) { + return NULL; + } + uint32_t index = __builtin_popcount(bits & (axisBit - 1)); + return &values[index]; + } + +#ifdef HAVE_ANDROID_OS + status_t readFromParcel(Parcel* parcel); + status_t writeToParcel(Parcel* parcel) const; +#endif + +private: + void tooManyAxes(int axis); }; /* @@ -185,12 +240,13 @@ public: inline int32_t getDeviceId() const { return mDeviceId; } inline int32_t getSource() const { return mSource; } - + + inline void setSource(int32_t source) { mSource = source; } + protected: void initialize(int32_t deviceId, int32_t source); void initialize(const InputEvent& from); -private: int32_t mDeviceId; int32_t mSource; }; @@ -241,7 +297,7 @@ public: nsecs_t eventTime); void initialize(const KeyEvent& from); -private: +protected: int32_t mAction; int32_t mFlags; int32_t mKeyCode; @@ -263,12 +319,18 @@ public: inline int32_t getAction() const { return mAction; } + inline void setAction(int32_t action) { mAction = action; } + inline int32_t getFlags() const { return mFlags; } inline int32_t getEdgeFlags() const { return mEdgeFlags; } + inline void setEdgeFlags(int32_t edgeFlags) { mEdgeFlags = edgeFlags; } + inline int32_t getMetaState() const { return mMetaState; } + inline void setMetaState(int32_t metaState) { mMetaState = metaState; } + inline float getXOffset() const { return mXOffset; } inline float getYOffset() const { return mYOffset; } @@ -285,48 +347,54 @@ public: inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; } + const PointerCoords* getRawPointerCoords(size_t pointerIndex) const; + + float getRawAxisValue(int32_t axis, size_t pointerIndex) const; + inline float getRawX(size_t pointerIndex) const { - return getCurrentPointerCoords(pointerIndex).x; + return getRawAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex); } inline float getRawY(size_t pointerIndex) const { - return getCurrentPointerCoords(pointerIndex).y; + return getRawAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex); } + float getAxisValue(int32_t axis, size_t pointerIndex) const; + inline float getX(size_t pointerIndex) const { - return getRawX(pointerIndex) + mXOffset; + return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex); } inline float getY(size_t pointerIndex) const { - return getRawY(pointerIndex) + mYOffset; + return getAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex); } inline float getPressure(size_t pointerIndex) const { - return getCurrentPointerCoords(pointerIndex).pressure; + return getAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerIndex); } inline float getSize(size_t pointerIndex) const { - return getCurrentPointerCoords(pointerIndex).size; + return getAxisValue(AMOTION_EVENT_AXIS_SIZE, pointerIndex); } inline float getTouchMajor(size_t pointerIndex) const { - return getCurrentPointerCoords(pointerIndex).touchMajor; + return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex); } inline float getTouchMinor(size_t pointerIndex) const { - return getCurrentPointerCoords(pointerIndex).touchMinor; + return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex); } inline float getToolMajor(size_t pointerIndex) const { - return getCurrentPointerCoords(pointerIndex).toolMajor; + return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex); } inline float getToolMinor(size_t pointerIndex) const { - return getCurrentPointerCoords(pointerIndex).toolMinor; + return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex); } inline float getOrientation(size_t pointerIndex) const { - return getCurrentPointerCoords(pointerIndex).orientation; + return getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex); } inline size_t getHistorySize() const { return mSampleEventTimes.size() - 1; } @@ -335,48 +403,67 @@ public: return mSampleEventTimes[historicalIndex]; } + const PointerCoords* getHistoricalRawPointerCoords( + size_t pointerIndex, size_t historicalIndex) const; + + float getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, + size_t historicalIndex) const; + inline float getHistoricalRawX(size_t pointerIndex, size_t historicalIndex) const { - return getHistoricalPointerCoords(pointerIndex, historicalIndex).x; + return getHistoricalRawAxisValue( + AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex); } inline float getHistoricalRawY(size_t pointerIndex, size_t historicalIndex) const { - return getHistoricalPointerCoords(pointerIndex, historicalIndex).y; + return getHistoricalRawAxisValue( + AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex); } + float getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const; + inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const { - return getHistoricalRawX(pointerIndex, historicalIndex) + mXOffset; + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex); } inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const { - return getHistoricalRawY(pointerIndex, historicalIndex) + mYOffset; + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex); } inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const { - return getHistoricalPointerCoords(pointerIndex, historicalIndex).pressure; + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_PRESSURE, pointerIndex, historicalIndex); } inline float getHistoricalSize(size_t pointerIndex, size_t historicalIndex) const { - return getHistoricalPointerCoords(pointerIndex, historicalIndex).size; + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_SIZE, pointerIndex, historicalIndex); } inline float getHistoricalTouchMajor(size_t pointerIndex, size_t historicalIndex) const { - return getHistoricalPointerCoords(pointerIndex, historicalIndex).touchMajor; + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex, historicalIndex); } inline float getHistoricalTouchMinor(size_t pointerIndex, size_t historicalIndex) const { - return getHistoricalPointerCoords(pointerIndex, historicalIndex).touchMinor; + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex, historicalIndex); } inline float getHistoricalToolMajor(size_t pointerIndex, size_t historicalIndex) const { - return getHistoricalPointerCoords(pointerIndex, historicalIndex).toolMajor; + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex, historicalIndex); } inline float getHistoricalToolMinor(size_t pointerIndex, size_t historicalIndex) const { - return getHistoricalPointerCoords(pointerIndex, historicalIndex).toolMinor; + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex, historicalIndex); } inline float getHistoricalOrientation(size_t pointerIndex, size_t historicalIndex) const { - return getHistoricalPointerCoords(pointerIndex, historicalIndex).orientation; + return getHistoricalAxisValue( + AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex); } void initialize( @@ -396,12 +483,23 @@ public: const int32_t* pointerIds, const PointerCoords* pointerCoords); + void copyFrom(const MotionEvent* other, bool keepHistory); + void addSample( nsecs_t eventTime, const PointerCoords* pointerCoords); void offsetLocation(float xOffset, float yOffset); + void scale(float scaleFactor); + +#ifdef HAVE_ANDROID_OS + void transform(const SkMatrix* matrix); + + status_t readFromParcel(Parcel* parcel); + status_t writeToParcel(Parcel* parcel) const; +#endif + // Low-level accessors. inline const int32_t* getPointerIds() const { return mPointerIds.array(); } inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); } @@ -409,7 +507,7 @@ public: return mSamplePointerCoords.array(); } -private: +protected: int32_t mAction; int32_t mFlags; int32_t mEdgeFlags; @@ -422,15 +520,6 @@ private: Vector<int32_t> mPointerIds; Vector<nsecs_t> mSampleEventTimes; Vector<PointerCoords> mSamplePointerCoords; - - inline const PointerCoords& getCurrentPointerCoords(size_t pointerIndex) const { - return mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; - } - - inline const PointerCoords& getHistoricalPointerCoords( - size_t pointerIndex, size_t historicalIndex) const { - return mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex]; - } }; /* @@ -486,11 +575,11 @@ public: inline const String8 getName() const { return mName; } inline uint32_t getSources() const { return mSources; } - const MotionRange* getMotionRange(int32_t rangeType) const; + const MotionRange* getMotionRange(int32_t axis) const; void addSource(uint32_t source); - void addMotionRange(int32_t rangeType, float min, float max, float flat, float fuzz); - void addMotionRange(int32_t rangeType, const MotionRange& range); + void addMotionRange(int32_t axis, float min, float max, float flat, float fuzz); + void addMotionRange(int32_t axis, const MotionRange& range); inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } inline int32_t getKeyboardType() const { return mKeyboardType; } diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h index 9b1a8975e457..dbccf29b110e 100755 --- a/include/ui/KeycodeLabels.h +++ b/include/ui/KeycodeLabels.h @@ -212,6 +212,22 @@ static const KeycodeLabel KEYCODES[] = { { "PROG_YELLOW", 185 }, { "PROG_BLUE", 186 }, { "APP_SWITCH", 187 }, + { "BUTTON_1", 188 }, + { "BUTTON_2", 189 }, + { "BUTTON_3", 190 }, + { "BUTTON_4", 191 }, + { "BUTTON_5", 192 }, + { "BUTTON_6", 193 }, + { "BUTTON_7", 194 }, + { "BUTTON_8", 195 }, + { "BUTTON_9", 196 }, + { "BUTTON_10", 197 }, + { "BUTTON_11", 198 }, + { "BUTTON_12", 199 }, + { "BUTTON_13", 200 }, + { "BUTTON_14", 201 }, + { "BUTTON_15", 202 }, + { "BUTTON_16", 203 }, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index 624f7eba8470..9f501e277cea 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -31,7 +31,7 @@ #include <binder/MemoryHeapBase.h> -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS #include <linux/android_pmem.h> #endif @@ -108,7 +108,7 @@ status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset) { if (size == 0) { // try to figure out the size automatically -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS // first try the PMEM ioctl pmem_region reg; int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®); diff --git a/libs/binder/MemoryHeapPmem.cpp b/libs/binder/MemoryHeapPmem.cpp index 16e92f939ee3..03322ea5d9c4 100644 --- a/libs/binder/MemoryHeapPmem.cpp +++ b/libs/binder/MemoryHeapPmem.cpp @@ -30,7 +30,7 @@ #include <binder/MemoryHeapPmem.h> #include <binder/MemoryHeapBase.h> -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS #include <linux/android_pmem.h> #endif @@ -72,7 +72,7 @@ SubRegionMemory::SubRegionMemory(const sp<MemoryHeapPmem>& heap, memset(start_ptr, 0xda, size); #endif -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS if (size > 0) { const size_t pagesize = getpagesize(); size = (size + pagesize-1) & ~(pagesize-1); @@ -107,7 +107,7 @@ void SubRegionMemory::revoke() // which means MemoryHeapPmem::revoke() wouldn't have been able to // promote() it. -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS if (mSize != 0) { const sp<MemoryHeapPmem>& heap(getHeap()); int our_fd = heap->heapID(); @@ -130,7 +130,7 @@ MemoryHeapPmem::MemoryHeapPmem(const sp<MemoryHeapBase>& pmemHeap, : MemoryHeapBase() { char const * const device = pmemHeap->getDevice(); -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS if (device) { int fd = open(device, O_RDWR | (flags & NO_CACHING ? O_SYNC : 0)); LOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno)); @@ -187,7 +187,7 @@ sp<MemoryHeapPmem::MemoryPmem> MemoryHeapPmem::createMemory( status_t MemoryHeapPmem::slap() { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS size_t size = getSize(); const size_t pagesize = getpagesize(); size = (size + pagesize-1) & ~(pagesize-1); @@ -205,7 +205,7 @@ status_t MemoryHeapPmem::slap() status_t MemoryHeapPmem::unslap() { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS size_t size = getSize(); const size_t pagesize = getpagesize(); size = (size + pagesize-1) & ~(pagesize-1); diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index d6cc8ce03443..223cf091e7bd 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -220,11 +220,19 @@ status_t SurfaceTexture::updateTexImage() { mSlots[mLastQueued].mEglImage = image; mSlots[mLastQueued].mEglDisplay = dpy; } + + GLint error; + while ((error = glGetError()) != GL_NO_ERROR) { + LOGE("GL error cleared before updating SurfaceTexture: %#04x", error); + } glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image); - GLint error = glGetError(); - if (error != GL_NO_ERROR) { + bool failed = false; + while ((error = glGetError()) != GL_NO_ERROR) { LOGE("error binding external texture image %p (slot %d): %#04x", image, mLastQueued, error); + failed = true; + } + if (failed) { return -EINVAL; } diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 8ee7ec3f60cd..68b54febf956 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1110,6 +1110,17 @@ void OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHei const uint32_t count = meshWidth * meshHeight * 6; + float left = FLT_MAX; + float top = FLT_MAX; + float right = FLT_MIN; + float bottom = FLT_MIN; + +#if RENDER_LAYERS_AS_REGIONS + bool hasActiveLayer = hasLayer(); +#else + bool hasActiveLayer = false; +#endif + // TODO: Support the colors array TextureVertex mesh[count]; TextureVertex* vertex = mesh; @@ -1138,12 +1149,28 @@ void OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHei TextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2); TextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1); TextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2); + +#if RENDER_LAYERS_AS_REGIONS + if (hasActiveLayer) { + // TODO: This could be optimized to avoid unnecessary ops + left = fminf(left, fminf(vertices[ax], fminf(vertices[bx], vertices[cx]))); + top = fminf(top, fminf(vertices[ay], fminf(vertices[by], vertices[cy]))); + right = fmaxf(right, fmaxf(vertices[ax], fmaxf(vertices[bx], vertices[cx]))); + bottom = fmaxf(bottom, fmaxf(vertices[ay], fmaxf(vertices[by], vertices[cy]))); + } +#endif } } +#if RENDER_LAYERS_AS_REGIONS + if (hasActiveLayer) { + dirtyLayer(left, top, right, bottom, *mSnapshot->transform); + } +#endif + drawTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, alpha / 255.0f, mode, texture->blend, &mesh[0].position[0], &mesh[0].texture[0], - GL_TRIANGLES, count); + GL_TRIANGLES, count, false, false, 0, false, false); } void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk index 38358525c61a..25c8beb3b2f5 100644 --- a/libs/rs/Android.mk +++ b/libs/rs/Android.mk @@ -130,6 +130,28 @@ LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY) +# Now build a host version for serialization +include $(CLEAR_VARS) +LOCAL_CFLAGS += -DANDROID_RS_SERIALIZE + +LOCAL_SRC_FILES:= \ + rsAllocation.cpp \ + rsComponent.cpp \ + rsElement.cpp \ + rsFileA3D.cpp \ + rsObjectBase.cpp \ + rsMesh.cpp \ + rsStream.cpp \ + rsType.cpp + +LOCAL_STATIC_LIBRARIES := libcutils libutils + +LOCAL_LDLIBS := -lpthread +LOCAL_MODULE:= libRSserialize +LOCAL_MODULE_TAGS := optional + +include $(BUILD_HOST_STATIC_LIBRARY) + # include the java examples include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk,\ java \ diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h index f160ef1a7b40..bb5e4aa29015 100644 --- a/libs/rs/RenderScript.h +++ b/libs/rs/RenderScript.h @@ -365,6 +365,9 @@ RsAllocation rsaAllocationCreateFromBitmap(RsContext con, RsType vtype, RsAllocation rsaAllocationCubeCreateFromBitmap(RsContext con, RsType vtype, RsAllocationMipmapControl mips, const void *data, uint32_t usages); +#ifdef ANDROID_RS_SERIALIZE +#define NO_RS_FUNCS +#endif #ifndef NO_RS_FUNCS #include "rsgApiFuncDecl.h" diff --git a/drm/drmioserver/Android.mk b/libs/rs/java/ImageProcessing/Android.mk index 11571c7904bc..7fa30d0b8bdd 100644 --- a/drm/drmioserver/Android.mk +++ b/libs/rs/java/ImageProcessing/Android.mk @@ -1,5 +1,5 @@ # -# Copyright (C) 2010 The Android Open Source Project +# Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,31 +13,20 @@ # See the License for the specific language governing permissions and # limitations under the License. # -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - main_drmioserver.cpp \ - DrmIOService.cpp -LOCAL_SHARED_LIBRARIES := \ - libutils \ - libbinder +ifneq ($(TARGET_SIMULATOR),true) -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -ldl -else - LOCAL_SHARED_LIBRARIES += libdl -endif +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) -LOCAL_STATIC_LIBRARIES := libdrmframeworkcommon +LOCAL_MODULE_TAGS := optional -LOCAL_C_INCLUDES := \ - $(TOP)/frameworks/base/drm/libdrmframework/include \ - $(TOP)/frameworks/base/include +LOCAL_SRC_FILES := $(call all-java-files-under, src) \ + $(call all-renderscript-files-under, src) +#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript -LOCAL_MODULE:= drmioserver +LOCAL_PACKAGE_NAME := ImageProcessing -LOCAL_MODULE_TAGS := optional +include $(BUILD_PACKAGE) -include $(BUILD_EXECUTABLE) +endif diff --git a/libs/rs/java/ImageProcessing/AndroidManifest.xml b/libs/rs/java/ImageProcessing/AndroidManifest.xml new file mode 100644 index 000000000000..0fcbf1e70ae5 --- /dev/null +++ b/libs/rs/java/ImageProcessing/AndroidManifest.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.rs.image"> + + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-sdk android:minSdkVersion="11" /> + <application android:label="Image Processing"> + <activity android:name="ImageProcessingActivity" + android:screenOrientation="portrait"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/libs/rs/java/ImageProcessing/res/drawable-hdpi/data.jpg b/libs/rs/java/ImageProcessing/res/drawable-hdpi/data.jpg Binary files differnew file mode 100644 index 000000000000..81a87b1726c2 --- /dev/null +++ b/libs/rs/java/ImageProcessing/res/drawable-hdpi/data.jpg diff --git a/libs/rs/java/ImageProcessing/res/drawable/data.jpg b/libs/rs/java/ImageProcessing/res/drawable/data.jpg Binary files differnew file mode 100644 index 000000000000..81a87b1726c2 --- /dev/null +++ b/libs/rs/java/ImageProcessing/res/drawable/data.jpg diff --git a/libs/rs/java/ImageProcessing/res/layout/main.xml b/libs/rs/java/ImageProcessing/res/layout/main.xml new file mode 100644 index 000000000000..c6ec729652aa --- /dev/null +++ b/libs/rs/java/ImageProcessing/res/layout/main.xml @@ -0,0 +1,187 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2009 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + + <SurfaceView + android:id="@+id/surface" + android:layout_width="1dip" + android:layout_height="1dip" /> + + <ImageView + android:id="@+id/display" + android:layout_width="320dip" + android:layout_height="266dip" /> + + <Button + android:layout_marginBottom="170dip" + android:layout_width="wrap_content" + android:layout_height="40dip" + android:text="@string/benchmark" + android:onClick="benchmark" + android:layout_gravity="bottom"/> + + <TextView + android:id="@+id/benchmarkText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="18sp" + android:layout_marginLeft="100dip" + android:layout_marginBottom="175dip" + android:layout_gravity="bottom" + android:text="@string/saturation"/> + + <SeekBar + android:id="@+id/inSaturation" + android:layout_marginBottom="140dip" + android:layout_marginLeft="10dip" + android:layout_marginRight="10dip" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" /> + + <TextView + android:id="@+id/inSaturationText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="18sp" + android:layout_marginLeft="50dip" + android:layout_marginBottom="142dip" + android:textColor="#000" + android:layout_gravity="bottom" + android:text="@string/saturation"/> + + <SeekBar + android:id="@+id/inGamma" + android:layout_marginBottom="110dip" + android:layout_marginLeft="10dip" + android:layout_marginRight="10dip" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" /> + + <TextView + android:id="@+id/inGammaText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="18sp" + android:layout_marginLeft="50dip" + android:layout_marginBottom="112dip" + android:textColor="#000" + android:layout_gravity="bottom" + android:text="@string/gamma"/> + + <SeekBar + android:id="@+id/outWhite" + android:layout_marginBottom="80dip" + android:layout_marginLeft="170dip" + android:layout_marginRight="10dip" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" /> + + <TextView + android:id="@+id/outWhiteText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="18sp" + android:layout_marginLeft="220dip" + android:layout_marginBottom="82dip" + android:textColor="#000" + android:layout_gravity="bottom" + android:text="@string/out_white"/> + + <SeekBar + android:id="@+id/inWhite" + android:layout_marginBottom="80dip" + android:layout_marginLeft="10dip" + android:layout_marginRight="170dip" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" /> + + <TextView + android:id="@+id/inWhiteText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="18sp" + android:layout_marginLeft="50dip" + android:layout_marginBottom="82dip" + android:textColor="#000" + android:layout_gravity="bottom" + android:text="@string/in_white"/> + + <SeekBar + android:id="@+id/outBlack" + android:layout_marginBottom="50dip" + android:layout_marginLeft="170dip" + android:layout_marginRight="10dip" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" /> + + <TextView + android:id="@+id/outBlackText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="18sp" + android:layout_marginLeft="220dip" + android:layout_marginBottom="52dip" + android:textColor="#000" + android:layout_gravity="bottom" + android:text="@string/out_black"/> + + <SeekBar + android:id="@+id/inBlack" + android:layout_marginBottom="50dip" + android:layout_marginLeft="10dip" + android:layout_marginRight="170dip" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" /> + + <TextView + android:id="@+id/inBlackText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="18sp" + android:layout_marginLeft="50dip" + android:layout_marginBottom="52dip" + android:textColor="#000" + android:layout_gravity="bottom" + android:text="@string/in_black"/> + + <SeekBar + android:id="@+id/radius" + android:layout_marginBottom="10dip" + android:layout_marginLeft="10dip" + android:layout_marginRight="10dip" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" /> + + <TextView + android:id="@+id/blurText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="18sp" + android:layout_marginLeft="50dip" + android:layout_marginBottom="12dip" + android:textColor="#000" + android:layout_gravity="bottom" + android:text="@string/blur_description"/> + +</merge>
\ No newline at end of file diff --git a/libs/rs/java/ImageProcessing/res/values/strings.xml b/libs/rs/java/ImageProcessing/res/values/strings.xml new file mode 100644 index 000000000000..cc5cc4de9782 --- /dev/null +++ b/libs/rs/java/ImageProcessing/res/values/strings.xml @@ -0,0 +1,33 @@ +<?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. +*/ +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- General --> + <skip /> + <!--slider label --> + <string name="blur_description">Blur Radius</string> + <string name="in_white">In White</string> + <string name="out_white">Out White</string> + <string name="in_black">In Black</string> + <string name="out_black">Out Black</string> + <string name="gamma">Gamma</string> + <string name="saturation">Saturation</string> + <string name="benchmark">Benchmark</string> + +</resources> diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java new file mode 100644 index 000000000000..5de09f75322c --- /dev/null +++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.image; + +import android.app.Activity; +import android.os.Bundle; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.renderscript.ScriptC; +import android.renderscript.RenderScript; +import android.renderscript.Type; +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.Script; +import android.view.SurfaceView; +import android.view.SurfaceHolder; +import android.widget.ImageView; +import android.widget.SeekBar; +import android.widget.TextView; +import android.view.View; +import java.lang.Math; + +public class ImageProcessingActivity extends Activity + implements SurfaceHolder.Callback, + SeekBar.OnSeekBarChangeListener { + private Bitmap mBitmapIn; + private Bitmap mBitmapOut; + private Bitmap mBitmapScratch; + private ScriptC_threshold mScript; + private ScriptC_vertical_blur mScriptVBlur; + private ScriptC_horizontal_blur mScriptHBlur; + private int mRadius = 0; + private SeekBar mRadiusSeekBar; + + private float mInBlack = 0.0f; + private SeekBar mInBlackSeekBar; + private float mOutBlack = 0.0f; + private SeekBar mOutBlackSeekBar; + private float mInWhite = 255.0f; + private SeekBar mInWhiteSeekBar; + private float mOutWhite = 255.0f; + private SeekBar mOutWhiteSeekBar; + private float mGamma = 1.0f; + private SeekBar mGammaSeekBar; + + private float mSaturation = 1.0f; + private SeekBar mSaturationSeekBar; + + private TextView mBenchmarkResult; + + @SuppressWarnings({"FieldCanBeLocal"}) + private RenderScript mRS; + @SuppressWarnings({"FieldCanBeLocal"}) + private Type mPixelType; + @SuppressWarnings({"FieldCanBeLocal"}) + private Allocation mInPixelsAllocation; + @SuppressWarnings({"FieldCanBeLocal"}) + private Allocation mOutPixelsAllocation; + @SuppressWarnings({"FieldCanBeLocal"}) + private Allocation mScratchPixelsAllocation1; + private Allocation mScratchPixelsAllocation2; + + private SurfaceView mSurfaceView; + private ImageView mDisplayView; + + class FilterCallback extends RenderScript.RSMessageHandler { + private Runnable mAction = new Runnable() { + public void run() { + mDisplayView.invalidate(); + } + }; + + @Override + public void run() { + mSurfaceView.removeCallbacks(mAction); + mSurfaceView.post(mAction); + } + } + + int in[]; + int interm[]; + int out[]; + int MAX_RADIUS = 25; + // Store our coefficients here + float gaussian[]; + + private long javaFilter() { + final int width = mBitmapIn.getWidth(); + final int height = mBitmapIn.getHeight(); + final int count = width * height; + + if (in == null) { + in = new int[count]; + interm = new int[count]; + out = new int[count]; + gaussian = new float[MAX_RADIUS * 2 + 1]; + mBitmapIn.getPixels(in, 0, width, 0, 0, width, height); + } + + long t = java.lang.System.currentTimeMillis(); + + int w, h, r; + + float fRadius = (float)mRadius; + int radius = (int)mRadius; + + // Compute gaussian weights for the blur + // e is the euler's number + float e = 2.718281828459045f; + float pi = 3.1415926535897932f; + // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) + // x is of the form [-radius .. 0 .. radius] + // and sigma varies with radius. + // Based on some experimental radius values and sigma's + // we approximately fit sigma = f(radius) as + // sigma = radius * 0.4 + 0.6 + // The larger the radius gets, the more our gaussian blur + // will resemble a box blur since with large sigma + // the gaussian curve begins to lose its shape + float sigma = 0.4f * fRadius + 0.6f; + // Now compute the coefficints + // We will store some redundant values to save some math during + // the blur calculations + // precompute some values + float coeff1 = 1.0f / (float)(Math.sqrt( 2.0f * pi ) * sigma); + float coeff2 = - 1.0f / (2.0f * sigma * sigma); + float normalizeFactor = 0.0f; + float floatR = 0.0f; + for (r = -radius; r <= radius; r ++) { + floatR = (float)r; + gaussian[r + radius] = coeff1 * (float)Math.pow(e, floatR * floatR * coeff2); + normalizeFactor += gaussian[r + radius]; + } + + //Now we need to normalize the weights because all our coefficients need to add up to one + normalizeFactor = 1.0f / normalizeFactor; + for (r = -radius; r <= radius; r ++) { + floatR = (float)r; + gaussian[r + radius] *= normalizeFactor; + } + + float blurredPixelR = 0.0f; + float blurredPixelG = 0.0f; + float blurredPixelB = 0.0f; + float blurredPixelA = 0.0f; + + for (h = 0; h < height; h ++) { + for (w = 0; w < width; w ++) { + + blurredPixelR = 0.0f; + blurredPixelG = 0.0f; + blurredPixelB = 0.0f; + blurredPixelA = 0.0f; + + for (r = -radius; r <= radius; r ++) { + // Stepping left and right away from the pixel + int validW = w + r; + // Clamp to zero and width max() isn't exposed for ints yet + if (validW < 0) { + validW = 0; + } + if (validW > width - 1) { + validW = width - 1; + } + + int input = in[h*width + validW]; + + int R = ((input >> 24) & 0xff); + int G = ((input >> 16) & 0xff); + int B = ((input >> 8) & 0xff); + int A = (input & 0xff); + + float weight = gaussian[r + radius]; + + blurredPixelR += (float)(R)*weight; + blurredPixelG += (float)(G)*weight; + blurredPixelB += (float)(B)*weight; + blurredPixelA += (float)(A)*weight; + } + + int R = (int)blurredPixelR; + int G = (int)blurredPixelG; + int B = (int)blurredPixelB; + int A = (int)blurredPixelA; + + interm[h*width + w] = (R << 24) | (G << 16) | (B << 8) | (A); + } + } + + for (h = 0; h < height; h ++) { + for (w = 0; w < width; w ++) { + + blurredPixelR = 0.0f; + blurredPixelG = 0.0f; + blurredPixelB = 0.0f; + blurredPixelA = 0.0f; + for (r = -radius; r <= radius; r ++) { + int validH = h + r; + // Clamp to zero and width + if (validH < 0) { + validH = 0; + } + if (validH > height - 1) { + validH = height - 1; + } + + int input = interm[validH*width + w]; + + int R = ((input >> 24) & 0xff); + int G = ((input >> 16) & 0xff); + int B = ((input >> 8) & 0xff); + int A = (input & 0xff); + + float weight = gaussian[r + radius]; + + blurredPixelR += (float)(R)*weight; + blurredPixelG += (float)(G)*weight; + blurredPixelB += (float)(B)*weight; + blurredPixelA += (float)(A)*weight; + } + + int R = (int)blurredPixelR; + int G = (int)blurredPixelG; + int B = (int)blurredPixelB; + int A = (int)blurredPixelA; + + out[h*width + w] = (R << 24) | (G << 16) | (B << 8) | (A); + } + } + + t = java.lang.System.currentTimeMillis() - t; + android.util.Log.v("Img", "Java frame time ms " + t); + mBitmapOut.setPixels(out, 0, width, 0, 0, width, height); + return t; + } + + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser) { + + if (seekBar == mRadiusSeekBar) { + float fRadius = progress / 100.0f; + fRadius *= (float)(MAX_RADIUS); + mRadius = (int)fRadius; + + mScript.set_radius(mRadius); + } else if (seekBar == mInBlackSeekBar) { + mInBlack = (float)progress; + mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite); + } else if (seekBar == mOutBlackSeekBar) { + mOutBlack = (float)progress; + mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite); + } else if (seekBar == mInWhiteSeekBar) { + mInWhite = (float)progress + 127.0f; + mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite); + } else if (seekBar == mOutWhiteSeekBar) { + mOutWhite = (float)progress + 127.0f; + mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite); + } else if (seekBar == mGammaSeekBar) { + mGamma = (float)progress/100.0f; + mGamma = Math.max(mGamma, 0.1f); + mGamma = 1.0f / mGamma; + mScriptVBlur.invoke_setGamma(mGamma); + } else if (seekBar == mSaturationSeekBar) { + mSaturation = (float)progress / 50.0f; + mScriptVBlur.invoke_setSaturation(mSaturation); + } + + long t = java.lang.System.currentTimeMillis(); + if (true) { + mScript.invoke_filter(); + mOutPixelsAllocation.copyTo(mBitmapOut); + } else { + javaFilter(); + mDisplayView.invalidate(); + } + + t = java.lang.System.currentTimeMillis() - t; + android.util.Log.v("Img", "Renderscript frame time core ms " + t); + } + } + + public void onStartTrackingTouch(SeekBar seekBar) { + } + + public void onStopTrackingTouch(SeekBar seekBar) { + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + mBitmapIn = loadBitmap(R.drawable.data); + mBitmapOut = loadBitmap(R.drawable.data); + mBitmapScratch = loadBitmap(R.drawable.data); + + mSurfaceView = (SurfaceView) findViewById(R.id.surface); + mSurfaceView.getHolder().addCallback(this); + + mDisplayView = (ImageView) findViewById(R.id.display); + mDisplayView.setImageBitmap(mBitmapOut); + + mRadiusSeekBar = (SeekBar) findViewById(R.id.radius); + mRadiusSeekBar.setOnSeekBarChangeListener(this); + + mInBlackSeekBar = (SeekBar)findViewById(R.id.inBlack); + mInBlackSeekBar.setOnSeekBarChangeListener(this); + mInBlackSeekBar.setMax(128); + mInBlackSeekBar.setProgress(0); + mOutBlackSeekBar = (SeekBar)findViewById(R.id.outBlack); + mOutBlackSeekBar.setOnSeekBarChangeListener(this); + mOutBlackSeekBar.setMax(128); + mOutBlackSeekBar.setProgress(0); + + mInWhiteSeekBar = (SeekBar)findViewById(R.id.inWhite); + mInWhiteSeekBar.setOnSeekBarChangeListener(this); + mInWhiteSeekBar.setMax(128); + mInWhiteSeekBar.setProgress(128); + mOutWhiteSeekBar = (SeekBar)findViewById(R.id.outWhite); + mOutWhiteSeekBar.setOnSeekBarChangeListener(this); + mOutWhiteSeekBar.setMax(128); + mOutWhiteSeekBar.setProgress(128); + + mGammaSeekBar = (SeekBar)findViewById(R.id.inGamma); + mGammaSeekBar.setOnSeekBarChangeListener(this); + mGammaSeekBar.setMax(150); + mGammaSeekBar.setProgress(100); + + mSaturationSeekBar = (SeekBar)findViewById(R.id.inSaturation); + mSaturationSeekBar.setOnSeekBarChangeListener(this); + mSaturationSeekBar.setProgress(50); + + mBenchmarkResult = (TextView) findViewById(R.id.benchmarkText); + mBenchmarkResult.setText("Benchmark not yet run"); + } + + public void surfaceCreated(SurfaceHolder holder) { + createScript(); + mScript.invoke_filter(); + mOutPixelsAllocation.copyTo(mBitmapOut); + } + + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + } + + public void surfaceDestroyed(SurfaceHolder holder) { + } + + private void createScript() { + mRS = RenderScript.create(this); + mRS.setMessageHandler(new FilterCallback()); + + mInPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapIn, + Allocation.MipmapControl.MIPMAP_NONE, + Allocation.USAGE_SCRIPT); + mOutPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapOut, + Allocation.MipmapControl.MIPMAP_NONE, + Allocation.USAGE_SCRIPT); + + Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS)); + tb.setX(mBitmapIn.getWidth()); + tb.setY(mBitmapIn.getHeight()); + mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create()); + mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create()); + + mScriptVBlur = new ScriptC_vertical_blur(mRS, getResources(), R.raw.vertical_blur); + mScriptHBlur = new ScriptC_horizontal_blur(mRS, getResources(), R.raw.horizontal_blur); + + mScript = new ScriptC_threshold(mRS, getResources(), R.raw.threshold); + mScript.set_width(mBitmapIn.getWidth()); + mScript.set_height(mBitmapIn.getHeight()); + mScript.set_radius(mRadius); + + mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite); + mScriptVBlur.invoke_setGamma(mGamma); + mScriptVBlur.invoke_setSaturation(mSaturation); + + mScript.bind_InPixel(mInPixelsAllocation); + mScript.bind_OutPixel(mOutPixelsAllocation); + mScript.bind_ScratchPixel1(mScratchPixelsAllocation1); + mScript.bind_ScratchPixel2(mScratchPixelsAllocation2); + + mScript.set_vBlurScript(mScriptVBlur); + mScript.set_hBlurScript(mScriptHBlur); + } + + private Bitmap loadBitmap(int resource) { + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + return copyBitmap(BitmapFactory.decodeResource(getResources(), resource, options)); + } + + private static Bitmap copyBitmap(Bitmap source) { + Bitmap b = Bitmap.createBitmap(source.getWidth(), source.getHeight(), source.getConfig()); + Canvas c = new Canvas(b); + c.drawBitmap(source, 0, 0, null); + source.recycle(); + return b; + } + + // button hook + public void benchmark(View v) { + android.util.Log.v("Img", "Benchmarking"); + int oldRadius = mRadius; + mRadius = MAX_RADIUS; + mScript.set_radius(mRadius); + + long t = java.lang.System.currentTimeMillis(); + + mScript.invoke_filter(); + mOutPixelsAllocation.copyTo(mBitmapOut); + + t = java.lang.System.currentTimeMillis() - t; + android.util.Log.v("Img", "Renderscript frame time core ms " + t); + + //long javaTime = javaFilter(); + //mBenchmarkResult.setText("RS: " + t + " ms Java: " + javaTime + " ms"); + mBenchmarkResult.setText("RS: " + t + " ms"); + + mRadius = oldRadius; + mScript.set_radius(mRadius); + + mScript.invoke_filter(); + mOutPixelsAllocation.copyTo(mBitmapOut); + } +} diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs new file mode 100644 index 000000000000..652ffd76dd3d --- /dev/null +++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs @@ -0,0 +1,30 @@ +#pragma version(1) + +#include "ip.rsh" + +void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) { + float4 *output = (float4 *)v_out; + const FilterStruct *fs = (const FilterStruct *)usrData; + const float4 *input = (const float4 *)rsGetElementAt(fs->ain, 0, y); + + float3 blurredPixel = 0; + const float *gPtr = fs->gaussian; + if ((x > fs->radius) && (x < (fs->width - fs->radius))) { + const float4 *i = input + (x - fs->radius); + for (int r = -fs->radius; r <= fs->radius; r ++) { + blurredPixel += i->xyz * gPtr[0]; + gPtr++; + i++; + } + } else { + for (int r = -fs->radius; r <= fs->radius; r ++) { + // Stepping left and right away from the pixel + int validW = rsClamp(x + r, (uint)0, (uint)(fs->width - 1)); + blurredPixel += input[validW].xyz * gPtr[0]; + gPtr++; + } + } + + output->xyz = blurredPixel; +} + diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ip.rsh b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ip.rsh new file mode 100644 index 000000000000..1d7a7197908d --- /dev/null +++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ip.rsh @@ -0,0 +1,15 @@ +#pragma rs java_package_name(com.android.rs.image) + +#define MAX_RADIUS 25 + +typedef struct FilterStruct_s { + rs_allocation ain; + + float *gaussian; //[MAX_RADIUS * 2 + 1]; + int height; + int width; + int radius; + +} FilterStruct; + + diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs new file mode 100644 index 000000000000..f2f9a361fa90 --- /dev/null +++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs @@ -0,0 +1,93 @@ +#pragma version(1) + +#include "ip.rsh" + +int height; +int width; +int radius; + +uchar4 * InPixel; +uchar4 * OutPixel; +float4 * ScratchPixel1; +float4 * ScratchPixel2; + +rs_script vBlurScript; +rs_script hBlurScript; + +const int CMD_FINISHED = 1; + +// Store our coefficients here +static float gaussian[MAX_RADIUS * 2 + 1]; + + +static void computeGaussianWeights() { + // Compute gaussian weights for the blur + // e is the euler's number + float e = 2.718281828459045f; + float pi = 3.1415926535897932f; + // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) + // x is of the form [-radius .. 0 .. radius] + // and sigma varies with radius. + // Based on some experimental radius values and sigma's + // we approximately fit sigma = f(radius) as + // sigma = radius * 0.4 + 0.6 + // The larger the radius gets, the more our gaussian blur + // will resemble a box blur since with large sigma + // the gaussian curve begins to lose its shape + float sigma = 0.4f * (float)radius + 0.6f; + + // Now compute the coefficints + // We will store some redundant values to save some math during + // the blur calculations + // precompute some values + float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); + float coeff2 = - 1.0f / (2.0f * sigma * sigma); + + float normalizeFactor = 0.0f; + float floatR = 0.0f; + int r; + for (r = -radius; r <= radius; r ++) { + floatR = (float)r; + gaussian[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); + normalizeFactor += gaussian[r + radius]; + } + + //Now we need to normalize the weights because all our coefficients need to add up to one + normalizeFactor = 1.0f / normalizeFactor; + for (r = -radius; r <= radius; r ++) { + floatR = (float)r; + gaussian[r + radius] *= normalizeFactor; + } +} + + +static void copyInput() { + rs_allocation ain; + rsSetObject(&ain,rsGetAllocation(InPixel)); + uint32_t dimx = rsAllocationGetDimX(ain); + uint32_t dimy = rsAllocationGetDimY(ain); + for (uint32_t y = 0; y < dimy; y++) { + for (uint32_t x = 0; x < dimx; x++) { + ScratchPixel1[x + y * dimx] = convert_float4(InPixel[x + y * dimx]); + } + } +} + +void filter() { + copyInput(); + computeGaussianWeights(); + + FilterStruct fs; + fs.gaussian = gaussian; + fs.width = width; + fs.height = height; + fs.radius = radius; + + fs.ain = rsGetAllocation(ScratchPixel1); + rsForEach(hBlurScript, fs.ain, rsGetAllocation(ScratchPixel2), &fs); + + fs.ain = rsGetAllocation(ScratchPixel2); + rsForEach(vBlurScript, fs.ain, rsGetAllocation(OutPixel), &fs); + rsSendToClientBlocking(CMD_FINISHED); +} + diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs new file mode 100644 index 000000000000..bd4ae4eca3b7 --- /dev/null +++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs @@ -0,0 +1,93 @@ +#pragma version(1) + +#include "ip.rsh" + +static float inBlack; +static float outBlack; +static float inWhite; +static float outWhite; +static float3 gamma; +static float saturation; + +static float inWMinInB; +static float outWMinOutB; +static float overInWMinInB; +static rs_matrix3x3 colorMat; + +void setLevels(float iBlk, float oBlk, float iWht, float oWht) { + inBlack = iBlk; + outBlack = oBlk; + inWhite = iWht; + outWhite = oWht; + + inWMinInB = inWhite - inBlack; + outWMinOutB = outWhite - outBlack; + overInWMinInB = 1.f / inWMinInB; +} + +void setSaturation(float sat) { + saturation = sat; + + // Saturation + // Linear weights + //float rWeight = 0.3086f; + //float gWeight = 0.6094f; + //float bWeight = 0.0820f; + + // Gamma 2.2 weights (we haven't converted our image to linear space yet for perf reasons) + float rWeight = 0.299f; + float gWeight = 0.587f; + float bWeight = 0.114f; + + float oneMinusS = 1.0f - saturation; + rsMatrixSet(&colorMat, 0, 0, oneMinusS * rWeight + saturation); + rsMatrixSet(&colorMat, 0, 1, oneMinusS * rWeight); + rsMatrixSet(&colorMat, 0, 2, oneMinusS * rWeight); + rsMatrixSet(&colorMat, 1, 0, oneMinusS * gWeight); + rsMatrixSet(&colorMat, 1, 1, oneMinusS * gWeight + saturation); + rsMatrixSet(&colorMat, 1, 2, oneMinusS * gWeight); + rsMatrixSet(&colorMat, 2, 0, oneMinusS * bWeight); + rsMatrixSet(&colorMat, 2, 1, oneMinusS * bWeight); + rsMatrixSet(&colorMat, 2, 2, oneMinusS * bWeight + saturation); +} + +void setGamma(float g) { + gamma = (float3)g; +} + +//sliao +extern uchar3 __attribute__((overloadable)) convert2uchar3(float3 xyz); + +void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) { + uchar4 *output = (uchar4 *)v_out; + const FilterStruct *fs = (const FilterStruct *)usrData; + const float4 *input = (const float4 *)rsGetElementAt(fs->ain, x, 0); + + float3 blurredPixel = 0; + const float *gPtr = fs->gaussian; + if ((y > fs->radius) && (y < (fs->height - fs->radius))) { + const float4 *i = input + ((y - fs->radius) * fs->width); + for (int r = -fs->radius; r <= fs->radius; r ++) { + blurredPixel += i->xyz * gPtr[0]; + gPtr++; + i += fs->width; + } + } else { + for (int r = -fs->radius; r <= fs->radius; r ++) { + int validH = rsClamp(y + r, (uint)0, (uint)(fs->height - 1)); + const float4 *i = input + validH * fs->width; + blurredPixel += i->xyz * gPtr[0]; + gPtr++; + } + } + + float3 temp = rsMatrixMultiply(&colorMat, blurredPixel); + temp = (clamp(temp, 0.f, 255.f) - inBlack) * overInWMinInB; + if (gamma.x != 1.0f) + temp = pow(temp, (float3)gamma); + temp = clamp(temp * outWMinOutB + outBlack, 0.f, 255.f); + + output->xyz = convert_uchar3(temp); + //output->w = input->w; +} + diff --git a/libs/rs/java/ModelViewer/Android.mk b/libs/rs/java/ModelViewer/Android.mk new file mode 100644 index 000000000000..efe77d7ac73b --- /dev/null +++ b/libs/rs/java/ModelViewer/Android.mk @@ -0,0 +1,31 @@ +# +# 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. +# + +ifneq ($(TARGET_SIMULATOR),true) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src) +#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript + +LOCAL_PACKAGE_NAME := ModelViewer + +include $(BUILD_PACKAGE) + +endif diff --git a/libs/rs/java/ModelViewer/AndroidManifest.xml b/libs/rs/java/ModelViewer/AndroidManifest.xml new file mode 100644 index 000000000000..e5e449b31573 --- /dev/null +++ b/libs/rs/java/ModelViewer/AndroidManifest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.modelviewer"> + <application android:label="ModelViewer"> + <activity android:name="SimpleModel" + android:label="SimpleModel"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity android:name="A3DSelector" + android:label="A3DSelector" + android:hardwareAccelerated="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + <activity android:name="SceneGraph" + android:label="SceneGraph" + android:theme="@android:style/Theme.Black.NoTitleBar"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/libs/rs/java/ModelViewer/res/drawable/robot.png b/libs/rs/java/ModelViewer/res/drawable/robot.png Binary files differnew file mode 100644 index 000000000000..f7353fd61c5b --- /dev/null +++ b/libs/rs/java/ModelViewer/res/drawable/robot.png diff --git a/libs/rs/java/ModelViewer/res/menu/loader_menu.xml b/libs/rs/java/ModelViewer/res/menu/loader_menu.xml new file mode 100644 index 000000000000..3c340238b778 --- /dev/null +++ b/libs/rs/java/ModelViewer/res/menu/loader_menu.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +--> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/load_model" + android:title="@string/load_model" /> + <item android:id="@+id/display_options" + android:title="@string/display_options" /> +</menu> diff --git a/libs/rs/java/ModelViewer/res/raw/robot.a3d b/libs/rs/java/ModelViewer/res/raw/robot.a3d Binary files differnew file mode 100644 index 000000000000..f48895cd8451 --- /dev/null +++ b/libs/rs/java/ModelViewer/res/raw/robot.a3d diff --git a/libs/rs/java/ModelViewer/res/values/strings.xml b/libs/rs/java/ModelViewer/res/values/strings.xml new file mode 100644 index 000000000000..8e1673f41179 --- /dev/null +++ b/libs/rs/java/ModelViewer/res/values/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <skip /> + <string name="load_model">Load Model</string> + <string name="display_options">Display Options</string> +</resources> diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/A3DSelector.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/A3DSelector.java new file mode 100644 index 000000000000..0e2004f2046f --- /dev/null +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/A3DSelector.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.modelviewer; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.List; + +import android.app.ListActivity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +/** + * A list view where the last item the user clicked is placed in + * the "activated" state, causing its background to highlight. + */ +public class A3DSelector extends ListActivity { + + File[] mCurrentSubList; + File mCurrentFile; + + class A3DFilter implements FileFilter { + public boolean accept(File file) { + if (file.isDirectory()) { + return true; + } + return file.getName().endsWith(".a3d"); + } + } + + private void populateList(File file) { + + mCurrentFile = file; + setTitle(mCurrentFile.getAbsolutePath() + "/*.a3d"); + List<String> names = new ArrayList<String>(); + names.add(".."); + + mCurrentSubList = mCurrentFile.listFiles(new A3DFilter()); + + if (mCurrentSubList != null) { + for (int i = 0; i < mCurrentSubList.length; i ++) { + String fileName = mCurrentSubList[i].getName(); + if (mCurrentSubList[i].isDirectory()) { + fileName = "/" + fileName; + } + names.add(fileName); + } + } + + // Use the built-in layout for showing a list item with a single + // line of text whose background is changes when activated. + setListAdapter(new ArrayAdapter<String>(this, + android.R.layout.simple_list_item_activated_1, names)); + getListView().setTextFilterEnabled(true); + + // Tell the list view to show one checked/activated item at a time. + getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + populateList(new File("/sdcard/")); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + if (position == 0) { + File parent = mCurrentFile.getParentFile(); + if (parent == null) { + return; + } + populateList(parent); + return; + } + + // the first thing in list is parent directory + File selectedFile = mCurrentSubList[position - 1]; + if (selectedFile.isDirectory()) { + populateList(selectedFile); + return; + } + + Intent resultIntent = new Intent(); + resultIntent.setData(Uri.fromFile(selectedFile)); + setResult(RESULT_OK, resultIntent); + finish(); + } + +} diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraph.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraph.java new file mode 100644 index 000000000000..b8717a732209 --- /dev/null +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraph.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.modelviewer; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; + +import android.app.Activity; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings.System; +import android.util.Config; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.ListView; + +import java.lang.Runtime; + +public class SceneGraph extends Activity { + + private SceneGraphView mView; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + // Create our Preview view and set it as the content of our + // Activity + mView = new SceneGraphView(this); + setContentView(mView); + } + + @Override + protected void onResume() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity looses focus + super.onResume(); + mView.resume(); + } + + @Override + protected void onPause() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity looses focus + super.onPause(); + mView.pause(); + } + +} + diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java new file mode 100644 index 000000000000..f91f31ee35e7 --- /dev/null +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.modelviewer; + +import java.io.Writer; +import java.util.Map; +import java.util.Vector; + +import android.content.res.Resources; +import android.renderscript.*; +import android.renderscript.Element.Builder; +import android.renderscript.Font.Style; +import android.renderscript.ProgramStore.DepthFunc; +import android.util.Log; + + +public class SceneGraphRS { + + private final int STATE_LAST_FOCUS = 1; + + int mWidth; + int mHeight; + int mRotation; + + public SceneGraphRS() { + } + + public void init(RenderScriptGL rs, Resources res, int width, int height) { + mRS = rs; + mRes = res; + mWidth = width; + mHeight = height; + mRotation = 0; + initRS(); + } + + private Resources mRes; + private RenderScriptGL mRS; + private Sampler mSampler; + private ProgramStore mPSBackground; + private ProgramFragment mPFBackground; + private ProgramVertex mPVBackground; + private ProgramVertexFixedFunction.Constants mPVA; + + private Allocation mGridImage; + private Allocation mAllocPV; + + private Mesh mMesh; + + private Font mItalic; + private Allocation mTextAlloc; + + private ScriptC_scenegraph mScript; + private ScriptC_transform mTransformScript; + + int mLastX; + int mLastY; + + public void touchEvent(int x, int y) { + int dx = mLastX - x; + if (Math.abs(dx) > 50 || Math.abs(dx) < 3) { + dx = 0; + } + + mRotation -= dx; + if (mRotation > 360) { + mRotation -= 360; + } + if (mRotation < 0) { + mRotation += 360; + } + + mScript.set_gRotate(-(float)mRotation); + + mLastX = x; + mLastY = y; + } + + private void initPFS() { + ProgramStore.Builder b = new ProgramStore.Builder(mRS); + + b.setDepthFunc(ProgramStore.DepthFunc.LESS); + b.setDitherEnabled(false); + b.setDepthMaskEnabled(true); + mPSBackground = b.create(); + + mScript.set_gPFSBackground(mPSBackground); + } + + private void initPF() { + Sampler.Builder bs = new Sampler.Builder(mRS); + bs.setMinification(Sampler.Value.LINEAR); + bs.setMagnification(Sampler.Value.LINEAR); + bs.setWrapS(Sampler.Value.CLAMP); + bs.setWrapT(Sampler.Value.CLAMP); + mSampler = bs.create(); + + ProgramFragmentFixedFunction.Builder b = new ProgramFragmentFixedFunction.Builder(mRS); + b.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE, + ProgramFragmentFixedFunction.Builder.Format.RGBA, 0); + mPFBackground = b.create(); + mPFBackground.bindSampler(mSampler, 0); + + mScript.set_gPFBackground(mPFBackground); + } + + private void initPV() { + ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS); + mPVBackground = pvb.create(); + + mPVA = new ProgramVertexFixedFunction.Constants(mRS); + ((ProgramVertexFixedFunction)mPVBackground).bindConstants(mPVA); + + mScript.set_gPVBackground(mPVBackground); + } + + private void loadImage() { + mGridImage = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot, + Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, + Allocation.USAGE_GRAPHICS_TEXTURE); + mScript.set_gTGrid(mGridImage); + } + + private void initTextAllocation() { + String allocString = "Displaying file: R.raw.robot"; + mTextAlloc = Allocation.createFromString(mRS, allocString, Allocation.USAGE_SCRIPT); + mScript.set_gTextAlloc(mTextAlloc); + } + + SgTransform mRootTransform; + SgTransform mGroup1; + + SgTransform mRobot1; + SgTransform mRobot2; + + void initTransformHierarchy() { + mRootTransform = new SgTransform(mRS); + + mGroup1 = new SgTransform(mRS); + mRootTransform.addChild(mGroup1); + + mRobot1 = new SgTransform(mRS); + mRobot2 = new SgTransform(mRS); + + mGroup1.addChild(mRobot1); + mGroup1.addChild(mRobot2); + + mGroup1.setTransform(0, new Float4(0.0f, 0.0f, -15.0f, 0.0f), TransformType.TRANSLATE); + mGroup1.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, 15.0f), TransformType.ROTATE); + + mRobot1.setTransform(0, new Float4(-3.0f, -0.5f, 0.0f, 0.0f), TransformType.TRANSLATE); + mRobot1.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, 20.0f), TransformType.ROTATE); + mRobot1.setTransform(2, new Float4(0.2f, 0.2f, 0.2f, 0.0f), TransformType.SCALE); + + mRobot2.setTransform(0, new Float4(3.0f, 0.0f, 0.0f, 0.0f), TransformType.TRANSLATE); + mRobot2.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, -20.0f), TransformType.ROTATE); + mRobot2.setTransform(2, new Float4(0.3f, 0.3f, 0.3f, 0.0f), TransformType.SCALE); + } + + private void initRS() { + + mScript = new ScriptC_scenegraph(mRS, mRes, R.raw.scenegraph); + mTransformScript = new ScriptC_transform(mRS, mRes, R.raw.transform); + mTransformScript.set_transformScript(mTransformScript); + + mScript.set_gTransformRS(mTransformScript); + + initPFS(); + initPF(); + initPV(); + + loadImage(); + + FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot); + FileA3D.IndexEntry entry = model.getIndexEntry(0); + if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) { + Log.e("rs", "could not load model"); + } else { + mMesh = (Mesh)entry.getObject(); + mScript.set_gTestMesh(mMesh); + } + + mItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8); + mScript.set_gItalic(mItalic); + + initTextAllocation(); + + initTransformHierarchy(); + + mScript.bind_gRootNode(mRootTransform.getField()); + + mScript.bind_gGroup(mGroup1.mParent.mChildField); + mScript.bind_gRobot1(mRobot1.mParent.mChildField); + mScript.set_gRobot1Index(mRobot1.mIndexInParentGroup); + mScript.bind_gRobot2(mRobot2.mParent.mChildField); + mScript.set_gRobot2Index(mRobot2.mIndexInParentGroup); + + mRS.bindRootScript(mScript); + } +} + + + diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphView.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphView.java new file mode 100644 index 000000000000..0b6a3b863866 --- /dev/null +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphView.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.modelviewer; + +import java.io.Writer; +import java.util.ArrayList; +import java.util.concurrent.Semaphore; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; +import android.renderscript.RenderScriptGL; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.KeyEvent; +import android.view.MotionEvent; + +public class SceneGraphView extends RSSurfaceView { + + public SceneGraphView(Context context) { + super(context); + //setFocusable(true); + } + + private RenderScriptGL mRS; + private SceneGraphRS mRender; + + + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + super.surfaceChanged(holder, format, w, h); + if (mRS == null) { + RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig(); + sc.setDepth(16, 24); + mRS = createRenderScriptGL(sc); + mRS.setSurface(holder, w, h); + mRender = new SceneGraphRS(); + mRender.init(mRS, getResources(), w, h); + } + } + + @Override + protected void onDetachedFromWindow() { + if (mRS != null) { + mRS = null; + destroyRenderScriptGL(); + } + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + // break point at here + // this method doesn't work when 'extends View' include 'extends ScrollView'. + return super.onKeyDown(keyCode, event); + } + + + @Override + public boolean onTouchEvent(MotionEvent ev) + { + boolean ret = true; + int act = ev.getAction(); + if (act == ev.ACTION_UP) { + ret = false; + } + + mRender.touchEvent((int)ev.getX(), (int)ev.getY()); + return ret; + } +} + + diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java new file mode 100644 index 000000000000..f5484e29c424 --- /dev/null +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.modelviewer; + +import java.io.Writer; +import java.util.Map; +import java.util.Vector; + +import android.content.res.Resources; +import android.renderscript.*; +import android.renderscript.Element.Builder; +import android.renderscript.ProgramStore.DepthFunc; +import android.util.Log; + +enum TransformType { + + NONE(0), + TRANSLATE(1), + ROTATE(2), + SCALE(3); + + int mID; + TransformType(int id) { + mID = id; + } +} + +public class SgTransform { + + + ScriptField_SgTransform mTransformField; + ScriptField_SgTransform mChildField; + public ScriptField_SgTransform.Item mTransformData; + + RenderScript mRS; + + Vector mChildren; + SgTransform mParent; + int mIndexInParentGroup; + + public void setParent(SgTransform parent, int parentIndex) { + mParent = parent; + mIndexInParentGroup = parentIndex; + } + + public void addChild(SgTransform child) { + mChildren.add(child); + child.setParent(this, mChildren.size() - 1); + } + + public void setTransform(int index, Float4 value, TransformType type) { + mTransformData.transforms[index] = value; + mTransformData.transformTypes[index] = type.mID; + } + + void initData() { + int numElements = mTransformData.transforms.length; + mTransformData.transformTypes = new int[numElements]; + for (int i = 0; i < numElements; i ++) { + mTransformData.transforms[i] = new Float4(0, 0, 0, 0); + mTransformData.transformTypes[i] = TransformType.NONE.mID; + } + + mTransformData.isDirty = 1; + mTransformData.children = null; + } + + public SgTransform(RenderScript rs) { + mRS = rs; + mTransformData = new ScriptField_SgTransform.Item(); + mChildren = new Vector(); + initData(); + } + + public ScriptField_SgTransform.Item getData() { + if (mChildren.size() != 0) { + mChildField = new ScriptField_SgTransform(mRS, mChildren.size()); + mTransformData.children = mChildField.getAllocation(); + + for (int i = 0; i < mChildren.size(); i ++) { + SgTransform child = (SgTransform)mChildren.get(i); + mChildField.set(child.getData(), i, false); + } + mChildField.copyAll(); + } + + return mTransformData; + } + + public ScriptField_SgTransform getField() { + mTransformField = new ScriptField_SgTransform(mRS, 1); + mTransformField.set(getData(), 0, true); + return mTransformField; + } +} + + + diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java new file mode 100644 index 000000000000..01cb70996237 --- /dev/null +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.modelviewer; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; + +import android.app.Activity; +import android.content.res.Configuration; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings.System; +import android.util.Config; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.MenuInflater; +import android.view.Window; +import android.widget.Button; +import android.widget.ListView; +import android.net.Uri; + +import java.lang.Runtime; + +public class SimpleModel extends Activity { + + private SimpleModelView mView; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + // Create our Preview view and set it as the content of our + // Activity + mView = new SimpleModelView(this); + setContentView(mView); + } + + @Override + protected void onResume() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity looses focus + super.onResume(); + mView.resume(); + } + + @Override + protected void onPause() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity looses focus + super.onPause(); + mView.pause(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.loader_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle item selection + switch (item.getItemId()) { + case R.id.load_model: + loadModel(); + return true; + case R.id.display_options: + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private static final int FIND_A3D_MODEL = 10; + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == RESULT_OK) { + if (requestCode == FIND_A3D_MODEL) { + Uri selectedImageUri = data.getData(); + Log.e("Selected Path: ", selectedImageUri.getPath()); + mView.loadA3DFile(selectedImageUri.getPath()); + } + } + } + + public void loadModel() { + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_PICK); + intent.setClassName("com.android.modelviewer", + "com.android.modelviewer.A3DSelector"); + startActivityForResult(intent, FIND_A3D_MODEL); + } + +} + diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java new file mode 100644 index 000000000000..63ef4666a587 --- /dev/null +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.modelviewer; + +import java.io.Writer; + +import android.content.res.Resources; +import android.renderscript.*; +import android.renderscript.ProgramStore.DepthFunc; +import android.util.Log; + + +public class SimpleModelRS { + + public SimpleModelRS() { + } + + public void init(RenderScriptGL rs, Resources res) { + mRS = rs; + mRes = res; + initRS(); + } + + public void surfaceChanged() { + mRS.getWidth(); + mRS.getHeight(); + } + + private Resources mRes; + private RenderScriptGL mRS; + private Sampler mSampler; + private ProgramStore mPSBackground; + private ProgramFragment mPFBackground; + private ProgramVertex mPVBackground; + private ProgramVertexFixedFunction.Constants mPVA; + + private Allocation mGridImage; + private Allocation mAllocPV; + + private Font mItalic; + private Allocation mTextAlloc; + + private ScriptField_MeshInfo mMeshes; + private ScriptC_simplemodel mScript; + + + public void onActionDown(float x, float y) { + mScript.invoke_onActionDown(x, y); + } + + public void onActionScale(float scale) { + mScript.invoke_onActionScale(scale); + } + + public void onActionMove(float x, float y) { + mScript.invoke_onActionMove(x, y); + } + + private void initPFS() { + ProgramStore.Builder b = new ProgramStore.Builder(mRS); + + b.setDepthFunc(ProgramStore.DepthFunc.LESS); + b.setDitherEnabled(false); + b.setDepthMaskEnabled(true); + mPSBackground = b.create(); + + mScript.set_gPFSBackground(mPSBackground); + } + + private void initPF() { + Sampler.Builder bs = new Sampler.Builder(mRS); + bs.setMinification(Sampler.Value.LINEAR); + bs.setMagnification(Sampler.Value.LINEAR); + bs.setWrapS(Sampler.Value.CLAMP); + bs.setWrapT(Sampler.Value.CLAMP); + mSampler = bs.create(); + + ProgramFragmentFixedFunction.Builder b = new ProgramFragmentFixedFunction.Builder(mRS); + b.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE, + ProgramFragmentFixedFunction.Builder.Format.RGBA, 0); + mPFBackground = b.create(); + mPFBackground.bindSampler(mSampler, 0); + + mScript.set_gPFBackground(mPFBackground); + } + + private void initPV() { + ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS); + mPVBackground = pvb.create(); + + mPVA = new ProgramVertexFixedFunction.Constants(mRS); + ((ProgramVertexFixedFunction)mPVBackground).bindConstants(mPVA); + + mScript.set_gPVBackground(mPVBackground); + } + + private void loadImage() { + mGridImage = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot, + Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, + Allocation.USAGE_GRAPHICS_TEXTURE); + mScript.set_gTGrid(mGridImage); + } + + private void initTextAllocation(String fileName) { + String allocString = "Displaying file: " + fileName; + mTextAlloc = Allocation.createFromString(mRS, allocString, Allocation.USAGE_SCRIPT); + mScript.set_gTextAlloc(mTextAlloc); + } + + private void initMeshes(FileA3D model) { + int numEntries = model.getIndexEntryCount(); + int numMeshes = 0; + for (int i = 0; i < numEntries; i ++) { + FileA3D.IndexEntry entry = model.getIndexEntry(i); + if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) { + numMeshes ++; + } + } + + if (numMeshes > 0) { + mMeshes = new ScriptField_MeshInfo(mRS, numMeshes); + + for (int i = 0; i < numEntries; i ++) { + FileA3D.IndexEntry entry = model.getIndexEntry(i); + if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) { + Mesh mesh = entry.getMesh(); + mMeshes.set_mMesh(i, mesh, false); + mMeshes.set_mNumIndexSets(i, mesh.getPrimitiveCount(), false); + } + } + mMeshes.copyAll(); + } else { + throw new RSRuntimeException("No valid meshes in file"); + } + + mScript.bind_gMeshes(mMeshes); + mScript.invoke_updateMeshInfo(); + } + + public void loadA3DFile(String path) { + FileA3D model = FileA3D.createFromFile(mRS, path); + initMeshes(model); + initTextAllocation(path); + } + + private void initRS() { + + mScript = new ScriptC_simplemodel(mRS, mRes, R.raw.simplemodel); + + initPFS(); + initPF(); + initPV(); + + loadImage(); + + FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot); + initMeshes(model); + + mItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8); + mScript.set_gItalic(mItalic); + + initTextAllocation("R.raw.robot"); + + mRS.bindRootScript(mScript); + } +} + + + diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java new file mode 100644 index 000000000000..9ab0f22f9129 --- /dev/null +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.modelviewer; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScriptGL; + +import android.content.Context; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.ScaleGestureDetector; +import android.util.Log; + +public class SimpleModelView extends RSSurfaceView { + + private RenderScriptGL mRS; + private SimpleModelRS mRender; + + private ScaleGestureDetector mScaleDetector; + + private static final int INVALID_POINTER_ID = -1; + private int mActivePointerId = INVALID_POINTER_ID; + + public SimpleModelView(Context context) { + super(context); + ensureRenderScript(); + mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); + } + + private void ensureRenderScript() { + if (mRS == null) { + RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig(); + sc.setDepth(16, 24); + mRS = createRenderScriptGL(sc); + mRender = new SimpleModelRS(); + mRender.init(mRS, getResources()); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + ensureRenderScript(); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + super.surfaceChanged(holder, format, w, h); + mRender.surfaceChanged(); + } + + @Override + protected void onDetachedFromWindow() { + mRender = null; + if (mRS != null) { + mRS = null; + destroyRenderScriptGL(); + } + } + + public void loadA3DFile(String path) { + mRender.loadA3DFile(path); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + mScaleDetector.onTouchEvent(ev); + + boolean ret = false; + float x = ev.getX(); + float y = ev.getY(); + + final int action = ev.getAction(); + + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: { + mRender.onActionDown(x, y); + mActivePointerId = ev.getPointerId(0); + ret = true; + break; + } + case MotionEvent.ACTION_MOVE: { + if (!mScaleDetector.isInProgress()) { + mRender.onActionMove(x, y); + } + mRender.onActionDown(x, y); + ret = true; + break; + } + + case MotionEvent.ACTION_UP: { + mActivePointerId = INVALID_POINTER_ID; + break; + } + + case MotionEvent.ACTION_CANCEL: { + mActivePointerId = INVALID_POINTER_ID; + break; + } + + case MotionEvent.ACTION_POINTER_UP: { + final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) + >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + final int pointerId = ev.getPointerId(pointerIndex); + if (pointerId == mActivePointerId) { + // This was our active pointer going up. Choose a new + // active pointer and adjust accordingly. + final int newPointerIndex = pointerIndex == 0 ? 1 : 0; + x = ev.getX(newPointerIndex); + y = ev.getY(newPointerIndex); + mRender.onActionDown(x, y); + mActivePointerId = ev.getPointerId(newPointerIndex); + } + break; + } + } + + return ret; + } + + private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { + @Override + public boolean onScale(ScaleGestureDetector detector) { + mRender.onActionScale(detector.getScaleFactor()); + return true; + } + } +} + + diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs new file mode 100644 index 000000000000..36790686feca --- /dev/null +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs @@ -0,0 +1,91 @@ +// Copyright (C) 2009 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma version(1) + +#pragma rs java_package_name(com.android.modelviewer) + +#include "rs_graphics.rsh" +#include "transform_def.rsh" + +rs_program_vertex gPVBackground; +rs_program_fragment gPFBackground; + +rs_allocation gTGrid; +rs_mesh gTestMesh; + +rs_program_store gPFSBackground; + +float gRotate; + +rs_font gItalic; +rs_allocation gTextAlloc; + +rs_script gTransformRS; + +SgTransform *gGroup; +SgTransform *gRobot1; +int gRobot1Index; +SgTransform *gRobot2; +int gRobot2Index; + +SgTransform *gRootNode; + +void init() { + gRotate = 0.0f; +} + +int root(int launchID) { + + gGroup->transforms[1].w += 0.5f; + gGroup->isDirty = 1; + + SgTransform *robot1Ptr = gRobot1 + gRobot1Index; + + robot1Ptr->transforms[1].w -= 1.5f; + robot1Ptr->isDirty = 1; + + SgTransform *robot2Ptr = gRobot2 + gRobot2Index; + robot2Ptr->transforms[1].w += 2.5f; + robot2Ptr->isDirty = 1; + + rsForEach(gTransformRS, gRootNode->children, gRootNode->children, 0); + + rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f); + rsgClearDepth(1.0f); + + rsgBindProgramVertex(gPVBackground); + rs_matrix4x4 proj; + float aspect = (float)rsgGetWidth() / (float)rsgGetHeight(); + rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f); + rsgProgramVertexLoadProjectionMatrix(&proj); + + rsgBindProgramFragment(gPFBackground); + rsgBindProgramStore(gPFSBackground); + rsgBindTexture(gPFBackground, 0, gTGrid); + + rsgProgramVertexLoadModelMatrix(&robot1Ptr->globalMat); + rsgDrawMesh(gTestMesh); + + rsgProgramVertexLoadModelMatrix(&robot2Ptr->globalMat); + rsgDrawMesh(gTestMesh); + + //color(0.3f, 0.3f, 0.3f, 1.0f); + rsgDrawText("Renderscript transform test", 30, 695); + + rsgBindFont(gItalic); + rsgDrawText(gTextAlloc, 30, 730); + + return 10; +} diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs new file mode 100644 index 000000000000..1034b85c55d7 --- /dev/null +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs @@ -0,0 +1,161 @@ +// Copyright (C) 2011 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma version(1) + +#pragma rs java_package_name(com.android.modelviewer) + +#include "rs_graphics.rsh" + +rs_program_vertex gPVBackground; +rs_program_fragment gPFBackground; + +rs_allocation gTGrid; + +rs_program_store gPFSBackground; + +rs_font gItalic; +rs_allocation gTextAlloc; + +typedef struct MeshInfo { + rs_mesh mMesh; + int mNumIndexSets; + float3 bBoxMin; + float3 bBoxMax; +} MeshInfo_t; + +MeshInfo_t *gMeshes; + +static float3 gLookAt; + +static float gRotateX; +static float gRotateY; +static float gZoom; + +static float gLastX; +static float gLastY; + +void onActionDown(float x, float y) { + gLastX = x; + gLastY = y; +} + +void onActionScale(float scale) { + + gZoom *= 1.0f / scale; + gZoom = max(0.1f, min(gZoom, 500.0f)); +} + +void onActionMove(float x, float y) { + float dx = gLastX - x; + float dy = gLastY - y; + + if (fabs(dy) <= 2.0f) { + dy = 0.0f; + } + if (fabs(dx) <= 2.0f) { + dx = 0.0f; + } + + gRotateY -= dx; + if (gRotateY > 360) { + gRotateY -= 360; + } + if (gRotateY < 0) { + gRotateY += 360; + } + + gRotateX -= dy; + gRotateX = min(gRotateX, 80.0f); + gRotateX = max(gRotateX, -80.0f); + + gLastX = x; + gLastY = y; +} + +void init() { + gRotateX = 0.0f; + gRotateY = 0.0f; + gZoom = 50.0f; + gLookAt = 0.0f; +} + +void updateMeshInfo() { + rs_allocation allMeshes = rsGetAllocation(gMeshes); + int size = rsAllocationGetDimX(allMeshes); + gLookAt = 0.0f; + float minX, minY, minZ, maxX, maxY, maxZ; + for (int i = 0; i < size; i++) { + MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i); + rsgMeshComputeBoundingBox(info->mMesh, + &minX, &minY, &minZ, + &maxX, &maxY, &maxZ); + info->bBoxMin = (minX, minY, minZ); + info->bBoxMax = (maxX, maxY, maxZ); + gLookAt += (info->bBoxMin + info->bBoxMax)*0.5f; + } + gLookAt = gLookAt / (float)size; +} + +static void renderAllMeshes() { + rs_allocation allMeshes = rsGetAllocation(gMeshes); + int size = rsAllocationGetDimX(allMeshes); + gLookAt = 0.0f; + float minX, minY, minZ, maxX, maxY, maxZ; + for (int i = 0; i < size; i++) { + MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i); + rsgDrawMesh(info->mMesh); + } +} + +void drawDescription() { + uint width = rsgGetWidth(); + uint height = rsgGetHeight(); + int left = 0, right = 0, top = 0, bottom = 0; + + rsgBindFont(gItalic); + + rsgMeasureText(gTextAlloc, &left, &right, &top, &bottom); + rsgDrawText(gTextAlloc, 2 -left, height - 2 + bottom); +} + +int root(int launchID) { + + rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f); + rsgClearDepth(1.0f); + + rsgBindProgramVertex(gPVBackground); + rs_matrix4x4 proj; + float aspect = (float)rsgGetWidth() / (float)rsgGetHeight(); + rsMatrixLoadPerspective(&proj, 30.0f, aspect, 1.0f, 100.0f); + rsgProgramVertexLoadProjectionMatrix(&proj); + + rsgBindProgramFragment(gPFBackground); + rsgBindProgramStore(gPFSBackground); + rsgBindTexture(gPFBackground, 0, gTGrid); + + rs_matrix4x4 matrix; + rsMatrixLoadIdentity(&matrix); + // Position our models on the screen + rsMatrixTranslate(&matrix, gLookAt.x, gLookAt.y, gLookAt.z - gZoom); + rsMatrixRotate(&matrix, gRotateX, 1.0f, 0.0f, 0.0f); + rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f); + rsgProgramVertexLoadModelMatrix(&matrix); + + renderAllMeshes(); + + drawDescription(); + + return 10; +} diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs new file mode 100644 index 000000000000..f328025ecf96 --- /dev/null +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs @@ -0,0 +1,96 @@ +// Copyright (C) 2009 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma version(1) + +#pragma rs java_package_name(com.android.modelviewer) + +#include "transform_def.rsh" + +rs_script transformScript; + +typedef struct { + int changed; + rs_matrix4x4 *mat; +} ParentData; + +static void appendTransformation(int type, float4 data, rs_matrix4x4 *mat) { + rs_matrix4x4 temp; + + switch (type) { + case TRANSFORM_TRANSLATE: + rsMatrixLoadTranslate(&temp, data.x, data.y, data.z); + break; + case TRANSFORM_ROTATE: + rsMatrixLoadRotate(&temp, data.w, data.x, data.y, data.z); + break; + case TRANSFORM_SCALE: + rsMatrixLoadScale(&temp, data.x, data.y, data.z); + break; + } + rsMatrixMultiply(mat, &temp); +} + +void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) { + + SgTransform *data = (SgTransform *)v_out; + const ParentData *parent = (const ParentData *)usrData; + + //rsDebug("Transform data", (int)data); + //rsDebug("Entering parent", (int)parent); + + rs_matrix4x4 *localMat = &data->localMat; + rs_matrix4x4 *globalMat = &data->globalMat; + + ParentData toChild; + toChild.changed = 0; + toChild.mat = globalMat; + + //rsDebug("Transform is dirty", data->isDirty); + + // Refresh matrices if dirty + if (data->isDirty) { + data->isDirty = 0; + toChild.changed = 1; + + // Reset our local matrix + rsMatrixLoadIdentity(localMat); + + for (int i = 0; i < 16; i ++) { + if (data->transformTypes[i] == TRANSFORM_NONE) { + break; + } + //rsDebug("Transform adding transformation", transformTypes[i]); + appendTransformation(data->transformTypes[i], data->transforms[i], localMat); + } + } + + //rsDebug("Transform checking parent", (int)0); + + if (parent) { + if (parent->changed) { + toChild.changed = 1; + + rsMatrixLoad(globalMat, parent->mat); + rsMatrixMultiply(globalMat, localMat); + } + } else { + rsMatrixLoad(globalMat, localMat); + } + + //rsDebug("Transform calling self with child ", (int)data->children.p); + if (data->children.p) { + rsForEach(transformScript, data->children, data->children, (void*)&toChild); + } +} diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh new file mode 100644 index 000000000000..24a36c1db28e --- /dev/null +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh @@ -0,0 +1,35 @@ +// Copyright (C) 2009 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma version(1) + +#pragma rs java_package_name(com.android.modelviewer) + +#define TRANSFORM_NONE 0 +#define TRANSFORM_TRANSLATE 1 +#define TRANSFORM_ROTATE 2 +#define TRANSFORM_SCALE 3 + +typedef struct __attribute__((packed, aligned(4))) SgTransform { + rs_matrix4x4 globalMat; + rs_matrix4x4 localMat; + + float4 transforms[16]; + int transformTypes[16]; + + int isDirty; + + rs_allocation children; + +} SgTransform; diff --git a/libs/rs/java/Samples/AndroidManifest.xml b/libs/rs/java/Samples/AndroidManifest.xml index c08a264de070..8dad1612e163 100644 --- a/libs/rs/java/Samples/AndroidManifest.xml +++ b/libs/rs/java/Samples/AndroidManifest.xml @@ -21,5 +21,14 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + + <activity android:name="RsBench" + android:label="RsBenchmark" + android:theme="@android:style/Theme.Black.NoTitleBar"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> </application> </manifest> diff --git a/libs/rs/java/Samples/src/com/android/samples/RsBench.java b/libs/rs/java/Samples/src/com/android/samples/RsBench.java new file mode 100644 index 000000000000..a29dddcf3390 --- /dev/null +++ b/libs/rs/java/Samples/src/com/android/samples/RsBench.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.samples; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; + +import android.app.Activity; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings.System; +import android.util.Config; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.ListView; + +import java.lang.Runtime; + +public class RsBench extends Activity { + + private RsBenchView mView; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + // Create our Preview view and set it as the content of our + // Activity + mView = new RsBenchView(this); + setContentView(mView); + } + + @Override + protected void onResume() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity loses focus + super.onResume(); + mView.resume(); + } + + @Override + protected void onPause() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity loses focus + super.onPause(); + mView.pause(); + } + +} + diff --git a/libs/rs/java/Samples/src/com/android/samples/RsBenchRS.java b/libs/rs/java/Samples/src/com/android/samples/RsBenchRS.java new file mode 100644 index 000000000000..1afcee335a50 --- /dev/null +++ b/libs/rs/java/Samples/src/com/android/samples/RsBenchRS.java @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.samples; + +import java.io.Writer; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.renderscript.*; +import android.renderscript.Allocation.MipmapControl; +import android.renderscript.Program.TextureType; +import android.renderscript.ProgramStore.DepthFunc; +import android.renderscript.ProgramStore.BlendSrcFunc; +import android.renderscript.ProgramStore.BlendDstFunc; +import android.renderscript.Sampler.Value; +import android.util.Log; + + +public class RsBenchRS { + + int mWidth; + int mHeight; + + public RsBenchRS() { + } + + public void init(RenderScriptGL rs, Resources res, int width, int height) { + mRS = rs; + mRes = res; + mWidth = width; + mHeight = height; + mOptionsARGB.inScaled = false; + mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888; + mMode = 0; + mMaxModes = 0; + initRS(); + } + + private Resources mRes; + private RenderScriptGL mRS; + + private Sampler mLinearClamp; + private Sampler mLinearWrap; + private Sampler mMipLinearWrap; + private Sampler mNearestClamp; + private Sampler mMipLinearAniso8; + private Sampler mMipLinearAniso15; + + private ProgramStore mProgStoreBlendNoneDepth; + private ProgramStore mProgStoreBlendNone; + private ProgramStore mProgStoreBlendAlpha; + private ProgramStore mProgStoreBlendAdd; + + private ProgramFragment mProgFragmentTexture; + private ProgramFragment mProgFragmentColor; + + private ProgramVertex mProgVertex; + private ProgramVertexFixedFunction.Constants mPVA; + + // Custom shaders + private ProgramVertex mProgVertexCustom; + private ProgramFragment mProgFragmentCustom; + private ProgramFragment mProgFragmentMultitex; + private ProgramVertex mProgVertexPixelLight; + private ProgramVertex mProgVertexPixelLightMove; + private ProgramFragment mProgFragmentPixelLight; + private ScriptField_VertexShaderConstants_s mVSConst; + private ScriptField_FragentShaderConstants_s mFSConst; + private ScriptField_VertexShaderConstants3_s mVSConstPixel; + private ScriptField_FragentShaderConstants3_s mFSConstPixel; + + private ProgramVertex mProgVertexCube; + private ProgramFragment mProgFragmentCube; + + private ProgramRaster mCullBack; + private ProgramRaster mCullFront; + private ProgramRaster mCullNone; + + private Allocation mTexTorus; + private Allocation mTexOpaque; + private Allocation mTexTransparent; + private Allocation mTexChecker; + private Allocation mTexCube; + + private Mesh m10by10Mesh; + private Mesh m100by100Mesh; + private Mesh mWbyHMesh; + private Mesh mTorus; + + Font mFontSans; + Font mFontSerif; + Font mFontSerifBold; + Font mFontSerifItalic; + Font mFontSerifBoldItalic; + Font mFontMono; + private Allocation mTextAlloc; + + private ScriptC_rsbench mScript; + + private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options(); + + int mMode; + int mMaxModes; + + public void onActionDown(int x, int y) { + mMode ++; + mMode = mMode % mMaxModes; + mScript.set_gDisplayMode(mMode); + } + + ProgramStore BLEND_ADD_DEPTH_NONE(RenderScript rs) { + ProgramStore.Builder builder = new ProgramStore.Builder(rs); + builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS); + builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE); + builder.setDitherEnabled(false); + builder.setDepthMaskEnabled(false); + return builder.create(); + } + + private Mesh getMbyNMesh(float width, float height, int wResolution, int hResolution) { + + Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS, + 2, Mesh.TriangleMeshBuilder.TEXTURE_0); + + for (int y = 0; y <= hResolution; y++) { + final float normalizedY = (float)y / hResolution; + final float yOffset = (normalizedY - 0.5f) * height; + for (int x = 0; x <= wResolution; x++) { + float normalizedX = (float)x / wResolution; + float xOffset = (normalizedX - 0.5f) * width; + tmb.setTexture((float)x % 2, (float)y % 2); + tmb.addVertex(xOffset, yOffset); + } + } + + for (int y = 0; y < hResolution; y++) { + final int curY = y * (wResolution + 1); + final int belowY = (y + 1) * (wResolution + 1); + for (int x = 0; x < wResolution; x++) { + int curV = curY + x; + int belowV = belowY + x; + tmb.addTriangle(curV, belowV, curV + 1); + tmb.addTriangle(belowV, belowV + 1, curV + 1); + } + } + + return tmb.create(true); + } + + private void initProgramStore() { + // Use stock the stock program store object + mProgStoreBlendNoneDepth = ProgramStore.BLEND_NONE_DEPTH_TEST(mRS); + mProgStoreBlendNone = ProgramStore.BLEND_NONE_DEPTH_NONE(mRS); + + // Create a custom program store + ProgramStore.Builder builder = new ProgramStore.Builder(mRS); + builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS); + builder.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA, + ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA); + builder.setDitherEnabled(false); + builder.setDepthMaskEnabled(false); + mProgStoreBlendAlpha = builder.create(); + + mProgStoreBlendAdd = BLEND_ADD_DEPTH_NONE(mRS); + + mScript.set_gProgStoreBlendNoneDepth(mProgStoreBlendNoneDepth); + mScript.set_gProgStoreBlendNone(mProgStoreBlendNone); + mScript.set_gProgStoreBlendAlpha(mProgStoreBlendAlpha); + mScript.set_gProgStoreBlendAdd(mProgStoreBlendAdd); + } + + private void initProgramFragment() { + + ProgramFragmentFixedFunction.Builder texBuilder = new ProgramFragmentFixedFunction.Builder(mRS); + texBuilder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE, + ProgramFragmentFixedFunction.Builder.Format.RGBA, 0); + mProgFragmentTexture = texBuilder.create(); + mProgFragmentTexture.bindSampler(mLinearClamp, 0); + + ProgramFragmentFixedFunction.Builder colBuilder = new ProgramFragmentFixedFunction.Builder(mRS); + colBuilder.setVaryingColor(false); + mProgFragmentColor = colBuilder.create(); + + mScript.set_gProgFragmentColor(mProgFragmentColor); + mScript.set_gProgFragmentTexture(mProgFragmentTexture); + } + + private void initProgramVertex() { + ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS); + mProgVertex = pvb.create(); + + mPVA = new ProgramVertexFixedFunction.Constants(mRS); + ((ProgramVertexFixedFunction)mProgVertex).bindConstants(mPVA); + Matrix4f proj = new Matrix4f(); + proj.loadOrthoWindow(mWidth, mHeight); + mPVA.setProjection(proj); + + mScript.set_gProgVertex(mProgVertex); + } + + private void initCustomShaders() { + mVSConst = new ScriptField_VertexShaderConstants_s(mRS, 1); + mFSConst = new ScriptField_FragentShaderConstants_s(mRS, 1); + mScript.bind_gVSConstants(mVSConst); + mScript.bind_gFSConstants(mFSConst); + + mVSConstPixel = new ScriptField_VertexShaderConstants3_s(mRS, 1); + mFSConstPixel = new ScriptField_FragentShaderConstants3_s(mRS, 1); + mScript.bind_gVSConstPixel(mVSConstPixel); + mScript.bind_gFSConstPixel(mFSConstPixel); + + // Initialize the shader builder + ProgramVertex.Builder pvbCustom = new ProgramVertex.Builder(mRS); + // Specify the resource that contains the shader string + pvbCustom.setShader(mRes, R.raw.shaderv); + // Use a script field to specify the input layout + pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS)); + // Define the constant input layout + pvbCustom.addConstant(mVSConst.getAllocation().getType()); + mProgVertexCustom = pvbCustom.create(); + // Bind the source of constant data + mProgVertexCustom.bindConstants(mVSConst.getAllocation(), 0); + + ProgramFragment.Builder pfbCustom = new ProgramFragment.Builder(mRS); + // Specify the resource that contains the shader string + pfbCustom.setShader(mRes, R.raw.shaderf); + // Tell the builder how many textures we have + pfbCustom.addTexture(Program.TextureType.TEXTURE_2D); + // Define the constant input layout + pfbCustom.addConstant(mFSConst.getAllocation().getType()); + mProgFragmentCustom = pfbCustom.create(); + // Bind the source of constant data + mProgFragmentCustom.bindConstants(mFSConst.getAllocation(), 0); + + // Cubemap test shaders + pvbCustom = new ProgramVertex.Builder(mRS); + pvbCustom.setShader(mRes, R.raw.shadercubev); + pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS)); + pvbCustom.addConstant(mVSConst.getAllocation().getType()); + mProgVertexCube = pvbCustom.create(); + mProgVertexCube.bindConstants(mVSConst.getAllocation(), 0); + + pfbCustom = new ProgramFragment.Builder(mRS); + pfbCustom.setShader(mRes, R.raw.shadercubef); + pfbCustom.addTexture(Program.TextureType.TEXTURE_CUBE); + mProgFragmentCube = pfbCustom.create(); + + pvbCustom = new ProgramVertex.Builder(mRS); + pvbCustom.setShader(mRes, R.raw.shader2v); + pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS)); + pvbCustom.addConstant(mVSConstPixel.getAllocation().getType()); + mProgVertexPixelLight = pvbCustom.create(); + mProgVertexPixelLight.bindConstants(mVSConstPixel.getAllocation(), 0); + + pvbCustom = new ProgramVertex.Builder(mRS); + pvbCustom.setShader(mRes, R.raw.shader2movev); + pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS)); + pvbCustom.addConstant(mVSConstPixel.getAllocation().getType()); + mProgVertexPixelLightMove = pvbCustom.create(); + mProgVertexPixelLightMove.bindConstants(mVSConstPixel.getAllocation(), 0); + + pfbCustom = new ProgramFragment.Builder(mRS); + pfbCustom.setShader(mRes, R.raw.shader2f); + pfbCustom.addTexture(Program.TextureType.TEXTURE_2D); + pfbCustom.addConstant(mFSConstPixel.getAllocation().getType()); + mProgFragmentPixelLight = pfbCustom.create(); + mProgFragmentPixelLight.bindConstants(mFSConstPixel.getAllocation(), 0); + + pfbCustom = new ProgramFragment.Builder(mRS); + pfbCustom.setShader(mRes, R.raw.multitexf); + for (int texCount = 0; texCount < 3; texCount ++) { + pfbCustom.addTexture(Program.TextureType.TEXTURE_2D); + } + mProgFragmentMultitex = pfbCustom.create(); + + mScript.set_gProgVertexCustom(mProgVertexCustom); + mScript.set_gProgFragmentCustom(mProgFragmentCustom); + mScript.set_gProgVertexCube(mProgVertexCube); + mScript.set_gProgFragmentCube(mProgFragmentCube); + mScript.set_gProgVertexPixelLight(mProgVertexPixelLight); + mScript.set_gProgVertexPixelLightMove(mProgVertexPixelLightMove); + mScript.set_gProgFragmentPixelLight(mProgFragmentPixelLight); + mScript.set_gProgFragmentMultitex(mProgFragmentMultitex); + } + + private Allocation loadTextureRGB(int id) { + return Allocation.createFromBitmapResource(mRS, mRes, id, + Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, + Allocation.USAGE_GRAPHICS_TEXTURE); + } + + private Allocation loadTextureARGB(int id) { + Bitmap b = BitmapFactory.decodeResource(mRes, id, mOptionsARGB); + return Allocation.createFromBitmap(mRS, b, + Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, + Allocation.USAGE_GRAPHICS_TEXTURE); + } + + private void loadImages() { + mTexTorus = loadTextureRGB(R.drawable.torusmap); + mTexOpaque = loadTextureRGB(R.drawable.data); + mTexTransparent = loadTextureARGB(R.drawable.leaf); + mTexChecker = loadTextureRGB(R.drawable.checker); + Bitmap b = BitmapFactory.decodeResource(mRes, R.drawable.cubemap_test); + mTexCube = Allocation.createCubemapFromBitmap(mRS, b); + + mScript.set_gTexTorus(mTexTorus); + mScript.set_gTexOpaque(mTexOpaque); + mScript.set_gTexTransparent(mTexTransparent); + mScript.set_gTexChecker(mTexChecker); + mScript.set_gTexCube(mTexCube); + } + + private void initFonts() { + // Sans font by family name + mFontSans = Font.create(mRS, mRes, "sans-serif", Font.Style.NORMAL, 8); + mFontSerif = Font.create(mRS, mRes, "serif", Font.Style.NORMAL, 8); + // Create fonts by family and style + mFontSerifBold = Font.create(mRS, mRes, "serif", Font.Style.BOLD, 8); + mFontSerifItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8); + mFontSerifBoldItalic = Font.create(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8); + mFontMono = Font.create(mRS, mRes, "mono", Font.Style.NORMAL, 8); + + mTextAlloc = Allocation.createFromString(mRS, "String from allocation", Allocation.USAGE_SCRIPT); + + mScript.set_gFontSans(mFontSans); + mScript.set_gFontSerif(mFontSerif); + mScript.set_gFontSerifBold(mFontSerifBold); + mScript.set_gFontSerifItalic(mFontSerifItalic); + mScript.set_gFontSerifBoldItalic(mFontSerifBoldItalic); + mScript.set_gFontMono(mFontMono); + mScript.set_gTextAlloc(mTextAlloc); + } + + private void initMesh() { + m10by10Mesh = getMbyNMesh(mWidth, mHeight, 10, 10); + mScript.set_g10by10Mesh(m10by10Mesh); + m100by100Mesh = getMbyNMesh(mWidth, mHeight, 100, 100); + mScript.set_g100by100Mesh(m100by100Mesh); + mWbyHMesh= getMbyNMesh(mWidth, mHeight, mWidth/4, mHeight/4); + mScript.set_gWbyHMesh(mWbyHMesh); + + FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus); + FileA3D.IndexEntry entry = model.getIndexEntry(0); + if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) { + Log.e("rs", "could not load model"); + } else { + mTorus = (Mesh)entry.getObject(); + mScript.set_gTorusMesh(mTorus); + } + } + + private void initSamplers() { + Sampler.Builder bs = new Sampler.Builder(mRS); + bs.setMinification(Sampler.Value.LINEAR); + bs.setMagnification(Sampler.Value.LINEAR); + bs.setWrapS(Sampler.Value.WRAP); + bs.setWrapT(Sampler.Value.WRAP); + mLinearWrap = bs.create(); + + mLinearClamp = Sampler.CLAMP_LINEAR(mRS); + mNearestClamp = Sampler.CLAMP_NEAREST(mRS); + mMipLinearWrap = Sampler.WRAP_LINEAR_MIP_LINEAR(mRS); + + bs = new Sampler.Builder(mRS); + bs.setMinification(Sampler.Value.LINEAR_MIP_LINEAR); + bs.setMagnification(Sampler.Value.LINEAR); + bs.setWrapS(Sampler.Value.WRAP); + bs.setWrapT(Sampler.Value.WRAP); + bs.setAnisotropy(8.0f); + mMipLinearAniso8 = bs.create(); + bs.setAnisotropy(15.0f); + mMipLinearAniso15 = bs.create(); + + mScript.set_gLinearClamp(mLinearClamp); + mScript.set_gLinearWrap(mLinearWrap); + mScript.set_gMipLinearWrap(mMipLinearWrap); + mScript.set_gMipLinearAniso8(mMipLinearAniso8); + mScript.set_gMipLinearAniso15(mMipLinearAniso15); + mScript.set_gNearestClamp(mNearestClamp); + } + + private void initProgramRaster() { + mCullBack = ProgramRaster.CULL_BACK(mRS); + mCullFront = ProgramRaster.CULL_FRONT(mRS); + mCullNone = ProgramRaster.CULL_NONE(mRS); + + mScript.set_gCullBack(mCullBack); + mScript.set_gCullFront(mCullFront); + mScript.set_gCullNone(mCullNone); + } + + private void initRS() { + + mScript = new ScriptC_rsbench(mRS, mRes, R.raw.rsbench); + + mMaxModes = mScript.get_gMaxModes(); + + initSamplers(); + initProgramStore(); + initProgramFragment(); + initProgramVertex(); + initFonts(); + loadImages(); + initMesh(); + initProgramRaster(); + initCustomShaders(); + + mRS.bindRootScript(mScript); + } +} + + + diff --git a/libs/rs/java/Samples/src/com/android/samples/RsBenchView.java b/libs/rs/java/Samples/src/com/android/samples/RsBenchView.java new file mode 100644 index 000000000000..0a566682ea56 --- /dev/null +++ b/libs/rs/java/Samples/src/com/android/samples/RsBenchView.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.samples; + +import java.io.Writer; +import java.util.ArrayList; +import java.util.concurrent.Semaphore; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; +import android.renderscript.RenderScriptGL; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.KeyEvent; +import android.view.MotionEvent; + +public class RsBenchView extends RSSurfaceView { + + public RsBenchView(Context context) { + super(context); + } + + private RenderScriptGL mRS; + private RsBenchRS mRender; + + + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + super.surfaceChanged(holder, format, w, h); + if (mRS == null) { + RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig(); + sc.setDepth(16, 24); + mRS = createRenderScriptGL(sc); + mRS.setSurface(holder, w, h); + mRender = new RsBenchRS(); + mRender.init(mRS, getResources(), w, h); + } + } + + @Override + protected void onDetachedFromWindow() { + if (mRS != null) { + mRS = null; + destroyRenderScriptGL(); + } + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return super.onKeyDown(keyCode, event); + } + + + @Override + public boolean onTouchEvent(MotionEvent ev) { + boolean ret = false; + int act = ev.getAction(); + if (act == ev.ACTION_DOWN) { + mRender.onActionDown((int)ev.getX(), (int)ev.getY()); + ret = true; + } + + return ret; + } +} + + diff --git a/libs/rs/java/Samples/src/com/android/samples/rsbench.rs b/libs/rs/java/Samples/src/com/android/samples/rsbench.rs new file mode 100644 index 000000000000..a1368e68fc10 --- /dev/null +++ b/libs/rs/java/Samples/src/com/android/samples/rsbench.rs @@ -0,0 +1,789 @@ +// Copyright (C) 2009 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma version(1) + +#pragma rs java_package_name(com.android.samples) + +#include "rs_graphics.rsh" +#include "shader_def.rsh" + +const int gMaxModes = 26; + +rs_program_vertex gProgVertex; +rs_program_fragment gProgFragmentColor; +rs_program_fragment gProgFragmentTexture; + +rs_program_store gProgStoreBlendNoneDepth; +rs_program_store gProgStoreBlendNone; +rs_program_store gProgStoreBlendAlpha; +rs_program_store gProgStoreBlendAdd; + +rs_allocation gTexOpaque; +rs_allocation gTexTorus; +rs_allocation gTexTransparent; +rs_allocation gTexChecker; +rs_allocation gTexCube; + +rs_mesh g10by10Mesh; +rs_mesh g100by100Mesh; +rs_mesh gWbyHMesh; +rs_mesh gTorusMesh; + +rs_font gFontSans; +rs_font gFontSerif; +rs_font gFontSerifBold; +rs_font gFontSerifItalic; +rs_font gFontSerifBoldItalic; +rs_font gFontMono; +rs_allocation gTextAlloc; + +int gDisplayMode; + +rs_sampler gLinearClamp; +rs_sampler gLinearWrap; +rs_sampler gMipLinearWrap; +rs_sampler gMipLinearAniso8; +rs_sampler gMipLinearAniso15; +rs_sampler gNearestClamp; + +rs_program_raster gCullBack; +rs_program_raster gCullFront; +rs_program_raster gCullNone; + +// Custom vertex shader compunents +VertexShaderConstants *gVSConstants; +FragentShaderConstants *gFSConstants; +VertexShaderConstants3 *gVSConstPixel; +FragentShaderConstants3 *gFSConstPixel; +// Export these out to easily set the inputs to shader +VertexShaderInputs *gVSInputs; +// Custom shaders we use for lighting +rs_program_vertex gProgVertexCustom; +rs_program_fragment gProgFragmentCustom; +rs_program_vertex gProgVertexPixelLight; +rs_program_vertex gProgVertexPixelLightMove; +rs_program_fragment gProgFragmentPixelLight; +rs_program_vertex gProgVertexCube; +rs_program_fragment gProgFragmentCube; +rs_program_fragment gProgFragmentMultitex; + +float gDt = 0; + +void init() { +} + +static const char *sampleText = "This is a sample of small text for performace"; +// Offsets for multiple layer of text +static int textOffsets[] = { 0, 0, -5, -5, 5, 5, -8, -8, 8, 8}; +static float textColors[] = {1.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.7f, 0.5f, 1.0f, + 0.7f, 0.5f, 0.5f, 1.0f, + 0.5f, 0.5f, 0.7f, 1.0f, + 0.5f, 0.6f, 0.7f, 1.0f, +}; + +static void displayFontSamples(int fillNum) { + + rs_font fonts[5]; + rsSetObject(&fonts[0], gFontSans); + rsSetObject(&fonts[1], gFontSerif); + rsSetObject(&fonts[2], gFontSerifBold); + rsSetObject(&fonts[3], gFontSerifBoldItalic); + rsSetObject(&fonts[4], gFontSans); + + uint width = rsgGetWidth(); + uint height = rsgGetHeight(); + int left = 0, right = 0, top = 0, bottom = 0; + rsgMeasureText(sampleText, &left, &right, &top, &bottom); + + int textHeight = top - bottom; + int textWidth = right - left; + int numVerticalLines = height / textHeight; + int yPos = top; + + int xOffset = 0, yOffset = 0; + for(int fillI = 0; fillI < fillNum; fillI ++) { + rsgBindFont(fonts[fillI]); + xOffset = textOffsets[fillI * 2]; + yOffset = textOffsets[fillI * 2 + 1]; + float *colPtr = textColors + fillI * 4; + rsgFontColor(colPtr[0], colPtr[1], colPtr[2], colPtr[3]); + for (int h = 0; h < 4; h ++) { + yPos = top + yOffset; + for (int v = 0; v < numVerticalLines; v ++) { + rsgDrawText(sampleText, xOffset + textWidth * h, yPos); + yPos += textHeight; + } + } + } + + for (int i = 0; i < 5; i ++) { + rsClearObject(&fonts[i]); + } +} + +static void bindProgramVertexOrtho() { + // Default vertex sahder + rsgBindProgramVertex(gProgVertex); + // Setup the projection matrix + rs_matrix4x4 proj; + rsMatrixLoadOrtho(&proj, 0, rsgGetWidth(), rsgGetHeight(), 0, -500, 500); + rsgProgramVertexLoadProjectionMatrix(&proj); +} + +static void displaySingletexFill(bool blend, int quadCount) { + bindProgramVertexOrtho(); + rs_matrix4x4 matrix; + rsMatrixLoadIdentity(&matrix); + rsgProgramVertexLoadModelMatrix(&matrix); + + // Fragment shader with texture + if (!blend) { + rsgBindProgramStore(gProgStoreBlendNone); + } else { + rsgBindProgramStore(gProgStoreBlendAlpha); + } + rsgBindProgramFragment(gProgFragmentTexture); + rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); + rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque); + + for (int i = 0; i < quadCount; i ++) { + float startX = 10 * i, startY = 10 * i; + float width = rsgGetWidth() - startX, height = rsgGetHeight() - startY; + rsgDrawQuadTexCoords(startX, startY, 0, 0, 0, + startX, startY + height, 0, 0, 1, + startX + width, startY + height, 0, 1, 1, + startX + width, startY, 0, 1, 0); + } +} + +static void displayBlendingSamples() { + int i; + + bindProgramVertexOrtho(); + rs_matrix4x4 matrix; + rsMatrixLoadIdentity(&matrix); + rsgProgramVertexLoadModelMatrix(&matrix); + + rsgBindProgramFragment(gProgFragmentColor); + + rsgBindProgramStore(gProgStoreBlendNone); + for (i = 0; i < 3; i ++) { + float iPlusOne = (float)(i + 1); + rsgProgramFragmentConstantColor(gProgFragmentColor, + 0.1f*iPlusOne, 0.2f*iPlusOne, 0.3f*iPlusOne, 1); + float yPos = 150 * (float)i; + rsgDrawRect(0, yPos, 200, yPos + 200, 0); + } + + rsgBindProgramStore(gProgStoreBlendAlpha); + for (i = 0; i < 3; i ++) { + float iPlusOne = (float)(i + 1); + rsgProgramFragmentConstantColor(gProgFragmentColor, + 0.2f*iPlusOne, 0.3f*iPlusOne, 0.1f*iPlusOne, 0.5); + float yPos = 150 * (float)i; + rsgDrawRect(150, yPos, 350, yPos + 200, 0); + } + + rsgBindProgramStore(gProgStoreBlendAdd); + for (i = 0; i < 3; i ++) { + float iPlusOne = (float)(i + 1); + rsgProgramFragmentConstantColor(gProgFragmentColor, + 0.3f*iPlusOne, 0.1f*iPlusOne, 0.2f*iPlusOne, 0.5); + float yPos = 150 * (float)i; + rsgDrawRect(300, yPos, 500, yPos + 200, 0); + } + + + rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f); + rsgBindFont(gFontMono); + rsgDrawText("No Blending", 10, 50); + rsgDrawText("Alpha Blending", 160, 150); + rsgDrawText("Additive Blending", 320, 250); + +} + +static void displayMeshSamples(int meshNum) { + + bindProgramVertexOrtho(); + rs_matrix4x4 matrix; + rsMatrixLoadTranslate(&matrix, rsgGetWidth()/2, rsgGetHeight()/2, 0); + rsgProgramVertexLoadModelMatrix(&matrix); + + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendNone); + rsgBindProgramFragment(gProgFragmentTexture); + rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); + rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque); + + if (meshNum == 0) { + rsgDrawMesh(g10by10Mesh); + } else if (meshNum == 1) { + rsgDrawMesh(g100by100Mesh); + } else if (meshNum == 2) { + rsgDrawMesh(gWbyHMesh); + } +} + +static void displayTextureSamplers() { + + bindProgramVertexOrtho(); + rs_matrix4x4 matrix; + rsMatrixLoadIdentity(&matrix); + rsgProgramVertexLoadModelMatrix(&matrix); + + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendNone); + rsgBindProgramFragment(gProgFragmentTexture); + rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque); + + // Linear clamp + rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); + float startX = 0, startY = 0; + float width = 300, height = 300; + rsgDrawQuadTexCoords(startX, startY, 0, 0, 0, + startX, startY + height, 0, 0, 1.1, + startX + width, startY + height, 0, 1.1, 1.1, + startX + width, startY, 0, 1.1, 0); + + // Linear Wrap + rsgBindSampler(gProgFragmentTexture, 0, gLinearWrap); + startX = 0; startY = 300; + width = 300; height = 300; + rsgDrawQuadTexCoords(startX, startY, 0, 0, 0, + startX, startY + height, 0, 0, 1.1, + startX + width, startY + height, 0, 1.1, 1.1, + startX + width, startY, 0, 1.1, 0); + + // Nearest + rsgBindSampler(gProgFragmentTexture, 0, gNearestClamp); + startX = 300; startY = 0; + width = 300; height = 300; + rsgDrawQuadTexCoords(startX, startY, 0, 0, 0, + startX, startY + height, 0, 0, 1.1, + startX + width, startY + height, 0, 1.1, 1.1, + startX + width, startY, 0, 1.1, 0); + + rsgBindSampler(gProgFragmentTexture, 0, gMipLinearWrap); + startX = 300; startY = 300; + width = 300; height = 300; + rsgDrawQuadTexCoords(startX, startY, 0, 0, 0, + startX, startY + height, 0, 0, 1.5, + startX + width, startY + height, 0, 1.5, 1.5, + startX + width, startY, 0, 1.5, 0); + + rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f); + rsgBindFont(gFontMono); + rsgDrawText("Filtering: linear clamp", 10, 290); + rsgDrawText("Filtering: linear wrap", 10, 590); + rsgDrawText("Filtering: nearest clamp", 310, 290); + rsgDrawText("Filtering: miplinear wrap", 310, 590); +} + +static float gTorusRotation = 0; +static void updateModelMatrix(rs_matrix4x4 *matrix, void *buffer) { + if (buffer == 0) { + rsgProgramVertexLoadModelMatrix(matrix); + } else { + rsgAllocationSyncAll(rsGetAllocation(buffer)); + } +} + +static void drawToruses(int numMeshes, rs_matrix4x4 *matrix, void *buffer) { + + if (numMeshes == 1) { + rsMatrixLoadTranslate(matrix, 0.0f, 0.0f, -7.5f); + rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f); + updateModelMatrix(matrix, buffer); + rsgDrawMesh(gTorusMesh); + return; + } + + if (numMeshes == 2) { + rsMatrixLoadTranslate(matrix, -1.6f, 0.0f, -7.5f); + rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f); + updateModelMatrix(matrix, buffer); + rsgDrawMesh(gTorusMesh); + + rsMatrixLoadTranslate(matrix, 1.6f, 0.0f, -7.5f); + rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f); + updateModelMatrix(matrix, buffer); + rsgDrawMesh(gTorusMesh); + return; + } + + float startX = -5.0f; + float startY = -1.5f; + float startZ = -15.0f; + float dist = 3.2f; + + for (int h = 0; h < 4; h ++) { + for (int v = 0; v < 2; v ++) { + // Position our model on the screen + rsMatrixLoadTranslate(matrix, startX + dist * h, startY + dist * v, startZ); + rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f); + updateModelMatrix(matrix, buffer); + rsgDrawMesh(gTorusMesh); + } + } +} + + +// Quick hack to get some geometry numbers +static void displaySimpleGeoSamples(bool useTexture, int numMeshes) { + rsgBindProgramVertex(gProgVertex); + rsgBindProgramRaster(gCullBack); + // Setup the projection matrix with 30 degree field of view + rs_matrix4x4 proj; + float aspect = (float)rsgGetWidth() / (float)rsgGetHeight(); + rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f); + rsgProgramVertexLoadProjectionMatrix(&proj); + + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendNoneDepth); + if (useTexture) { + rsgBindProgramFragment(gProgFragmentTexture); + } else { + rsgBindProgramFragment(gProgFragmentColor); + rsgProgramFragmentConstantColor(gProgFragmentColor, 0.1, 0.7, 0.1, 1); + } + rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); + rsgBindTexture(gProgFragmentTexture, 0, gTexTorus); + + // Apply a rotation to our mesh + gTorusRotation += 50.0f * gDt; + if (gTorusRotation > 360.0f) { + gTorusRotation -= 360.0f; + } + + rs_matrix4x4 matrix; + drawToruses(numMeshes, &matrix, 0); +} + +float gLight0Rotation = 0; +float gLight1Rotation = 0; + +static void setupCustomShaderLights() { + float4 light0Pos = {-5.0f, 5.0f, -10.0f, 1.0f}; + float4 light1Pos = {2.0f, 5.0f, 15.0f, 1.0f}; + float4 light0DiffCol = {0.9f, 0.7f, 0.7f, 1.0f}; + float4 light0SpecCol = {0.9f, 0.6f, 0.6f, 1.0f}; + float4 light1DiffCol = {0.5f, 0.5f, 0.9f, 1.0f}; + float4 light1SpecCol = {0.5f, 0.5f, 0.9f, 1.0f}; + + gLight0Rotation += 50.0f * gDt; + if (gLight0Rotation > 360.0f) { + gLight0Rotation -= 360.0f; + } + gLight1Rotation -= 50.0f * gDt; + if (gLight1Rotation > 360.0f) { + gLight1Rotation -= 360.0f; + } + + rs_matrix4x4 l0Mat; + rsMatrixLoadRotate(&l0Mat, gLight0Rotation, 1.0f, 0.0f, 0.0f); + light0Pos = rsMatrixMultiply(&l0Mat, light0Pos); + rs_matrix4x4 l1Mat; + rsMatrixLoadRotate(&l1Mat, gLight1Rotation, 0.0f, 0.0f, 1.0f); + light1Pos = rsMatrixMultiply(&l1Mat, light1Pos); + + // Set light 0 properties + gVSConstants->light0_Posision = light0Pos; + gVSConstants->light0_Diffuse = 1.0f; + gVSConstants->light0_Specular = 0.5f; + gVSConstants->light0_CosinePower = 10.0f; + // Set light 1 properties + gVSConstants->light1_Posision = light1Pos; + gVSConstants->light1_Diffuse = 1.0f; + gVSConstants->light1_Specular = 0.7f; + gVSConstants->light1_CosinePower = 25.0f; + rsgAllocationSyncAll(rsGetAllocation(gVSConstants)); + + // Update fragment shader constants + // Set light 0 colors + gFSConstants->light0_DiffuseColor = light0DiffCol; + gFSConstants->light0_SpecularColor = light0SpecCol; + // Set light 1 colors + gFSConstants->light1_DiffuseColor = light1DiffCol; + gFSConstants->light1_SpecularColor = light1SpecCol; + rsgAllocationSyncAll(rsGetAllocation(gFSConstants)); + + // Set light 0 properties for per pixel lighting + gFSConstPixel->light0_Posision = light0Pos; + gFSConstPixel->light0_Diffuse = 1.0f; + gFSConstPixel->light0_Specular = 0.5f; + gFSConstPixel->light0_CosinePower = 10.0f; + gFSConstPixel->light0_DiffuseColor = light0DiffCol; + gFSConstPixel->light0_SpecularColor = light0SpecCol; + // Set light 1 properties + gFSConstPixel->light1_Posision = light1Pos; + gFSConstPixel->light1_Diffuse = 1.0f; + gFSConstPixel->light1_Specular = 0.7f; + gFSConstPixel->light1_CosinePower = 25.0f; + gFSConstPixel->light1_DiffuseColor = light1DiffCol; + gFSConstPixel->light1_SpecularColor = light1SpecCol; + rsgAllocationSyncAll(rsGetAllocation(gFSConstPixel)); +} + +static void displayCustomShaderSamples(int numMeshes) { + + // Update vertex shader constants + // Load model matrix + // Apply a rotation to our mesh + gTorusRotation += 50.0f * gDt; + if (gTorusRotation > 360.0f) { + gTorusRotation -= 360.0f; + } + + // Setup the projection matrix + float aspect = (float)rsgGetWidth() / (float)rsgGetHeight(); + rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f); + setupCustomShaderLights(); + + rsgBindProgramVertex(gProgVertexCustom); + + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendNoneDepth); + rsgBindProgramFragment(gProgFragmentCustom); + rsgBindSampler(gProgFragmentCustom, 0, gLinearClamp); + rsgBindTexture(gProgFragmentCustom, 0, gTexTorus); + + // Use back face culling + rsgBindProgramRaster(gCullBack); + + drawToruses(numMeshes, &gVSConstants->model, gVSConstants); +} + +static void displayPixelLightSamples(int numMeshes, bool heavyVertex) { + + // Update vertex shader constants + // Load model matrix + // Apply a rotation to our mesh + gTorusRotation += 30.0f * gDt; + if (gTorusRotation > 360.0f) { + gTorusRotation -= 360.0f; + } + + gVSConstPixel->time = rsUptimeMillis()*0.005; + + // Setup the projection matrix + float aspect = (float)rsgGetWidth() / (float)rsgGetHeight(); + rsMatrixLoadPerspective(&gVSConstPixel->proj, 30.0f, aspect, 0.1f, 100.0f); + setupCustomShaderLights(); + + if (heavyVertex) { + rsgBindProgramVertex(gProgVertexPixelLightMove); + } else { + rsgBindProgramVertex(gProgVertexPixelLight); + } + + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendNoneDepth); + rsgBindProgramFragment(gProgFragmentPixelLight); + rsgBindSampler(gProgFragmentPixelLight, 0, gLinearClamp); + rsgBindTexture(gProgFragmentPixelLight, 0, gTexTorus); + + // Use back face culling + rsgBindProgramRaster(gCullBack); + + drawToruses(numMeshes, &gVSConstPixel->model, gVSConstPixel); +} + +static void displayMultitextureSample(bool blend, int quadCount) { + bindProgramVertexOrtho(); + rs_matrix4x4 matrix; + rsMatrixLoadIdentity(&matrix); + rsgProgramVertexLoadModelMatrix(&matrix); + + // Fragment shader with texture + if (!blend) { + rsgBindProgramStore(gProgStoreBlendNone); + } else { + rsgBindProgramStore(gProgStoreBlendAlpha); + } + rsgBindProgramFragment(gProgFragmentMultitex); + rsgBindSampler(gProgFragmentMultitex, 0, gLinearClamp); + rsgBindSampler(gProgFragmentMultitex, 1, gLinearWrap); + rsgBindSampler(gProgFragmentMultitex, 2, gLinearClamp); + rsgBindTexture(gProgFragmentMultitex, 0, gTexChecker); + rsgBindTexture(gProgFragmentMultitex, 1, gTexTorus); + rsgBindTexture(gProgFragmentMultitex, 2, gTexTransparent); + + for (int i = 0; i < quadCount; i ++) { + float startX = 10 * i, startY = 10 * i; + float width = rsgGetWidth() - startX, height = rsgGetHeight() - startY; + rsgDrawQuadTexCoords(startX, startY, 0, 0, 0, + startX, startY + height, 0, 0, 1, + startX + width, startY + height, 0, 1, 1, + startX + width, startY, 0, 1, 0); + } +} + +static float gAnisoTime = 0.0f; +static uint anisoMode = 0; +static void displayAnisoSample() { + + gAnisoTime += gDt; + + rsgBindProgramVertex(gProgVertex); + float aspect = (float)rsgGetWidth() / (float)rsgGetHeight(); + rs_matrix4x4 proj; + rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f); + rsgProgramVertexLoadProjectionMatrix(&proj); + + rs_matrix4x4 matrix; + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendNone); + rsgBindProgramFragment(gProgFragmentTexture); + rsMatrixLoadTranslate(&matrix, 0.0f, 0.0f, -10.0f); + rsMatrixRotate(&matrix, -80, 1.0f, 0.0f, 0.0f); + rsgProgramVertexLoadModelMatrix(&matrix); + + rsgBindProgramRaster(gCullNone); + + rsgBindTexture(gProgFragmentTexture, 0, gTexChecker); + + if (gAnisoTime >= 5.0f) { + gAnisoTime = 0.0f; + anisoMode ++; + anisoMode = anisoMode % 3; + } + + if (anisoMode == 0) { + rsgBindSampler(gProgFragmentTexture, 0, gMipLinearAniso8); + } else if (anisoMode == 1) { + rsgBindSampler(gProgFragmentTexture, 0, gMipLinearAniso15); + } else { + rsgBindSampler(gProgFragmentTexture, 0, gMipLinearWrap); + } + + float startX = -15; + float startY = -15; + float width = 30; + float height = 30; + rsgDrawQuadTexCoords(startX, startY, 0, 0, 0, + startX, startY + height, 0, 0, 10, + startX + width, startY + height, 0, 10, 10, + startX + width, startY, 0, 10, 0); + + rsgBindProgramRaster(gCullBack); + + rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f); + rsgBindFont(gFontMono); + if (anisoMode == 0) { + rsgDrawText("Anisotropic filtering 8", 10, 40); + } else if (anisoMode == 1) { + rsgDrawText("Anisotropic filtering 15", 10, 40); + } else { + rsgDrawText("Miplinear filtering", 10, 40); + } +} + +static bool checkInit() { + + static int countdown = 5; + + if (countdown == 0) { + gDt = 0; + countdown --; + } + // Perform all the uploads so we only measure rendered time + if(countdown > 1) { + displayFontSamples(5); + displaySingletexFill(true, 3); + displayBlendingSamples(); + displayMeshSamples(0); + displayMeshSamples(1); + displayMeshSamples(2); + displayTextureSamplers(); + displayMultitextureSample(true, 5); + displayAnisoSample(); + displayPixelLightSamples(1, false); + displayPixelLightSamples(1, true); + countdown --; + rsgClearColor(0.2f, 0.2f, 0.2f, 0.0f); + + // Now use text metrics to center the text + uint width = rsgGetWidth(); + uint height = rsgGetHeight(); + int left = 0, right = 0, top = 0, bottom = 0; + + rsgFontColor(0.9f, 0.9f, 0.95f, 1.0f); + rsgBindFont(gFontSerifBoldItalic); + + const char* text = "Initializing"; + rsgMeasureText(text, &left, &right, &top, &bottom); + int centeredPosX = width / 2 - (right - left) / 2; + int centeredPosY = height / 2 - (top - bottom) / 2; + rsgDrawText(text, centeredPosX, centeredPosY); + + return false; + } + + return true; +} + +static int frameCount = 0; +static int totalFramesRendered = 0; +static int benchMode = 0; + +#define testTime 5.0f +static float curTestTime = testTime; + +static const char *testNames[] = { + "Finished text fill 1", + "Finished text fill 2", + "Finished text fill 3", + "Finished text fill 4", + "Finished text fill 5", + "Finished 25.6k geo flat color", + "Finished 51.2k geo flat color", + "Finished 204.8k geo raster load flat color", + "Finished 25.6k geo texture", + "Finished 51.2k geo texture", + "Finished 204.8k geo raster load texture", + "Finished full screen mesh 10 by 10", + "Finished full screen mesh 100 by 100", + "Finished full screen mesh W / 4 by H / 4", + "Finished 25.6k geo heavy vertex", + "Finished 51.2k geo heavy vertex", + "Finished 204.8k geo raster load heavy vertex", + "Finished singletexture 5x fill", + "Finished 3tex multitexture 5x fill", + "Finished blend singletexture 5x fill", + "Finished blend 3tex multitexture 5x fill", + "Finished 25.6k geo heavy fragment", + "Finished 51.2k geo heavy fragment", + "Finished 204.8k geo raster load heavy fragment", + "Finished 25.6k geo heavy fragment, heavy vertex", + "Finished 51.2k geo heavy fragment, heavy vertex", + "Finished 204.8k geo raster load heavy fragment, heavy vertex", +}; + +int root(int launchID) { + + gDt = rsGetDt(); + + rsgClearColor(0.2f, 0.2f, 0.2f, 0.0f); + rsgClearDepth(1.0f); + + if(!checkInit()) { + return 1; + } + + curTestTime -= gDt; + if(curTestTime < 0.0f) { + float fps = (float)(frameCount) / (testTime - curTestTime); + rsDebug(testNames[benchMode], fps); + benchMode ++; + curTestTime = testTime; + totalFramesRendered += frameCount; + frameCount = 0; + gTorusRotation = 0; + + if (benchMode > gMaxModes) { + benchMode = 0; + } + } + + switch (benchMode) { + case 0: + displayFontSamples(1); + break; + case 1: + displayFontSamples(2); + break; + case 2: + displayFontSamples(3); + break; + case 3: + displayFontSamples(4); + break; + case 4: + displayFontSamples(5); + break; + case 5: + displaySimpleGeoSamples(false, 1); + break; + case 6: + displaySimpleGeoSamples(false, 2); + break; + case 7: + displaySimpleGeoSamples(false, 8); + break; + case 8: + displaySimpleGeoSamples(true, 1); + break; + case 9: + displaySimpleGeoSamples(true, 2); + break; + case 10: + displaySimpleGeoSamples(true, 8); + break; + case 11: + displayMeshSamples(0); + break; + case 12: + displayMeshSamples(1); + break; + case 13: + displayMeshSamples(2); + break; + case 14: + displayCustomShaderSamples(1); + break; + case 15: + displayCustomShaderSamples(2); + break; + case 16: + displayCustomShaderSamples(8); + break; + case 17: + displaySingletexFill(false, 5); + break; + case 18: + displayMultitextureSample(false, 5); + break; + case 19: + displaySingletexFill(true, 5); + break; + case 20: + displayMultitextureSample(true, 5); + break; + case 21: + displayPixelLightSamples(1, false); + break; + case 22: + displayPixelLightSamples(2, false); + break; + case 23: + displayPixelLightSamples(8, false); + break; + case 24: + displayPixelLightSamples(1, true); + break; + case 25: + displayPixelLightSamples(2, true); + break; + case 26: + displayPixelLightSamples(8, true); + break; + + } + + frameCount ++; + + return 1; +} diff --git a/libs/rs/java/tests/Android.mk b/libs/rs/java/tests/Android.mk new file mode 100644 index 000000000000..6c992d5baf0b --- /dev/null +++ b/libs/rs/java/tests/Android.mk @@ -0,0 +1,30 @@ +# +# 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. +# + +ifneq ($(TARGET_SIMULATOR),true) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src) + +LOCAL_PACKAGE_NAME := RSTest + +include $(BUILD_PACKAGE) + +endif diff --git a/libs/rs/java/tests/AndroidManifest.xml b/libs/rs/java/tests/AndroidManifest.xml new file mode 100644 index 000000000000..b660398de1a4 --- /dev/null +++ b/libs/rs/java/tests/AndroidManifest.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.rs.test"> + <application + android:label="_RS_Test" + android:icon="@drawable/test_pattern"> + <activity android:name="RSTest" + android:screenOrientation="portrait"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/libs/rs/java/tests/res/drawable/test_pattern.png b/libs/rs/java/tests/res/drawable/test_pattern.png Binary files differnew file mode 100644 index 000000000000..e7d145554c00 --- /dev/null +++ b/libs/rs/java/tests/res/drawable/test_pattern.png diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTest.java b/libs/rs/java/tests/src/com/android/rs/test/RSTest.java new file mode 100644 index 000000000000..6b8fa6b8a02c --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/RSTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.test; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; + +import android.app.Activity; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings.System; +import android.util.Config; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.ListView; + +import java.lang.Runtime; + +public class RSTest extends Activity { + //EventListener mListener = new EventListener(); + + private static final String LOG_TAG = "libRS_jni"; + private static final boolean DEBUG = false; + private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV; + + private RSTestView mView; + + // get the current looper (from your Activity UI thread for instance + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + // Create our Preview view and set it as the content of our + // Activity + mView = new RSTestView(this); + setContentView(mView); + } + + @Override + protected void onResume() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity loses focus + super.onResume(); + mView.resume(); + } + + @Override + protected void onPause() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity loses focus + super.onPause(); + mView.pause(); + } + + static void log(String message) { + if (LOG_ENABLED) { + Log.v(LOG_TAG, message); + } + } + + +} diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java new file mode 100644 index 000000000000..541bf2234f76 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.test; + +import android.content.Context; +import android.content.res.Resources; +import android.renderscript.*; +import android.util.Log; +import java.util.ArrayList; +import java.util.ListIterator; +import java.util.Timer; +import java.util.TimerTask; + + +public class RSTestCore { + int mWidth; + int mHeight; + Context mCtx; + + public RSTestCore(Context ctx) { + mCtx = ctx; + } + + private Resources mRes; + private RenderScriptGL mRS; + + private Font mFont; + ScriptField_ListAllocs_s mListAllocs; + int mLastX; + int mLastY; + private ScriptC_rslist mScript; + + private ArrayList<UnitTest> unitTests; + private ListIterator<UnitTest> test_iter; + private UnitTest activeTest; + private boolean stopTesting; + + /* Periodic timer for ensuring future tests get scheduled */ + private Timer mTimer; + public static final int RS_TIMER_PERIOD = 100; + + public void init(RenderScriptGL rs, Resources res, int width, int height) { + mRS = rs; + mRes = res; + mWidth = width; + mHeight = height; + stopTesting = false; + + mScript = new ScriptC_rslist(mRS, mRes, R.raw.rslist); + + unitTests = new ArrayList<UnitTest>(); + + unitTests.add(new UT_primitives(this, mRes, mCtx)); + unitTests.add(new UT_rsdebug(this, mRes, mCtx)); + unitTests.add(new UT_rstime(this, mRes, mCtx)); + unitTests.add(new UT_rstypes(this, mRes, mCtx)); + unitTests.add(new UT_math(this, mRes, mCtx)); + unitTests.add(new UT_fp_mad(this, mRes, mCtx)); + /* + unitTests.add(new UnitTest(null, "<Pass>", 1)); + unitTests.add(new UnitTest()); + unitTests.add(new UnitTest(null, "<Fail>", -1)); + + for (int i = 0; i < 20; i++) { + unitTests.add(new UnitTest(null, "<Pass>", 1)); + } + */ + + UnitTest [] uta = new UnitTest[unitTests.size()]; + uta = unitTests.toArray(uta); + + mListAllocs = new ScriptField_ListAllocs_s(mRS, uta.length); + for (int i = 0; i < uta.length; i++) { + ScriptField_ListAllocs_s.Item listElem = new ScriptField_ListAllocs_s.Item(); + listElem.text = Allocation.createFromString(mRS, uta[i].name, Allocation.USAGE_SCRIPT); + listElem.result = uta[i].result; + mListAllocs.set(listElem, i, false); + uta[i].setItem(listElem); + } + + mListAllocs.copyAll(); + + mScript.bind_gList(mListAllocs); + + mFont = Font.create(mRS, mRes, "serif", Font.Style.BOLD, 8); + mScript.set_gFont(mFont); + + mRS.bindRootScript(mScript); + + test_iter = unitTests.listIterator(); + refreshTestResults(); /* Kick off the first test */ + + TimerTask pTask = new TimerTask() { + public void run() { + refreshTestResults(); + } + }; + + mTimer = new Timer(); + mTimer.schedule(pTask, RS_TIMER_PERIOD, RS_TIMER_PERIOD); + } + + public void checkAndRunNextTest() { + if (activeTest != null) { + if (!activeTest.isAlive()) { + /* Properly clean up on our last test */ + try { + activeTest.join(); + } + catch (InterruptedException e) { + } + activeTest = null; + } + } + + if (!stopTesting && activeTest == null) { + if (test_iter.hasNext()) { + activeTest = test_iter.next(); + activeTest.start(); + /* This routine will only get called once when a new test + * should start running. The message handler in UnitTest.java + * ensures this. */ + } + else { + if (mTimer != null) { + mTimer.cancel(); + mTimer.purge(); + mTimer = null; + } + } + } + } + + public void refreshTestResults() { + checkAndRunNextTest(); + + if (mListAllocs != null && mScript != null && mRS != null) { + mListAllocs.copyAll(); + + mScript.bind_gList(mListAllocs); + mRS.bindRootScript(mScript); + } + } + + public void cleanup() { + stopTesting = true; + UnitTest t = activeTest; + + /* Stop periodic refresh of testing */ + if (mTimer != null) { + mTimer.cancel(); + mTimer.purge(); + mTimer = null; + } + + /* Wait to exit until we finish the current test */ + if (t != null) { + try { + t.join(); + } + catch (InterruptedException e) { + } + t = null; + } + + } + + public void newTouchPosition(float x, float y, float pressure, int id) { + } + + public void onActionDown(int x, int y) { + mScript.set_gDY(0.0f); + mLastX = x; + mLastY = y; + refreshTestResults(); + } + + public void onActionMove(int x, int y) { + int dx = mLastX - x; + int dy = mLastY - y; + + if (Math.abs(dy) <= 2) { + dy = 0; + } + + mScript.set_gDY(dy); + + mLastX = x; + mLastY = y; + refreshTestResults(); + } +} diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java new file mode 100644 index 000000000000..368f2866ad2d --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.test; + +import java.io.Writer; +import java.util.ArrayList; +import java.util.concurrent.Semaphore; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; +import android.renderscript.RenderScriptGL; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.KeyEvent; +import android.view.MotionEvent; + +public class RSTestView extends RSSurfaceView { + + private Context mCtx; + + public RSTestView(Context context) { + super(context); + mCtx = context; + //setFocusable(true); + } + + private RenderScriptGL mRS; + private RSTestCore mRender; + + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + super.surfaceChanged(holder, format, w, h); + if (mRS == null) { + RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig(); + mRS = createRenderScriptGL(sc); + mRS.setSurface(holder, w, h); + mRender = new RSTestCore(mCtx); + mRender.init(mRS, getResources(), w, h); + } + } + + @Override + protected void onDetachedFromWindow() { + if(mRS != null) { + mRender.cleanup(); + mRS = null; + destroyRenderScriptGL(); + } + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) + { + boolean ret = false; + int act = ev.getAction(); + if (act == ev.ACTION_DOWN) { + mRender.onActionDown((int)ev.getX(), (int)ev.getY()); + ret = true; + } + else if (act == ev.ACTION_MOVE) { + mRender.onActionMove((int)ev.getX(), (int)ev.getY()); + ret = true; + } + + return ret; + } +} diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java b/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java new file mode 100644 index 000000000000..f2c91afd045b --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.test; + +import android.content.Context; +import android.content.res.Resources; +import android.renderscript.*; + +public class UT_fp_mad extends UnitTest { + private Resources mRes; + + protected UT_fp_mad(RSTestCore rstc, Resources res, Context ctx) { + super(rstc, "Fp_Mad", ctx); + mRes = res; + } + + public void run() { + RenderScript pRS = RenderScript.create(mCtx); + ScriptC_fp_mad s = new ScriptC_fp_mad(pRS, mRes, R.raw.fp_mad); + pRS.setMessageHandler(mRsMessage); + s.invoke_fp_mad_test(0, 0); + pRS.finish(); + waitForMessage(); + pRS.destroy(); + } +} diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_math.java b/libs/rs/java/tests/src/com/android/rs/test/UT_math.java new file mode 100644 index 000000000000..bf133be07619 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/UT_math.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.test; + +import android.content.Context; +import android.content.res.Resources; +import android.renderscript.*; + +public class UT_math extends UnitTest { + private Resources mRes; + + protected UT_math(RSTestCore rstc, Resources res, Context ctx) { + super(rstc, "Math", ctx); + mRes = res; + } + + public void run() { + RenderScript pRS = RenderScript.create(mCtx); + ScriptC_math s = new ScriptC_math(pRS, mRes, R.raw.math); + pRS.setMessageHandler(mRsMessage); + s.invoke_math_test(0, 0); + pRS.finish(); + waitForMessage(); + pRS.destroy(); + } +} diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java b/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java new file mode 100644 index 000000000000..b7a65a57951e --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.test; + +import android.content.Context; +import android.content.res.Resources; +import android.renderscript.*; + +public class UT_primitives extends UnitTest { + private Resources mRes; + + protected UT_primitives(RSTestCore rstc, Resources res, Context ctx) { + super(rstc, "Primitives", ctx); + mRes = res; + } + + private boolean initializeGlobals(ScriptC_primitives s) { + float pF = s.get_floatTest(); + if (pF != 1.99f) { + return false; + } + s.set_floatTest(2.99f); + + double pD = s.get_doubleTest(); + if (pD != 2.05) { + return false; + } + s.set_doubleTest(3.05); + + byte pC = s.get_charTest(); + if (pC != -8) { + return false; + } + s.set_charTest((byte)-16); + + short pS = s.get_shortTest(); + if (pS != -16) { + return false; + } + s.set_shortTest((short)-32); + + int pI = s.get_intTest(); + if (pI != -32) { + return false; + } + s.set_intTest(-64); + + long pL = s.get_longTest(); + if (pL != 17179869184l) { + return false; + } + s.set_longTest(17179869185l); + + long puL = s.get_ulongTest(); + if (puL != 4611686018427387904L) { + return false; + } + s.set_ulongTest(4611686018427387903L); + + + long pLL = s.get_longlongTest(); + if (pLL != 68719476736L) { + return false; + } + s.set_longlongTest(68719476735L); + + long pu64 = s.get_uint64_tTest(); + if (pu64 != 117179869184l) { + return false; + } + s.set_uint64_tTest(117179869185l); + + return true; + } + + public void run() { + RenderScript pRS = RenderScript.create(mCtx); + ScriptC_primitives s = new ScriptC_primitives(pRS, mRes, R.raw.primitives); + pRS.setMessageHandler(mRsMessage); + if (!initializeGlobals(s)) { + // initializeGlobals failed + result = -1; + } else { + s.invoke_primitives_test(0, 0); + pRS.finish(); + waitForMessage(); + } + pRS.destroy(); + } +} diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_rsdebug.java b/libs/rs/java/tests/src/com/android/rs/test/UT_rsdebug.java new file mode 100644 index 000000000000..0614b1a63b80 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/UT_rsdebug.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.test; + +import android.content.Context; +import android.content.res.Resources; +import android.renderscript.*; + +public class UT_rsdebug extends UnitTest { + private Resources mRes; + + protected UT_rsdebug(RSTestCore rstc, Resources res, Context ctx) { + super(rstc, "rsDebug", ctx); + mRes = res; + } + + public void run() { + RenderScript pRS = RenderScript.create(mCtx); + ScriptC_rsdebug s = new ScriptC_rsdebug(pRS, mRes, R.raw.rsdebug); + pRS.setMessageHandler(mRsMessage); + s.invoke_test_rsdebug(0, 0); + pRS.finish(); + waitForMessage(); + pRS.destroy(); + } +} diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_rstime.java b/libs/rs/java/tests/src/com/android/rs/test/UT_rstime.java new file mode 100644 index 000000000000..f302e1a3f0e3 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/UT_rstime.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.test; + +import android.content.Context; +import android.content.res.Resources; +import android.renderscript.*; + +public class UT_rstime extends UnitTest { + private Resources mRes; + + protected UT_rstime(RSTestCore rstc, Resources res, Context ctx) { + super(rstc, "rsTime", ctx); + mRes = res; + } + + public void run() { + RenderScript pRS = RenderScript.create(mCtx); + ScriptC_rstime s = new ScriptC_rstime(pRS, mRes, R.raw.rstime); + pRS.setMessageHandler(mRsMessage); + s.invoke_test_rstime(0, 0); + pRS.finish(); + waitForMessage(); + pRS.destroy(); + } +} diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_rstypes.java b/libs/rs/java/tests/src/com/android/rs/test/UT_rstypes.java new file mode 100644 index 000000000000..74211c8d0a75 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/UT_rstypes.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.test; + +import android.content.Context; +import android.content.res.Resources; +import android.renderscript.*; + +public class UT_rstypes extends UnitTest { + private Resources mRes; + + protected UT_rstypes(RSTestCore rstc, Resources res, Context ctx) { + super(rstc, "rsTypes", ctx); + mRes = res; + } + + public void run() { + RenderScript pRS = RenderScript.create(mCtx); + ScriptC_rstypes s = new ScriptC_rstypes(pRS, mRes, R.raw.rstypes); + pRS.setMessageHandler(mRsMessage); + s.invoke_test_rstypes(0, 0); + pRS.finish(); + waitForMessage(); + pRS.destroy(); + } +} diff --git a/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java new file mode 100644 index 000000000000..a7722c7deab4 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.test; +import android.content.Context; +import android.renderscript.RenderScript.RSMessageHandler; +import android.util.Log; + +public class UnitTest extends Thread { + public String name; + public int result; + private ScriptField_ListAllocs_s.Item mItem; + private RSTestCore mRSTC; + private boolean msgHandled; + protected Context mCtx; + + /* These constants must match those in shared.rsh */ + public static final int RS_MSG_TEST_PASSED = 100; + public static final int RS_MSG_TEST_FAILED = 101; + + private static int numTests = 0; + public int testID; + + protected UnitTest(RSTestCore rstc, String n, int initResult, Context ctx) { + super(); + mRSTC = rstc; + name = n; + msgHandled = false; + mCtx = ctx; + result = initResult; + testID = numTests++; + } + + protected UnitTest(RSTestCore rstc, String n, Context ctx) { + this(rstc, n, 0, ctx); + } + + protected UnitTest(RSTestCore rstc, Context ctx) { + this (rstc, "<Unknown>", ctx); + } + + protected UnitTest(Context ctx) { + this (null, ctx); + } + + protected RSMessageHandler mRsMessage = new RSMessageHandler() { + public void run() { + if (result == 0) { + switch (mID) { + case RS_MSG_TEST_PASSED: + result = 1; + break; + case RS_MSG_TEST_FAILED: + result = -1; + break; + default: + android.util.Log.v("RenderScript", "Unit test got unexpected message"); + return; + } + } + + if (mItem != null) { + mItem.result = result; + msgHandled = true; + try { + mRSTC.refreshTestResults(); + } + catch (IllegalStateException e) { + /* Ignore the case where our message receiver has been + disconnected. This happens when we leave the application + before it finishes running all of the unit tests. */ + } + } + } + }; + + public void waitForMessage() { + while (!msgHandled) { + yield(); + } + } + + public void setItem(ScriptField_ListAllocs_s.Item item) { + mItem = item; + } + + public void run() { + /* This method needs to be implemented for each subclass */ + if (mRSTC != null) { + mRSTC.refreshTestResults(); + } + } +} diff --git a/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs new file mode 100644 index 000000000000..b6f2b2a60371 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs @@ -0,0 +1,174 @@ +#include "shared.rsh" + +const int TEST_COUNT = 1; + +static float data_f1[1025]; +static float4 data_f4[1025]; + +static void test_mad4(uint32_t index) { + start(); + + float total = 0; + // Do ~1 billion ops + for (int ct=0; ct < 1000 * (1000 / 80); ct++) { + for (int i=0; i < (1000); i++) { + data_f4[i] = (data_f4[i] * 0.02f + + data_f4[i+1] * 0.04f + + data_f4[i+2] * 0.05f + + data_f4[i+3] * 0.1f + + data_f4[i+4] * 0.2f + + data_f4[i+5] * 0.2f + + data_f4[i+6] * 0.1f + + data_f4[i+7] * 0.05f + + data_f4[i+8] * 0.04f + + data_f4[i+9] * 0.02f + 1.f); + } + } + + float time = end(index); + rsDebug("fp_mad4 M ops", 1000.f / time); +} + +static void test_mad(uint32_t index) { + start(); + + float total = 0; + // Do ~1 billion ops + for (int ct=0; ct < 1000 * (1000 / 20); ct++) { + for (int i=0; i < (1000); i++) { + data_f1[i] = (data_f1[i] * 0.02f + + data_f1[i+1] * 0.04f + + data_f1[i+2] * 0.05f + + data_f1[i+3] * 0.1f + + data_f1[i+4] * 0.2f + + data_f1[i+5] * 0.2f + + data_f1[i+6] * 0.1f + + data_f1[i+7] * 0.05f + + data_f1[i+8] * 0.04f + + data_f1[i+9] * 0.02f + 1.f); + } + } + + float time = end(index); + rsDebug("fp_mad M ops", 1000.f / time); +} + +static void test_norm(uint32_t index) { + start(); + + float total = 0; + // Do ~10 M ops + for (int ct=0; ct < 1000 * 10; ct++) { + for (int i=0; i < (1000); i++) { + data_f4[i] = normalize(data_f4[i]); + } + } + + float time = end(index); + rsDebug("fp_norm M ops", 10.f / time); +} + +static void test_sincos4(uint32_t index) { + start(); + + float total = 0; + // Do ~10 M ops + for (int ct=0; ct < 1000 * 10 / 4; ct++) { + for (int i=0; i < (1000); i++) { + data_f4[i] = sin(data_f4[i]) * cos(data_f4[i]); + } + } + + float time = end(index); + rsDebug("fp_sincos4 M ops", 10.f / time); +} + +static void test_sincos(uint32_t index) { + start(); + + float total = 0; + // Do ~10 M ops + for (int ct=0; ct < 1000 * 10; ct++) { + for (int i=0; i < (1000); i++) { + data_f1[i] = sin(data_f1[i]) * cos(data_f1[i]); + } + } + + float time = end(index); + rsDebug("fp_sincos M ops", 10.f / time); +} + +static void test_clamp(uint32_t index) { + start(); + + // Do ~100 M ops + for (int ct=0; ct < 1000 * 100; ct++) { + for (int i=0; i < (1000); i++) { + data_f1[i] = clamp(data_f1[i], -1.f, 1.f); + } + } + + float time = end(index); + rsDebug("fp_clamp M ops", 100.f / time); + + start(); + // Do ~100 M ops + for (int ct=0; ct < 1000 * 100; ct++) { + for (int i=0; i < (1000); i++) { + if (data_f1[i] < -1.f) data_f1[i] = -1.f; + if (data_f1[i] > -1.f) data_f1[i] = 1.f; + } + } + + time = end(index); + rsDebug("fp_clamp ref M ops", 100.f / time); +} + +static void test_clamp4(uint32_t index) { + start(); + + float total = 0; + // Do ~100 M ops + for (int ct=0; ct < 1000 * 100 /4; ct++) { + for (int i=0; i < (1000); i++) { + data_f4[i] = clamp(data_f4[i], -1.f, 1.f); + } + } + + float time = end(index); + rsDebug("fp_clamp4 M ops", 100.f / time); +} + +void fp_mad_test(uint32_t index, int test_num) { + int x; + for (x=0; x < 1025; x++) { + data_f1[x] = (x & 0xf) * 0.1f; + data_f4[x].x = (x & 0xf) * 0.1f; + data_f4[x].y = (x & 0xf0) * 0.1f; + data_f4[x].z = (x & 0x33) * 0.1f; + data_f4[x].w = (x & 0x77) * 0.1f; + } + + test_mad4(index); + test_mad(index); + + for (x=0; x < 1025; x++) { + data_f1[x] = (x & 0xf) * 0.1f + 1.f; + data_f4[x].x = (x & 0xf) * 0.1f + 1.f; + data_f4[x].y = (x & 0xf0) * 0.1f + 1.f; + data_f4[x].z = (x & 0x33) * 0.1f + 1.f; + data_f4[x].w = (x & 0x77) * 0.1f + 1.f; + } + + test_norm(index); + test_sincos4(index); + test_sincos(index); + test_clamp4(index); + test_clamp(index); + + // TODO Actually verify test result accuracy + rsDebug("fp_mad_test PASSED", 0); + rsSendToClientBlocking(RS_MSG_TEST_PASSED); +} + + diff --git a/libs/rs/java/tests/src/com/android/rs/test/math.rs b/libs/rs/java/tests/src/com/android/rs/test/math.rs new file mode 100644 index 000000000000..8cad82bfd569 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/math.rs @@ -0,0 +1,322 @@ +#include "shared.rsh" + +// Testing math library + +volatile float f1; +volatile float2 f2; +volatile float3 f3; +volatile float4 f4; + +volatile int i1; +volatile int2 i2; +volatile int3 i3; +volatile int4 i4; + +volatile uint ui1; +volatile uint2 ui2; +volatile uint3 ui3; +volatile uint4 ui4; + +volatile short s1; +volatile short2 s2; +volatile short3 s3; +volatile short4 s4; + +volatile ushort us1; +volatile ushort2 us2; +volatile ushort3 us3; +volatile ushort4 us4; + +volatile char c1; +volatile char2 c2; +volatile char3 c3; +volatile char4 c4; + +volatile uchar uc1; +volatile uchar2 uc2; +volatile uchar3 uc3; +volatile uchar4 uc4; + +#define TEST_FN_FUNC_FN(fnc) \ + rsDebug("Testing " #fnc, 0); \ + f1 = fnc(f1); \ + f2 = fnc(f2); \ + f3 = fnc(f3); \ + f4 = fnc(f4); + +#define TEST_FN_FUNC_FN_PFN(fnc) \ + rsDebug("Testing " #fnc, 0); \ + f1 = fnc(f1, (float*) &f1); \ + f2 = fnc(f2, (float2*) &f2); \ + f3 = fnc(f3, (float3*) &f3); \ + f4 = fnc(f4, (float4*) &f4); + +#define TEST_FN_FUNC_FN_FN(fnc) \ + rsDebug("Testing " #fnc, 0); \ + f1 = fnc(f1, f1); \ + f2 = fnc(f2, f2); \ + f3 = fnc(f3, f3); \ + f4 = fnc(f4, f4); + +#define TEST_FN_FUNC_FN_F(fnc) \ + rsDebug("Testing " #fnc, 0); \ + f1 = fnc(f1, f1); \ + f2 = fnc(f2, f1); \ + f3 = fnc(f3, f1); \ + f4 = fnc(f4, f1); + +#define TEST_FN_FUNC_FN_IN(fnc) \ + rsDebug("Testing " #fnc, 0); \ + f1 = fnc(f1, i1); \ + f2 = fnc(f2, i2); \ + f3 = fnc(f3, i3); \ + f4 = fnc(f4, i4); + +#define TEST_FN_FUNC_FN_I(fnc) \ + rsDebug("Testing " #fnc, 0); \ + f1 = fnc(f1, i1); \ + f2 = fnc(f2, i1); \ + f3 = fnc(f3, i1); \ + f4 = fnc(f4, i1); + +#define TEST_FN_FUNC_FN_FN_FN(fnc) \ + rsDebug("Testing " #fnc, 0); \ + f1 = fnc(f1, f1, f1); \ + f2 = fnc(f2, f2, f2); \ + f3 = fnc(f3, f3, f3); \ + f4 = fnc(f4, f4, f4); + +#define TEST_FN_FUNC_FN_PIN(fnc) \ + rsDebug("Testing " #fnc, 0); \ + f1 = fnc(f1, (int*) &i1); \ + f2 = fnc(f2, (int2*) &i2); \ + f3 = fnc(f3, (int3*) &i3); \ + f4 = fnc(f4, (int4*) &i4); + +#define TEST_FN_FUNC_FN_FN_PIN(fnc) \ + rsDebug("Testing " #fnc, 0); \ + f1 = fnc(f1, f1, (int*) &i1); \ + f2 = fnc(f2, f2, (int2*) &i2); \ + f3 = fnc(f3, f3, (int3*) &i3); \ + f4 = fnc(f4, f4, (int4*) &i4); + +#define TEST_IN_FUNC_FN(fnc) \ + rsDebug("Testing " #fnc, 0); \ + i1 = fnc(f1); \ + i2 = fnc(f2); \ + i3 = fnc(f3); \ + i4 = fnc(f4); + + +static bool test_fp_math(uint32_t index) { + bool failed = false; + start(); + + TEST_FN_FUNC_FN(acos); + TEST_FN_FUNC_FN(acosh); + TEST_FN_FUNC_FN(acospi); + TEST_FN_FUNC_FN(asin); + TEST_FN_FUNC_FN(asinh); + TEST_FN_FUNC_FN(asinpi); + TEST_FN_FUNC_FN(atan); + TEST_FN_FUNC_FN_FN(atan2); + TEST_FN_FUNC_FN(atanh); + TEST_FN_FUNC_FN(atanpi); + TEST_FN_FUNC_FN_FN(atan2pi); + TEST_FN_FUNC_FN(cbrt); + TEST_FN_FUNC_FN(ceil); + TEST_FN_FUNC_FN_FN(copysign); + TEST_FN_FUNC_FN(cos); + TEST_FN_FUNC_FN(cosh); + TEST_FN_FUNC_FN(cospi); + TEST_FN_FUNC_FN(erfc); + TEST_FN_FUNC_FN(erf); + TEST_FN_FUNC_FN(exp); + TEST_FN_FUNC_FN(exp2); + TEST_FN_FUNC_FN(exp10); + TEST_FN_FUNC_FN(expm1); + TEST_FN_FUNC_FN(fabs); + TEST_FN_FUNC_FN_FN(fdim); + TEST_FN_FUNC_FN(floor); + TEST_FN_FUNC_FN_FN_FN(fma); + TEST_FN_FUNC_FN_FN(fmax); + TEST_FN_FUNC_FN_F(fmax); + TEST_FN_FUNC_FN_FN(fmin); + TEST_FN_FUNC_FN_F(fmin); + TEST_FN_FUNC_FN_FN(fmod); + TEST_FN_FUNC_FN_PFN(fract); + TEST_FN_FUNC_FN_PIN(frexp); + TEST_FN_FUNC_FN_FN(hypot); + TEST_IN_FUNC_FN(ilogb); + TEST_FN_FUNC_FN_IN(ldexp); + TEST_FN_FUNC_FN_I(ldexp); + TEST_FN_FUNC_FN(lgamma); + TEST_FN_FUNC_FN_PIN(lgamma); + TEST_FN_FUNC_FN(log); + TEST_FN_FUNC_FN(log2); + TEST_FN_FUNC_FN(log10); + TEST_FN_FUNC_FN(log1p); + TEST_FN_FUNC_FN(logb); + TEST_FN_FUNC_FN_FN_FN(mad); + TEST_FN_FUNC_FN_PFN(modf); + // nan + TEST_FN_FUNC_FN_FN(nextafter); + TEST_FN_FUNC_FN_FN(pow); + TEST_FN_FUNC_FN_IN(pown); + TEST_FN_FUNC_FN_FN(powr); + TEST_FN_FUNC_FN_FN(remainder); + TEST_FN_FUNC_FN_FN_PIN(remquo); + TEST_FN_FUNC_FN(rint); + TEST_FN_FUNC_FN_IN(rootn); + TEST_FN_FUNC_FN(round); + TEST_FN_FUNC_FN(rsqrt); + TEST_FN_FUNC_FN(sin); + TEST_FN_FUNC_FN_PFN(sincos); + TEST_FN_FUNC_FN(sinh); + TEST_FN_FUNC_FN(sinpi); + TEST_FN_FUNC_FN(sqrt); + TEST_FN_FUNC_FN(tan); + TEST_FN_FUNC_FN(tanh); + TEST_FN_FUNC_FN(tanpi); + TEST_FN_FUNC_FN(tgamma); + TEST_FN_FUNC_FN(trunc); + + float time = end(index); + + if (failed) { + rsDebug("test_fp_math FAILED", time); + } + else { + rsDebug("test_fp_math PASSED", time); + } + + return failed; +} + +#define DECL_INT(prefix) \ +volatile char prefix##_c_1 = 1; \ +volatile char2 prefix##_c_2 = 1; \ +volatile char3 prefix##_c_3 = 1; \ +volatile char4 prefix##_c_4 = 1; \ +volatile uchar prefix##_uc_1 = 1; \ +volatile uchar2 prefix##_uc_2 = 1; \ +volatile uchar3 prefix##_uc_3 = 1; \ +volatile uchar4 prefix##_uc_4 = 1; \ +volatile short prefix##_s_1 = 1; \ +volatile short2 prefix##_s_2 = 1; \ +volatile short3 prefix##_s_3 = 1; \ +volatile short4 prefix##_s_4 = 1; \ +volatile ushort prefix##_us_1 = 1; \ +volatile ushort2 prefix##_us_2 = 1; \ +volatile ushort3 prefix##_us_3 = 1; \ +volatile ushort4 prefix##_us_4 = 1; \ +volatile int prefix##_i_1 = 1; \ +volatile int2 prefix##_i_2 = 1; \ +volatile int3 prefix##_i_3 = 1; \ +volatile int4 prefix##_i_4 = 1; \ +volatile uint prefix##_ui_1 = 1; \ +volatile uint2 prefix##_ui_2 = 1; \ +volatile uint3 prefix##_ui_3 = 1; \ +volatile uint4 prefix##_ui_4 = 1; \ +volatile long prefix##_l_1 = 1; \ +volatile ulong prefix##_ul_1 = 1; + +#define TEST_INT_OP_TYPE(op, type) \ +rsDebug("Testing " #op " for " #type "1", i++); \ +res_##type##_1 = src1_##type##_1 op src2_##type##_1; \ +rsDebug("Testing " #op " for " #type "2", i++); \ +res_##type##_2 = src1_##type##_2 op src2_##type##_2; \ +rsDebug("Testing " #op " for " #type "3", i++); \ +res_##type##_3 = src1_##type##_3 op src2_##type##_3; \ +rsDebug("Testing " #op " for " #type "4", i++); \ +res_##type##_4 = src1_##type##_4 op src2_##type##_4; + +#define TEST_INT_OP(op) \ +TEST_INT_OP_TYPE(op, c) \ +TEST_INT_OP_TYPE(op, uc) \ +TEST_INT_OP_TYPE(op, s) \ +TEST_INT_OP_TYPE(op, us) \ +TEST_INT_OP_TYPE(op, i) \ +TEST_INT_OP_TYPE(op, ui) \ +rsDebug("Testing " #op " for l1", i++); \ +res_l_1 = src1_l_1 op src2_l_1; \ +rsDebug("Testing " #op " for ul1", i++); \ +res_ul_1 = src1_ul_1 op src2_ul_1; + +DECL_INT(res) +DECL_INT(src1) +DECL_INT(src2) + +static bool test_basic_operators() { + bool failed = false; + int i = 0; + + TEST_INT_OP(+); + TEST_INT_OP(-); + TEST_INT_OP(*); + TEST_INT_OP(/); + TEST_INT_OP(%); + TEST_INT_OP(<<); + TEST_INT_OP(>>); + + if (failed) { + rsDebug("test_basic_operators FAILED", 0); + } + else { + rsDebug("test_basic_operators PASSED", 0); + } + + return failed; +} + +#define TEST_CVT(to, from, type) \ +rsDebug("Testing convert from " #from " to " #to, 0); \ +to##1 = from##1; \ +to##2 = convert_##type##2(from##2); \ +to##3 = convert_##type##3(from##3); \ +to##4 = convert_##type##4(from##4); + +#define TEST_CVT_MATRIX(to, type) \ +TEST_CVT(to, c, type); \ +TEST_CVT(to, uc, type); \ +TEST_CVT(to, s, type); \ +TEST_CVT(to, us, type); \ +TEST_CVT(to, i, type); \ +TEST_CVT(to, ui, type); \ +TEST_CVT(to, f, type); \ + +static bool test_convert() { + bool failed = false; + + TEST_CVT_MATRIX(c, char); + TEST_CVT_MATRIX(uc, uchar); + TEST_CVT_MATRIX(s, short); + TEST_CVT_MATRIX(us, ushort); + TEST_CVT_MATRIX(i, int); + TEST_CVT_MATRIX(ui, uint); + TEST_CVT_MATRIX(f, float); + + if (failed) { + rsDebug("test_convert FAILED", 0); + } + else { + rsDebug("test_convert PASSED", 0); + } + + return failed; +} + +void math_test(uint32_t index, int test_num) { + bool failed = false; + failed |= test_convert(); + failed |= test_fp_math(index); + failed |= test_basic_operators(); + + if (failed) { + rsSendToClientBlocking(RS_MSG_TEST_FAILED); + } + else { + rsSendToClientBlocking(RS_MSG_TEST_PASSED); + } +} + diff --git a/libs/rs/java/tests/src/com/android/rs/test/primitives.rs b/libs/rs/java/tests/src/com/android/rs/test/primitives.rs new file mode 100644 index 000000000000..ce451da8b415 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/primitives.rs @@ -0,0 +1,61 @@ +#include "shared.rsh" + +// Testing primitive types +float floatTest = 1.99f; +double doubleTest = 2.05; +char charTest = -8; +short shortTest = -16; +int intTest = -32; +long longTest = 17179869184l; // 1 << 34 +long long longlongTest = 68719476736l; // 1 << 36 + +uchar ucharTest = 8; +ushort ushortTest = 16; +uint uintTest = 32; +ulong ulongTest = 4611686018427387904L; +int64_t int64_tTest = -17179869184l; // - 1 << 34 +uint64_t uint64_tTest = 117179869184l; + +static bool test_primitive_types(uint32_t index) { + bool failed = false; + start(); + + _RS_ASSERT(floatTest == 2.99f); + _RS_ASSERT(doubleTest == 3.05); + _RS_ASSERT(charTest == -16); + _RS_ASSERT(shortTest == -32); + _RS_ASSERT(intTest == -64); + _RS_ASSERT(longTest == 17179869185l); + _RS_ASSERT(longlongTest == 68719476735l); + + _RS_ASSERT(ucharTest == 8); + _RS_ASSERT(ushortTest == 16); + _RS_ASSERT(uintTest == 32); + _RS_ASSERT(ulongTest == 4611686018427387903L); + _RS_ASSERT(int64_tTest == -17179869184l); + _RS_ASSERT(uint64_tTest == 117179869185l); + + float time = end(index); + + if (failed) { + rsDebug("test_primitives FAILED", time); + } + else { + rsDebug("test_primitives PASSED", time); + } + + return failed; +} + +void primitives_test(uint32_t index, int test_num) { + bool failed = false; + failed |= test_primitive_types(index); + + if (failed) { + rsSendToClientBlocking(RS_MSG_TEST_FAILED); + } + else { + rsSendToClientBlocking(RS_MSG_TEST_PASSED); + } +} + diff --git a/libs/rs/java/tests/src/com/android/rs/test/rsdebug.rs b/libs/rs/java/tests/src/com/android/rs/test/rsdebug.rs new file mode 100644 index 000000000000..f7942a5eaf16 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/rsdebug.rs @@ -0,0 +1,56 @@ +#include "shared.rsh" + +// Testing primitive types +float floatTest = 1.99f; +double doubleTest = 2.05; +char charTest = -8; +short shortTest = -16; +int intTest = -32; +long longTest = 17179869184l; // 1 << 34 +long long longlongTest = 68719476736l; // 1 << 36 + +uchar ucharTest = 8; +ushort ushortTest = 16; +uint uintTest = 32; +ulong ulongTest = 4611686018427387904L; +int64_t int64_tTest = -17179869184l; // - 1 << 34 +uint64_t uint64_tTest = 117179869184l; + +static bool basic_test(uint32_t index) { + bool failed = false; + + // This test focuses primarily on compilation-time, not run-time. + // For this reason, none of the outputs are actually checked. + + rsDebug("floatTest", floatTest); + rsDebug("doubleTest", doubleTest); + rsDebug("charTest", charTest); + rsDebug("shortTest", shortTest); + rsDebug("intTest", intTest); + rsDebug("longTest", longTest); + rsDebug("longlongTest", longlongTest); + + rsDebug("ucharTest", ucharTest); + rsDebug("ushortTest", ushortTest); + rsDebug("uintTest", uintTest); + rsDebug("ulongTest", ulongTest); + rsDebug("int64_tTest", int64_tTest); + rsDebug("uint64_tTest", uint64_tTest); + + return failed; +} + +void test_rsdebug(uint32_t index, int test_num) { + bool failed = false; + failed |= basic_test(index); + + if (failed) { + rsSendToClientBlocking(RS_MSG_TEST_FAILED); + rsDebug("rsdebug_test FAILED", -1); + } + else { + rsSendToClientBlocking(RS_MSG_TEST_PASSED); + rsDebug("rsdebug_test PASSED", 0); + } +} + diff --git a/libs/rs/java/tests/src/com/android/rs/test/rslist.rs b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs new file mode 100644 index 000000000000..67c2b8623c61 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs @@ -0,0 +1,107 @@ +// Copyright (C) 2009 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma version(1) + +#pragma rs java_package_name(com.android.rs.test) + +#include "rs_graphics.rsh" + +float gDY; + +rs_font gFont; + +typedef struct ListAllocs_s { + rs_allocation text; + int result; +} ListAllocs; + +ListAllocs *gList; + +void init() { + gDY = 0.0f; +} + +int textPos = 0; + +int root(int launchID) { + + rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f); + rsgClearDepth(1.0f); + + textPos -= (int)gDY*2; + gDY *= 0.95; + + rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f); + rsgBindFont(gFont); + + rs_allocation listAlloc; + rsSetObject(&listAlloc, rsGetAllocation(gList)); + int allocSize = rsAllocationGetDimX(listAlloc); + + int width = rsgGetWidth(); + int height = rsgGetHeight(); + + int itemHeight = 80; + int totalItemHeight = itemHeight * allocSize; + + /* Prevent scrolling above the top of the list */ + int firstItem = height - totalItemHeight; + if (firstItem < 0) { + firstItem = 0; + } + + /* Prevent scrolling past the last line of the list */ + int lastItem = -1 * (totalItemHeight - height); + if (lastItem > 0) { + lastItem = 0; + } + + if (textPos > firstItem) { + textPos = firstItem; + } + else if (textPos < lastItem) { + textPos = lastItem; + } + + int currentYPos = itemHeight + textPos; + + for(int i = 0; i < allocSize; i ++) { + if(currentYPos - itemHeight > height) { + break; + } + + if(currentYPos > 0) { + switch(gList[i].result) { + case 1: /* Passed */ + rsgFontColor(0.5f, 0.9f, 0.5f, 1.0f); + break; + case -1: /* Failed */ + rsgFontColor(0.9f, 0.5f, 0.5f, 1.0f); + break; + case 0: /* Still Testing */ + rsgFontColor(0.9f, 0.9f, 0.5f, 1.0f); + break; + default: /* Unknown */ + rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f); + break; + } + rsgDrawRect(0, currentYPos - 1, width, currentYPos, 0); + rsgDrawText(gList[i].text, 30, currentYPos - 32); + } + currentYPos += itemHeight; + } + + return 10; +} diff --git a/libs/rs/java/tests/src/com/android/rs/test/rstime.rs b/libs/rs/java/tests/src/com/android/rs/test/rstime.rs new file mode 100644 index 000000000000..5e3e07816f9c --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/rstime.rs @@ -0,0 +1,52 @@ +#include "shared.rsh" + +static bool basic_test(uint32_t index) { + bool failed = false; + + rs_time_t curTime = rsTime(0); + rs_tm tm; + rsDebug("curTime", curTime); + + rsLocaltime(&tm, &curTime); + + rsDebug("tm.tm_sec", tm.tm_sec); + rsDebug("tm.tm_min", tm.tm_min); + rsDebug("tm.tm_hour", tm.tm_hour); + rsDebug("tm.tm_mday", tm.tm_mday); + rsDebug("tm.tm_mon", tm.tm_mon); + rsDebug("tm.tm_year", tm.tm_year); + rsDebug("tm.tm_wday", tm.tm_wday); + rsDebug("tm.tm_yday", tm.tm_yday); + rsDebug("tm.tm_isdst", tm.tm_isdst); + + // Test a specific time (only valid for PST localtime) + curTime = 1294438893; + rsLocaltime(&tm, &curTime); + + _RS_ASSERT(tm.tm_sec == 33); + _RS_ASSERT(tm.tm_min == 21); + _RS_ASSERT(tm.tm_hour == 14); + _RS_ASSERT(tm.tm_mday == 7); + _RS_ASSERT(tm.tm_mon == 0); + _RS_ASSERT(tm.tm_year == 111); + _RS_ASSERT(tm.tm_wday == 5); + _RS_ASSERT(tm.tm_yday == 6); + _RS_ASSERT(tm.tm_isdst == 0); + + return failed; +} + +void test_rstime(uint32_t index, int test_num) { + bool failed = false; + failed |= basic_test(index); + + if (failed) { + rsSendToClientBlocking(RS_MSG_TEST_FAILED); + rsDebug("rstime_test FAILED", -1); + } + else { + rsSendToClientBlocking(RS_MSG_TEST_PASSED); + rsDebug("rstime_test PASSED", 0); + } +} + diff --git a/libs/rs/java/tests/src/com/android/rs/test/rstypes.rs b/libs/rs/java/tests/src/com/android/rs/test/rstypes.rs new file mode 100644 index 000000000000..f3bf2448a4f6 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/rstypes.rs @@ -0,0 +1,79 @@ +#include "shared.rsh" +#include "rs_graphics.rsh" + +rs_element elementTest; +rs_type typeTest; +rs_allocation allocationTest; +rs_sampler samplerTest; +rs_script scriptTest; +rs_mesh meshTest; +rs_program_fragment program_fragmentTest; +rs_program_vertex program_vertexTest; +rs_program_raster program_rasterTest; +rs_program_store program_storeTest; +rs_font fontTest; + +rs_matrix4x4 matrix4x4Test; +rs_matrix3x3 matrix3x3Test; +rs_matrix2x2 matrix2x2Test; + +struct my_struct { + int i; + rs_font fontTestStruct; +}; + +static bool basic_test(uint32_t index) { + bool failed = false; + + rs_matrix4x4 matrix4x4TestLocal; + rs_matrix3x3 matrix3x3TestLocal; + rs_matrix2x2 matrix2x2TestLocal; + + // This test focuses primarily on compilation-time, not run-time. + rs_element elementTestLocal; + rs_type typeTestLocal; + rs_allocation allocationTestLocal; + rs_sampler samplerTestLocal; + rs_script scriptTestLocal; + rs_mesh meshTestLocal; + rs_program_fragment program_fragmentTestLocal; + rs_program_vertex program_vertexTestLocal; + rs_program_raster program_rasterTestLocal; + rs_program_store program_storeTestLocal; + rs_font fontTestLocal; + + rs_font fontTestLocalArray[4]; + + rs_font fontTestLocalPreInit = fontTest; + + struct my_struct structTest; + + rsSetObject(&fontTestLocal, fontTest); + //allocationTestLocal = allocationTest; + + rsSetObject(&fontTest, fontTestLocal); + //allocationTest = allocationTestLocal; + + /*for (int i = 0; i < 4; i++) { + rsSetObject(&fontTestLocalArray[i], fontTestLocal); + }*/ + + /*rsSetObject(&fontTest, fontTestLocalArray[3]);*/ + + return failed; +} + +void test_rstypes(uint32_t index, int test_num) { + bool failed = false; + failed |= basic_test(index); + + if (failed) { + rsSendToClientBlocking(RS_MSG_TEST_FAILED); + rsDebug("rstypes_test FAILED", -1); + } + else { + rsSendToClientBlocking(RS_MSG_TEST_PASSED); + rsDebug("rstypes_test PASSED", 0); + } +} + diff --git a/libs/rs/java/tests/src/com/android/rs/test/shared.rsh b/libs/rs/java/tests/src/com/android/rs/test/shared.rsh new file mode 100644 index 000000000000..21be9af2678e --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/shared.rsh @@ -0,0 +1,38 @@ +#pragma version(1) + +#pragma rs java_package_name(com.android.rs.test) + +typedef struct TestResult_s { + rs_allocation name; + bool pass; + float score; + int64_t time; +} TestResult; +//TestResult *g_results; + +static int64_t g_time; + +static void start(void) { + g_time = rsUptimeMillis(); +} + +static float end(uint32_t idx) { + int64_t t = rsUptimeMillis() - g_time; + //g_results[idx].time = t; + //rsDebug("test time", (int)t); + return ((float)t) / 1000.f; +} + +#define _RS_ASSERT(b) \ +do { \ + if (!(b)) { \ + failed = true; \ + rsDebug(#b " FAILED", 0); \ + } \ +\ +} while (0) + +/* These constants must match those in UnitTest.java */ +static const int RS_MSG_TEST_PASSED = 100; +static const int RS_MSG_TEST_FAILED = 101; + diff --git a/libs/rs/java/tests/src/com/android/rs/test/test_root.rs b/libs/rs/java/tests/src/com/android/rs/test/test_root.rs new file mode 100644 index 000000000000..6dc83ba13019 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/test_root.rs @@ -0,0 +1,23 @@ +// Fountain test script +#pragma version(1) + +#pragma rs java_package_name(com.android.rs.test) + +#pragma stateFragment(parent) + +#include "rs_graphics.rsh" + + +typedef struct TestResult { + rs_allocation name; + bool pass; + float score; +} TestResult_t; +TestResult_t *results; + +int root() { + + return 0; +} + + diff --git a/libs/rs/rsAdapter.cpp b/libs/rs/rsAdapter.cpp index 8d363fde19e1..6e8ca705110e 100644 --- a/libs/rs/rsAdapter.cpp +++ b/libs/rs/rsAdapter.cpp @@ -15,11 +15,7 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" -#else -#include "rsContextHostStub.h" -#endif using namespace android; using namespace android::renderscript; diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp index c598f0395362..54dcbcbefc30 100644 --- a/libs/rs/rsAllocation.cpp +++ b/libs/rs/rsAllocation.cpp @@ -13,20 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST -#include "rsContext.h" +#include "rsContext.h" +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES2/gl2.h> #include <GLES/glext.h> -#else -#include "rsContextHostStub.h" - -#include <OpenGL/gl.h> -#include <OpenGl/glext.h> -#endif - -#include "utils/StopWatch.h" +#endif //ANDROID_RS_SERIALIZE static void rsaAllocationGenerateScriptMips(RsContext con, RsAllocation va); @@ -83,7 +76,7 @@ Allocation::~Allocation() { mPtr = NULL; } freeScriptMemory(); - +#ifndef ANDROID_RS_SERIALIZE if (mBufferID) { // Causes a SW crash.... //LOGV(" mBufferID %i", mBufferID); @@ -94,6 +87,7 @@ Allocation::~Allocation() { glDeleteTextures(1, &mTextureID); mTextureID = 0; } +#endif //ANDROID_RS_SERIALIZE } void Allocation::setCpuWritable(bool) { @@ -118,6 +112,7 @@ void Allocation::deferedUploadToTexture(const Context *rsc) { } uint32_t Allocation::getGLTarget() const { +#ifndef ANDROID_RS_SERIALIZE if (getIsTexture()) { if (mType->getDimFaces()) { return GL_TEXTURE_CUBE_MAP; @@ -128,6 +123,7 @@ uint32_t Allocation::getGLTarget() const { if (getIsBufferObject()) { return GL_ARRAY_BUFFER; } +#endif //ANDROID_RS_SERIALIZE return 0; } @@ -158,7 +154,7 @@ void Allocation::syncAll(Context *rsc, RsAllocationUsageType src) { } void Allocation::uploadToTexture(const Context *rsc) { - +#ifndef ANDROID_RS_SERIALIZE mUsageFlags |= RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE; GLenum type = mType->getElement()->getComponent().getGLType(); GLenum format = mType->getElement()->getComponent().getGLFormat(); @@ -195,8 +191,10 @@ void Allocation::uploadToTexture(const Context *rsc) { } rsc->checkError("Allocation::uploadToTexture"); +#endif //ANDROID_RS_SERIALIZE } +#ifndef ANDROID_RS_SERIALIZE const static GLenum gFaceOrder[] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, @@ -205,10 +203,12 @@ const static GLenum gFaceOrder[] = { GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z }; +#endif //ANDROID_RS_SERIALIZE void Allocation::update2DTexture(const void *ptr, uint32_t xoff, uint32_t yoff, uint32_t lod, RsAllocationCubemapFace face, uint32_t w, uint32_t h) { +#ifndef ANDROID_RS_SERIALIZE GLenum type = mType->getElement()->getComponent().getGLType(); GLenum format = mType->getElement()->getComponent().getGLFormat(); GLenum target = (GLenum)getGLTarget(); @@ -220,9 +220,11 @@ void Allocation::update2DTexture(const void *ptr, uint32_t xoff, uint32_t yoff, t = gFaceOrder[face]; } glTexSubImage2D(t, lod, xoff, yoff, w, h, format, type, ptr); +#endif //ANDROID_RS_SERIALIZE } void Allocation::upload2DTexture(bool isFirstUpload) { +#ifndef ANDROID_RS_SERIALIZE GLenum type = mType->getElement()->getComponent().getGLType(); GLenum format = mType->getElement()->getComponent().getGLFormat(); @@ -258,10 +260,9 @@ void Allocation::upload2DTexture(bool isFirstUpload) { } if (mMipmapControl == RS_ALLOCATION_MIPMAP_ON_SYNC_TO_TEXTURE) { -#ifndef ANDROID_RS_BUILD_FOR_HOST glGenerateMipmap(target); -#endif //ANDROID_RS_BUILD_FOR_HOST } +#endif //ANDROID_RS_SERIALIZE } void Allocation::deferedUploadToBufferObject(const Context *rsc) { @@ -270,6 +271,7 @@ void Allocation::deferedUploadToBufferObject(const Context *rsc) { } void Allocation::uploadToBufferObject(const Context *rsc) { +#ifndef ANDROID_RS_SERIALIZE rsAssert(!mType->getDimY()); rsAssert(!mType->getDimZ()); @@ -288,6 +290,7 @@ void Allocation::uploadToBufferObject(const Context *rsc) { glBufferData(target, mType->getSizeBytes(), getPtr(), GL_DYNAMIC_DRAW); glBindBuffer(target, 0); rsc->checkError("Allocation::uploadToBufferObject"); +#endif //ANDROID_RS_SERIALIZE } void Allocation::uploadCheck(Context *rsc) { @@ -386,7 +389,7 @@ void Allocation::elementData(Context *rsc, uint32_t x, const void *data, ptr += mType->getElement()->getFieldOffsetBytes(cIdx); if (sizeBytes != e->getSizeBytes()) { - LOGE("Error Allocation::subElementData data size %i does not match field size %i.", sizeBytes, e->getSizeBytes()); + LOGE("Error Allocation::subElementData data size %i does not match field size %zu.", sizeBytes, e->getSizeBytes()); rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size."); return; } @@ -429,7 +432,7 @@ void Allocation::elementData(Context *rsc, uint32_t x, uint32_t y, ptr += mType->getElement()->getFieldOffsetBytes(cIdx); if (sizeBytes != e->getSizeBytes()) { - LOGE("Error Allocation::subElementData data size %i does not match field size %i.", sizeBytes, e->getSizeBytes()); + LOGE("Error Allocation::subElementData data size %i does not match field size %zu.", sizeBytes, e->getSizeBytes()); rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size."); return; } @@ -445,10 +448,13 @@ void Allocation::elementData(Context *rsc, uint32_t x, uint32_t y, } void Allocation::addProgramToDirty(const Program *p) { +#ifndef ANDROID_RS_SERIALIZE mToDirtyList.push(p); +#endif //ANDROID_RS_SERIALIZE } void Allocation::removeProgramToDirty(const Program *p) { +#ifndef ANDROID_RS_SERIALIZE for (size_t ct=0; ct < mToDirtyList.size(); ct++) { if (mToDirtyList[ct] == p) { mToDirtyList.removeAt(ct); @@ -456,6 +462,7 @@ void Allocation::removeProgramToDirty(const Program *p) { } } rsAssert(0); +#endif //ANDROID_RS_SERIALIZE } void Allocation::dumpLOGV(const char *prefix) const { @@ -530,9 +537,11 @@ Allocation *Allocation::createFromStream(Context *rsc, IStream *stream) { } void Allocation::sendDirty() const { +#ifndef ANDROID_RS_SERIALIZE for (size_t ct=0; ct < mToDirtyList.size(); ct++) { mToDirtyList[ct]->forceDirty(); } +#endif //ANDROID_RS_SERIALIZE } void Allocation::incRefs(const void *ptr, size_t ct, size_t startOff) const { @@ -591,7 +600,7 @@ void Allocation::resize2D(Context *rsc, uint32_t dimX, uint32_t dimY) { ///////////////// // - +#ifndef ANDROID_RS_SERIALIZE namespace android { namespace renderscript { @@ -674,8 +683,6 @@ static void mip(const Adapter2D &out, const Adapter2D &in) { } } -#ifndef ANDROID_RS_BUILD_FOR_HOST - void rsi_AllocationSyncAll(Context *rsc, RsAllocation va, RsAllocationUsageType src) { Allocation *a = static_cast<Allocation *>(va); a->syncAll(rsc, src); @@ -739,8 +746,6 @@ void rsi_AllocationResize2D(Context *rsc, RsAllocation va, uint32_t dimX, uint32 a->resize2D(rsc, dimX, dimY); } -#endif //ANDROID_RS_BUILD_FOR_HOST - } } @@ -840,3 +845,5 @@ RsAllocation rsaAllocationCubeCreateFromBitmap(RsContext con, RsType vtype, texAlloc->deferedUploadToTexture(rsc); return texAlloc; } + +#endif //ANDROID_RS_SERIALIZE diff --git a/libs/rs/rsAnimation.cpp b/libs/rs/rsAnimation.cpp index 6abda3c0cd14..48b4f029c6d0 100644 --- a/libs/rs/rsAnimation.cpp +++ b/libs/rs/rsAnimation.cpp @@ -14,12 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" -#else -#include "rsContextHostStub.h" -#endif //ANDROID_RS_BUILD_FOR_HOST - #include "rsAnimation.h" diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp index 81ade5d4f60f..4c4987aaf7a4 100644 --- a/libs/rs/rsComponent.cpp +++ b/libs/rs/rsComponent.cpp @@ -16,10 +16,8 @@ #include "rsComponent.h" -#ifndef ANDROID_RS_BUILD_FOR_HOST +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> -#else -#include <OpenGL/gl.h> #endif using namespace android; @@ -183,6 +181,7 @@ bool Component::isReference() const { } uint32_t Component::getGLType() const { +#ifndef ANDROID_RS_SERIALIZE switch (mType) { case RS_TYPE_UNSIGNED_5_6_5: return GL_UNSIGNED_SHORT_5_6_5; case RS_TYPE_UNSIGNED_5_5_5_1: return GL_UNSIGNED_SHORT_5_5_5_1; @@ -196,11 +195,12 @@ uint32_t Component::getGLType() const { case RS_TYPE_SIGNED_16: return GL_SHORT; default: break; } - +#endif //ANDROID_RS_SERIALIZE return 0; } uint32_t Component::getGLFormat() const { +#ifndef ANDROID_RS_SERIALIZE switch (mKind) { case RS_KIND_PIXEL_L: return GL_LUMINANCE; case RS_KIND_PIXEL_A: return GL_ALPHA; @@ -209,6 +209,7 @@ uint32_t Component::getGLFormat() const { case RS_KIND_PIXEL_RGBA: return GL_RGBA; default: break; } +#endif //ANDROID_RS_SERIALIZE return 0; } diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h index 9f94f26aa8e4..c5e32a67dd13 100644 --- a/libs/rs/rsContext.h +++ b/libs/rs/rsContext.h @@ -18,16 +18,16 @@ #define ANDROID_RS_CONTEXT_H #include "rsUtils.h" -#include "rsMutex.h" - -#include "rsThreadIO.h" #include "rsType.h" -#include "rsMatrix.h" #include "rsAllocation.h" #include "rsMesh.h" + +#ifndef ANDROID_RS_SERIALIZE +#include "rsMutex.h" +#include "rsThreadIO.h" +#include "rsMatrix.h" #include "rsDevice.h" #include "rsScriptC.h" -#include "rsAllocation.h" #include "rsAdapter.h" #include "rsSampler.h" #include "rsFont.h" @@ -42,6 +42,7 @@ #include "rsLocklessFifo.h" #include <ui/egl/android_natives.h> +#endif // ANDROID_RS_SERIALIZE // --------------------------------------------------------------------------- namespace android { @@ -66,6 +67,8 @@ namespace renderscript { #define CHECK_OBJ_OR_NULL(o) #endif +#ifndef ANDROID_RS_SERIALIZE + class Context { public: static Context * createContext(Device *, const RsSurfaceConfig *sc); @@ -321,6 +324,39 @@ private: uint32_t mAverageFPS; }; -} -} +#else + +class Context { +public: + Context() { + mObjHead = NULL; + } + ~Context() { + ObjectBase::zeroAllUserRef(this); + } + + ElementState mStateElement; + TypeState mStateType; + + struct { + bool mLogTimes; + bool mLogScripts; + bool mLogObjects; + bool mLogShaders; + bool mLogShadersAttr; + bool mLogShadersUniforms; + bool mLogVisual; + } props; + + void setError(RsError e, const char *msg = NULL) { } + + mutable const ObjectBase * mObjHead; + +protected: + +}; +#endif //ANDROID_RS_SERIALIZE + +} // renderscript +} // android #endif diff --git a/libs/rs/rsContextHostStub.h b/libs/rs/rsContextHostStub.h deleted file mode 100644 index 8cfb38bd5b18..000000000000 --- a/libs/rs/rsContextHostStub.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_RS_CONTEXT_HOST_STUB_H -#define ANDROID_RS_CONTEXT_HOST_STUB_H - -#include "rsUtils.h" -//#include "rsMutex.h" - -//#include "rsThreadIO.h" -#include "rsType.h" -#include "rsMatrix.h" -#include "rsAllocation.h" -#include "rsMesh.h" -//#include "rsDevice.h" -#include "rsScriptC.h" -#include "rsAllocation.h" -#include "rsAdapter.h" -#include "rsSampler.h" -#include "rsProgramFragment.h" -#include "rsProgramStore.h" -#include "rsProgramRaster.h" -#include "rsProgramVertex.h" -#include "rsShaderCache.h" -#include "rsVertexArray.h" - -//#include "rsgApiStructs.h" -//#include "rsLocklessFifo.h" - -//#include <ui/egl/android_natives.h> - -// --------------------------------------------------------------------------- -namespace android { -namespace renderscript { - -class Device; - -class Context { -public: - Context(Device *, bool isGraphics, bool useDepth) { - mObjHead = NULL; - } - ~Context() { - } - - - //StructuredAllocationContext mStateAllocation; - ElementState mStateElement; - TypeState mStateType; - SamplerState mStateSampler; - //ProgramFragmentState mStateFragment; - ProgramStoreState mStateFragmentStore; - //ProgramRasterState mStateRaster; - //ProgramVertexState mStateVertex; - VertexArrayState mStateVertexArray; - - //ScriptCState mScriptC; - ShaderCache mShaderCache; - - RsSurfaceConfig mUserSurfaceConfig; - - //bool setupCheck(); - - ProgramFragment * getDefaultProgramFragment() const { - return NULL; - } - ProgramVertex * getDefaultProgramVertex() const { - return NULL; - } - ProgramStore * getDefaultProgramStore() const { - return NULL; - } - ProgramRaster * getDefaultProgramRaster() const { - return NULL; - } - - uint32_t getWidth() const {return 0;} - uint32_t getHeight() const {return 0;} - - // Timers - enum Timers { - RS_TIMER_IDLE, - RS_TIMER_INTERNAL, - RS_TIMER_SCRIPT, - RS_TIMER_CLEAR_SWAP, - _RS_TIMER_TOTAL - }; - - bool checkVersion1_1() const {return false; } - bool checkVersion2_0() const {return false; } - - struct { - bool mLogTimes; - bool mLogScripts; - bool mLogObjects; - bool mLogShaders; - bool mLogShadersAttr; - bool mLogShadersUniforms; - bool mLogVisual; - } props; - - void dumpDebug() const { } - void checkError(const char *) const { }; - void setError(RsError e, const char *msg = NULL) { } - - mutable const ObjectBase * mObjHead; - - bool ext_OES_texture_npot() const {return mGL.OES_texture_npot;} - bool ext_GL_NV_texture_npot_2D_mipmap() const {return mGL.GL_NV_texture_npot_2D_mipmap;} - float ext_texture_max_aniso() const {return mGL.EXT_texture_max_aniso; } - uint32_t getMaxFragmentTextures() const {return mGL.mMaxFragmentTextureImageUnits;} - uint32_t getMaxFragmentUniformVectors() const {return mGL.mMaxFragmentUniformVectors;} - uint32_t getMaxVertexUniformVectors() const {return mGL.mMaxVertexUniformVectors;} - uint32_t getMaxVertexAttributes() const {return mGL.mMaxVertexAttribs;} - -protected: - - struct { - const uint8_t * mVendor; - const uint8_t * mRenderer; - const uint8_t * mVersion; - const uint8_t * mExtensions; - - uint32_t mMajorVersion; - uint32_t mMinorVersion; - - int32_t mMaxVaryingVectors; - int32_t mMaxTextureImageUnits; - - int32_t mMaxFragmentTextureImageUnits; - int32_t mMaxFragmentUniformVectors; - - int32_t mMaxVertexAttribs; - int32_t mMaxVertexUniformVectors; - int32_t mMaxVertexTextureUnits; - - bool OES_texture_npot; - bool GL_NV_texture_npot_2D_mipmap; - float EXT_texture_max_aniso; - } mGL; - -}; - -} -} -#endif diff --git a/libs/rs/rsDevice.cpp b/libs/rs/rsDevice.cpp index dd964452e5b5..d7d03f6a283d 100644 --- a/libs/rs/rsDevice.cpp +++ b/libs/rs/rsDevice.cpp @@ -15,11 +15,7 @@ */ #include "rsDevice.h" -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" -#else -#include "rsContextHostStub.h" -#endif using namespace android; using namespace android::renderscript; diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp index 6ae8bb8f640e..477cb6166b21 100644 --- a/libs/rs/rsElement.cpp +++ b/libs/rs/rsElement.cpp @@ -15,13 +15,7 @@ */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" -#include <GLES/gl.h> -#else -#include "rsContextHostStub.h" -#include <OpenGL/gl.h> -#endif using namespace android; using namespace android::renderscript; @@ -65,7 +59,7 @@ size_t Element::getSizeBits() const { void Element::dumpLOGV(const char *prefix) const { ObjectBase::dumpLOGV(prefix); - LOGV("%s Element: fieldCount: %i, size bytes: %i", prefix, mFieldCount, getSizeBytes()); + LOGV("%s Element: fieldCount: %zu, size bytes: %zu", prefix, mFieldCount, getSizeBytes()); for (uint32_t ct = 0; ct < mFieldCount; ct++) { LOGV("%s Element field index: %u ------------------", prefix, ct); LOGV("%s name: %s, offsetBits: %u, arraySize: %u", diff --git a/libs/rs/rsFileA3D.cpp b/libs/rs/rsFileA3D.cpp index d34ddd68b77e..cd02c24b981f 100644 --- a/libs/rs/rsFileA3D.cpp +++ b/libs/rs/rsFileA3D.cpp @@ -15,12 +15,7 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" -#else -#include "rsContextHostStub.h" -#endif - #include "rsFileA3D.h" #include "rsMesh.h" @@ -249,31 +244,31 @@ ObjectBase *FileA3D::initializeFromEntry(size_t index) { entry->mRsObj = Allocation::createFromStream(mRSC, mReadStream); break; case RS_A3D_CLASS_ID_PROGRAM_VERTEX: - entry->mRsObj = ProgramVertex::createFromStream(mRSC, mReadStream); + //entry->mRsObj = ProgramVertex::createFromStream(mRSC, mReadStream); break; case RS_A3D_CLASS_ID_PROGRAM_RASTER: - entry->mRsObj = ProgramRaster::createFromStream(mRSC, mReadStream); + //entry->mRsObj = ProgramRaster::createFromStream(mRSC, mReadStream); break; case RS_A3D_CLASS_ID_PROGRAM_FRAGMENT: - entry->mRsObj = ProgramFragment::createFromStream(mRSC, mReadStream); + //entry->mRsObj = ProgramFragment::createFromStream(mRSC, mReadStream); break; case RS_A3D_CLASS_ID_PROGRAM_STORE: - entry->mRsObj = ProgramStore::createFromStream(mRSC, mReadStream); + //entry->mRsObj = ProgramStore::createFromStream(mRSC, mReadStream); break; case RS_A3D_CLASS_ID_SAMPLER: - entry->mRsObj = Sampler::createFromStream(mRSC, mReadStream); + //entry->mRsObj = Sampler::createFromStream(mRSC, mReadStream); break; case RS_A3D_CLASS_ID_ANIMATION: - entry->mRsObj = Animation::createFromStream(mRSC, mReadStream); + //entry->mRsObj = Animation::createFromStream(mRSC, mReadStream); break; case RS_A3D_CLASS_ID_ADAPTER_1D: - entry->mRsObj = Adapter1D::createFromStream(mRSC, mReadStream); + //entry->mRsObj = Adapter1D::createFromStream(mRSC, mReadStream); break; case RS_A3D_CLASS_ID_ADAPTER_2D: - entry->mRsObj = Adapter2D::createFromStream(mRSC, mReadStream); + //entry->mRsObj = Adapter2D::createFromStream(mRSC, mReadStream); break; case RS_A3D_CLASS_ID_SCRIPT_C: - return NULL; + break; } if (entry->mRsObj) { entry->mRsObj->incUserRef(); diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp index 7fdfbe05f287..8a5ab99e8bad 100644 --- a/libs/rs/rsFont.cpp +++ b/libs/rs/rsFont.cpp @@ -15,11 +15,7 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" -#else -#include "rsContextHostStub.h" -#endif #include "rsFont.h" #include "rsProgramFragment.h" diff --git a/libs/rs/rsMesh.cpp b/libs/rs/rsMesh.cpp index baf4c5319074..76fe62da8830 100644 --- a/libs/rs/rsMesh.cpp +++ b/libs/rs/rsMesh.cpp @@ -14,17 +14,11 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" - +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES2/gl2.h> #include <GLES/glext.h> -#else -#include "rsContextHostStub.h" - -#include <OpenGL/gl.h> -#include <OpenGl/glext.h> #endif using namespace android; @@ -35,10 +29,13 @@ Mesh::Mesh(Context *rsc) : ObjectBase(rsc) { mPrimitivesCount = 0; mVertexBuffers = NULL; mVertexBufferCount = 0; + +#ifndef ANDROID_RS_SERIALIZE mAttribs = NULL; mAttribAllocationIndex = NULL; mAttribCount = 0; +#endif } Mesh::~Mesh() { @@ -53,12 +50,97 @@ Mesh::~Mesh() { delete[] mPrimitives; } +#ifndef ANDROID_RS_SERIALIZE if (mAttribs) { delete[] mAttribs; delete[] mAttribAllocationIndex; } +#endif +} + +void Mesh::serialize(OStream *stream) const { + // Need to identify ourselves + stream->addU32((uint32_t)getClassId()); + + String8 name(getName()); + stream->addString(&name); + + // Store number of vertex streams + stream->addU32(mVertexBufferCount); + for (uint32_t vCount = 0; vCount < mVertexBufferCount; vCount ++) { + mVertexBuffers[vCount]->serialize(stream); + } + + stream->addU32(mPrimitivesCount); + // Store the primitives + for (uint32_t pCount = 0; pCount < mPrimitivesCount; pCount ++) { + Primitive_t * prim = mPrimitives[pCount]; + + stream->addU8((uint8_t)prim->mPrimitive); + + if (prim->mIndexBuffer.get()) { + stream->addU32(1); + prim->mIndexBuffer->serialize(stream); + } else { + stream->addU32(0); + } + } } +Mesh *Mesh::createFromStream(Context *rsc, IStream *stream) { + // First make sure we are reading the correct object + RsA3DClassID classID = (RsA3DClassID)stream->loadU32(); + if (classID != RS_A3D_CLASS_ID_MESH) { + LOGE("mesh loading skipped due to invalid class id"); + return NULL; + } + + Mesh * mesh = new Mesh(rsc); + + String8 name; + stream->loadString(&name); + mesh->setName(name.string(), name.size()); + + mesh->mVertexBufferCount = stream->loadU32(); + if (mesh->mVertexBufferCount) { + mesh->mVertexBuffers = new ObjectBaseRef<Allocation>[mesh->mVertexBufferCount]; + + for (uint32_t vCount = 0; vCount < mesh->mVertexBufferCount; vCount ++) { + Allocation *vertexAlloc = Allocation::createFromStream(rsc, stream); + mesh->mVertexBuffers[vCount].set(vertexAlloc); + } + } + + mesh->mPrimitivesCount = stream->loadU32(); + if (mesh->mPrimitivesCount) { + mesh->mPrimitives = new Primitive_t *[mesh->mPrimitivesCount]; + + // load all primitives + for (uint32_t pCount = 0; pCount < mesh->mPrimitivesCount; pCount ++) { + Primitive_t * prim = new Primitive_t; + mesh->mPrimitives[pCount] = prim; + + prim->mPrimitive = (RsPrimitive)stream->loadU8(); + + // Check to see if the index buffer was stored + uint32_t isIndexPresent = stream->loadU32(); + if (isIndexPresent) { + Allocation *indexAlloc = Allocation::createFromStream(rsc, stream); + prim->mIndexBuffer.set(indexAlloc); + } + } + } + +#ifndef ANDROID_RS_SERIALIZE + mesh->updateGLPrimitives(); + mesh->initVertexAttribs(); + mesh->uploadAll(rsc); +#endif + return mesh; +} + +#ifndef ANDROID_RS_SERIALIZE + bool Mesh::isValidGLComponent(const Element *elem, uint32_t fieldIdx) { // Do not create attribs for padding if (elem->getFieldName(fieldIdx)[0] == '#') { @@ -224,86 +306,6 @@ void Mesh::updateGLPrimitives() { } } -void Mesh::serialize(OStream *stream) const { - // Need to identify ourselves - stream->addU32((uint32_t)getClassId()); - - String8 name(getName()); - stream->addString(&name); - - // Store number of vertex streams - stream->addU32(mVertexBufferCount); - for (uint32_t vCount = 0; vCount < mVertexBufferCount; vCount ++) { - mVertexBuffers[vCount]->serialize(stream); - } - - stream->addU32(mPrimitivesCount); - // Store the primitives - for (uint32_t pCount = 0; pCount < mPrimitivesCount; pCount ++) { - Primitive_t * prim = mPrimitives[pCount]; - - stream->addU8((uint8_t)prim->mPrimitive); - - if (prim->mIndexBuffer.get()) { - stream->addU32(1); - prim->mIndexBuffer->serialize(stream); - } else { - stream->addU32(0); - } - } -} - -Mesh *Mesh::createFromStream(Context *rsc, IStream *stream) { - // First make sure we are reading the correct object - RsA3DClassID classID = (RsA3DClassID)stream->loadU32(); - if (classID != RS_A3D_CLASS_ID_MESH) { - LOGE("mesh loading skipped due to invalid class id"); - return NULL; - } - - Mesh * mesh = new Mesh(rsc); - - String8 name; - stream->loadString(&name); - mesh->setName(name.string(), name.size()); - - mesh->mVertexBufferCount = stream->loadU32(); - if (mesh->mVertexBufferCount) { - mesh->mVertexBuffers = new ObjectBaseRef<Allocation>[mesh->mVertexBufferCount]; - - for (uint32_t vCount = 0; vCount < mesh->mVertexBufferCount; vCount ++) { - Allocation *vertexAlloc = Allocation::createFromStream(rsc, stream); - mesh->mVertexBuffers[vCount].set(vertexAlloc); - } - } - - mesh->mPrimitivesCount = stream->loadU32(); - if (mesh->mPrimitivesCount) { - mesh->mPrimitives = new Primitive_t *[mesh->mPrimitivesCount]; - - // load all primitives - for (uint32_t pCount = 0; pCount < mesh->mPrimitivesCount; pCount ++) { - Primitive_t * prim = new Primitive_t; - mesh->mPrimitives[pCount] = prim; - - prim->mPrimitive = (RsPrimitive)stream->loadU8(); - - // Check to see if the index buffer was stored - uint32_t isIndexPresent = stream->loadU32(); - if (isIndexPresent) { - Allocation *indexAlloc = Allocation::createFromStream(rsc, stream); - prim->mIndexBuffer.set(indexAlloc); - } - } - } - - mesh->updateGLPrimitives(); - mesh->initVertexAttribs(); - mesh->uploadAll(rsc); - - return mesh; -} - void Mesh::computeBBox() { float *posPtr = NULL; uint32_t vectorSize = 0; @@ -347,13 +349,6 @@ void Mesh::computeBBox() { } } - -MeshContext::MeshContext() { -} - -MeshContext::~MeshContext() { -} - namespace android { namespace renderscript { @@ -428,3 +423,5 @@ void rsaMeshGetIndices(RsContext con, RsMesh mv, RsAllocation *va, uint32_t *pri } } } + +#endif diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h index 410b70bc5632..3e080e2aabb9 100644 --- a/libs/rs/rsMesh.h +++ b/libs/rs/rsMesh.h @@ -50,15 +50,18 @@ public: Primitive_t ** mPrimitives; uint32_t mPrimitivesCount; + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_MESH; } + static Mesh *createFromStream(Context *rsc, IStream *stream); + +#ifndef ANDROID_RS_SERIALIZE void render(Context *) const; void renderPrimitive(Context *, uint32_t primIndex) const; void renderPrimitiveRange(Context *, uint32_t primIndex, uint32_t start, uint32_t len) const; void uploadAll(Context *); void updateGLPrimitives(); - virtual void serialize(OStream *stream) const; - virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_MESH; } - static Mesh *createFromStream(Context *rsc, IStream *stream); + // Bounding volumes float mBBoxMin[3]; @@ -76,12 +79,15 @@ protected: // buffer, it lets us properly map it uint32_t *mAttribAllocationIndex; uint32_t mAttribCount; +#endif }; class MeshContext { public: - MeshContext(); - ~MeshContext(); + MeshContext() { + } + ~MeshContext() { + } }; } diff --git a/libs/rs/rsObjectBase.cpp b/libs/rs/rsObjectBase.cpp index aec2f6794de5..f428f9486d8d 100644 --- a/libs/rs/rsObjectBase.cpp +++ b/libs/rs/rsObjectBase.cpp @@ -15,13 +15,7 @@ */ #include "rsObjectBase.h" - -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" -#else -#include "rsContextHostStub.h" -#endif - using namespace android; using namespace android::renderscript; diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp index 39b85e39ed3f..4ef05bf4372f 100644 --- a/libs/rs/rsProgram.cpp +++ b/libs/rs/rsProgram.cpp @@ -14,15 +14,11 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" +#ifndef ANDROID_RS_SERIALIZE #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> -#else -#include "rsContextHostStub.h" -#include <OpenGL/gl.h> -#include <OpenGL/glext.h> -#endif //ANDROID_RS_BUILD_FOR_HOST +#endif //ANDROID_RS_SERIALIZE #include "rsProgram.h" diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp index 22cd5d39a69e..ff314b7f6429 100644 --- a/libs/rs/rsProgramFragment.cpp +++ b/libs/rs/rsProgramFragment.cpp @@ -14,17 +14,13 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES/glext.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> -#else -#include "rsContextHostStub.h" -#include <OpenGL/gl.h> -#include <OpenGL/glext.h> -#endif //ANDROID_RS_BUILD_FOR_HOST +#endif //ANDROID_RS_SERIALIZE #include "rsProgramFragment.h" diff --git a/libs/rs/rsProgramRaster.cpp b/libs/rs/rsProgramRaster.cpp index f2b5b9af7d4b..ace1572f4010 100644 --- a/libs/rs/rsProgramRaster.cpp +++ b/libs/rs/rsProgramRaster.cpp @@ -14,15 +14,11 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES/glext.h> -#else -#include "rsContextHostStub.h" -#include <OpenGL/gl.h> -#include <OpenGl/glext.h> -#endif //ANDROID_RS_BUILD_FOR_HOST +#endif //ANDROID_RS_SERIALIZE #include "rsProgramRaster.h" diff --git a/libs/rs/rsProgramStore.cpp b/libs/rs/rsProgramStore.cpp index 72ac574af2fe..09b759d9a340 100644 --- a/libs/rs/rsProgramStore.cpp +++ b/libs/rs/rsProgramStore.cpp @@ -14,15 +14,11 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES/glext.h> -#else -#include "rsContextHostStub.h" -#include <OpenGL/gl.h> -#include <OpenGl/glext.h> -#endif //ANDROID_RS_BUILD_FOR_HOST +#endif //ANDROID_RS_SERIALIZE #include "rsProgramStore.h" diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp index ad2beafbf9d2..403c2a6e356b 100644 --- a/libs/rs/rsProgramVertex.cpp +++ b/libs/rs/rsProgramVertex.cpp @@ -14,17 +14,13 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES/glext.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> -#else -#include "rsContextHostStub.h" -#include <OpenGL/gl.h> -#include <OpenGL/glext.h> -#endif //ANDROID_RS_BUILD_FOR_HOST +#endif //ANDROID_RS_SERIALIZE #include "rsProgramVertex.h" diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp index c80aecca79b8..db2383aca7eb 100644 --- a/libs/rs/rsSampler.cpp +++ b/libs/rs/rsSampler.cpp @@ -14,15 +14,11 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST +#include "rsContext.h" +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES/glext.h> -#include "rsContext.h" -#else -#include "rsContextHostStub.h" -#include <OpenGL/gl.h> -#include <OpenGL/glext.h> -#endif //ANDROID_RS_BUILD_FOR_HOST +#endif //ANDROID_RS_SERIALIZE #include "rsSampler.h" diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index fc673a2d517b..445a4e4edeee 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -485,12 +485,7 @@ bool ScriptCState::runCompiler(Context *rsc, } #if 1 - if (bccLinkBC(s->mBccScript, - resName, - NULL /*rs_runtime_lib_bc*/, - 1 /*rs_runtime_lib_bc_size*/ - /*"1" means skip buffer here, and let libbcc decide*/, - 0) != 0) { + if (bccLinkFile(s->mBccScript, "/system/lib/libclcore.bc", 0) != 0) { LOGE("bcc: FAILS to link bitcode"); return false; } diff --git a/libs/rs/rsShaderCache.cpp b/libs/rs/rsShaderCache.cpp index b958021b37a8..e8d89c21cb77 100644 --- a/libs/rs/rsShaderCache.cpp +++ b/libs/rs/rsShaderCache.cpp @@ -14,14 +14,11 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES2/gl2.h> -#else -#include "rsContextHostStub.h" -#include <OpenGL/gl.h> -#endif //ANDROID_RS_BUILD_FOR_HOST +#endif //ANDROID_RS_SERIALIZE using namespace android; using namespace android::renderscript; diff --git a/libs/rs/rsStream.cpp b/libs/rs/rsStream.cpp index 49ed567dceca..b9df0ccfac6a 100644 --- a/libs/rs/rsStream.cpp +++ b/libs/rs/rsStream.cpp @@ -15,12 +15,7 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" -#else -#include "rsContextHostStub.h" -#endif - #include "rsStream.h" using namespace android; diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp index d7b5f1204361..cd2be94979a8 100644 --- a/libs/rs/rsType.cpp +++ b/libs/rs/rsType.cpp @@ -14,13 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" -#include <GLES/gl.h> -#else -#include "rsContextHostStub.h" -#include <OpenGL/gl.h> -#endif using namespace android; using namespace android::renderscript; @@ -146,7 +140,7 @@ uint32_t Type::getLODFaceOffset(uint32_t lod, RsAllocationCubemapFace face, uint void Type::dumpLOGV(const char *prefix) const { char buf[1024]; ObjectBase::dumpLOGV(prefix); - LOGV("%s Type: x=%i y=%i z=%i mip=%i face=%i", prefix, mDimX, mDimY, mDimZ, mDimLOD, mFaces); + LOGV("%s Type: x=%zu y=%zu z=%zu mip=%i face=%i", prefix, mDimX, mDimY, mDimZ, mDimLOD, mFaces); snprintf(buf, sizeof(buf), "%s element: ", prefix); mElement->dumpLOGV(buf); } diff --git a/libs/rs/rsUtils.h b/libs/rs/rsUtils.h index 0699b57fa7fb..3b60af58fb4d 100644 --- a/libs/rs/rsUtils.h +++ b/libs/rs/rsUtils.h @@ -32,7 +32,7 @@ #include <time.h> #include <cutils/atomic.h> -#ifndef ANDROID_RS_BUILD_FOR_HOST +#ifndef ANDROID_RS_SERIALIZE #include <EGL/egl.h> #endif diff --git a/libs/rs/rsVertexArray.cpp b/libs/rs/rsVertexArray.cpp index d9393fe303af..354ee89f7c5b 100644 --- a/libs/rs/rsVertexArray.cpp +++ b/libs/rs/rsVertexArray.cpp @@ -14,13 +14,10 @@ * limitations under the License. */ -#ifndef ANDROID_RS_BUILD_FOR_HOST #include "rsContext.h" +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES2/gl2.h> -#else -#include "rsContextHostStub.h" -#include <OpenGL/gl.h> #endif using namespace android; diff --git a/libs/rs/scriptc/rs_core.rsh b/libs/rs/scriptc/rs_core.rsh index 464e1d9aba5e..4768bbed1f14 100644 --- a/libs/rs/scriptc/rs_core.rsh +++ b/libs/rs/scriptc/rs_core.rsh @@ -295,6 +295,256 @@ extern void __attribute__((overloadable)) rsMatrixTranspose(rs_matrix3x3 *m); extern void __attribute__((overloadable)) rsMatrixTranspose(rs_matrix2x2 *m); ///////////////////////////////////////////////////// +// quaternion ops +///////////////////////////////////////////////////// + +static void __attribute__((overloadable)) +rsQuaternionSet(rs_quaternion *q, float w, float x, float y, float z) { + q->w = w; + q->x = x; + q->y = y; + q->z = z; +} + +static void __attribute__((overloadable)) +rsQuaternionSet(rs_quaternion *q, const rs_quaternion *rhs) { + q->w = rhs->w; + q->x = rhs->x; + q->y = rhs->y; + q->z = rhs->z; +} + +static void __attribute__((overloadable)) +rsQuaternionMultiply(rs_quaternion *q, float s) { + q->w *= s; + q->x *= s; + q->y *= s; + q->z *= s; +} + +static void __attribute__((overloadable)) +rsQuaternionMultiply(rs_quaternion *q, const rs_quaternion *rhs) { + q->w = -q->x*rhs->x - q->y*rhs->y - q->z*rhs->z + q->w*rhs->w; + q->x = q->x*rhs->w + q->y*rhs->z - q->z*rhs->y + q->w*rhs->x; + q->y = -q->x*rhs->z + q->y*rhs->w + q->z*rhs->x + q->w*rhs->y; + q->z = q->x*rhs->y - q->y*rhs->x + q->z*rhs->w + q->w*rhs->z; +} + +static void +rsQuaternionAdd(rs_quaternion *q, const rs_quaternion *rhs) { + q->w *= rhs->w; + q->x *= rhs->x; + q->y *= rhs->y; + q->z *= rhs->z; +} + +static void +rsQuaternionLoadRotateUnit(rs_quaternion *q, float rot, float x, float y, float z) { + rot *= (float)(M_PI / 180.0f) * 0.5f; + float c = cos(rot); + float s = sin(rot); + + q->w = c; + q->x = x * s; + q->y = y * s; + q->z = z * s; +} + +static void +rsQuaternionLoadRotate(rs_quaternion *q, float rot, float x, float y, float z) { + const float len = x*x + y*y + z*z; + if (len != 1) { + const float recipLen = 1.f / sqrt(len); + x *= recipLen; + y *= recipLen; + z *= recipLen; + } + rsQuaternionLoadRotateUnit(q, rot, x, y, z); +} + +static void +rsQuaternionConjugate(rs_quaternion *q) { + q->x = -q->x; + q->y = -q->y; + q->z = -q->z; +} + +static float +rsQuaternionDot(const rs_quaternion *q0, const rs_quaternion *q1) { + return q0->w*q1->w + q0->x*q1->x + q0->y*q1->y + q0->z*q1->z; +} + +static void +rsQuaternionNormalize(rs_quaternion *q) { + const float len = rsQuaternionDot(q, q); + if (len != 1) { + const float recipLen = 1.f / sqrt(len); + rsQuaternionMultiply(q, recipLen); + } +} + +static void +rsQuaternionSlerp(rs_quaternion *q, const rs_quaternion *q0, const rs_quaternion *q1, float t) { + if (t <= 0.0f) { + rsQuaternionSet(q, q0); + return; + } + if (t >= 1.0f) { + rsQuaternionSet(q, q1); + return; + } + + rs_quaternion tempq0, tempq1; + rsQuaternionSet(&tempq0, q0); + rsQuaternionSet(&tempq1, q1); + + float angle = rsQuaternionDot(q0, q1); + if (angle < 0) { + rsQuaternionMultiply(&tempq0, -1.0f); + angle *= -1.0f; + } + + float scale, invScale; + if (angle + 1.0f > 0.05f) { + if (1.0f - angle >= 0.05f) { + float theta = acos(angle); + float invSinTheta = 1.0f / sin(theta); + scale = sin(theta * (1.0f - t)) * invSinTheta; + invScale = sin(theta * t) * invSinTheta; + } else { + scale = 1.0f - t; + invScale = t; + } + } else { + rsQuaternionSet(&tempq1, tempq0.z, -tempq0.y, tempq0.x, -tempq0.w); + scale = sin(M_PI * (0.5f - t)); + invScale = sin(M_PI * t); + } + + rsQuaternionSet(q, tempq0.w*scale + tempq1.w*invScale, tempq0.x*scale + tempq1.x*invScale, + tempq0.y*scale + tempq1.y*invScale, tempq0.z*scale + tempq1.z*invScale); +} + +static void rsQuaternionGetMatrixUnit(rs_matrix4x4 *m, const rs_quaternion *q) { + float x2 = 2.0f * q->x * q->x; + float y2 = 2.0f * q->y * q->y; + float z2 = 2.0f * q->z * q->z; + float xy = 2.0f * q->x * q->y; + float wz = 2.0f * q->w * q->z; + float xz = 2.0f * q->x * q->z; + float wy = 2.0f * q->w * q->y; + float wx = 2.0f * q->w * q->x; + float yz = 2.0f * q->y * q->z; + + m->m[0] = 1.0f - y2 - z2; + m->m[1] = xy - wz; + m->m[2] = xz + wy; + m->m[3] = 0.0f; + + m->m[4] = xy + wz; + m->m[5] = 1.0f - x2 - z2; + m->m[6] = yz - wx; + m->m[7] = 0.0f; + + m->m[8] = xz - wy; + m->m[9] = yz - wx; + m->m[10] = 1.0f - x2 - y2; + m->m[11] = 0.0f; + + m->m[12] = 0.0f; + m->m[13] = 0.0f; + m->m[14] = 0.0f; + m->m[15] = 1.0f; +} + +///////////////////////////////////////////////////// +// utility funcs +///////////////////////////////////////////////////// +__inline__ static void __attribute__((overloadable, always_inline)) +rsExtractFrustumPlanes(const rs_matrix4x4 *modelViewProj, + float4 *left, float4 *right, + float4 *top, float4 *bottom, + float4 *near, float4 *far) { + // x y z w = a b c d in the plane equation + left->x = modelViewProj->m[3] + modelViewProj->m[0]; + left->y = modelViewProj->m[7] + modelViewProj->m[4]; + left->z = modelViewProj->m[11] + modelViewProj->m[8]; + left->w = modelViewProj->m[15] + modelViewProj->m[12]; + + right->x = modelViewProj->m[3] - modelViewProj->m[0]; + right->y = modelViewProj->m[7] - modelViewProj->m[4]; + right->z = modelViewProj->m[11] - modelViewProj->m[8]; + right->w = modelViewProj->m[15] - modelViewProj->m[12]; + + top->x = modelViewProj->m[3] - modelViewProj->m[1]; + top->y = modelViewProj->m[7] - modelViewProj->m[5]; + top->z = modelViewProj->m[11] - modelViewProj->m[9]; + top->w = modelViewProj->m[15] - modelViewProj->m[13]; + + bottom->x = modelViewProj->m[3] + modelViewProj->m[1]; + bottom->y = modelViewProj->m[7] + modelViewProj->m[5]; + bottom->z = modelViewProj->m[11] + modelViewProj->m[9]; + bottom->w = modelViewProj->m[15] + modelViewProj->m[13]; + + near->x = modelViewProj->m[3] + modelViewProj->m[2]; + near->y = modelViewProj->m[7] + modelViewProj->m[6]; + near->z = modelViewProj->m[11] + modelViewProj->m[10]; + near->w = modelViewProj->m[15] + modelViewProj->m[14]; + + far->x = modelViewProj->m[3] - modelViewProj->m[2]; + far->y = modelViewProj->m[7] - modelViewProj->m[6]; + far->z = modelViewProj->m[11] - modelViewProj->m[10]; + far->w = modelViewProj->m[15] - modelViewProj->m[14]; + + float len = length(left->xyz); + *left /= len; + len = length(right->xyz); + *right /= len; + len = length(top->xyz); + *top /= len; + len = length(bottom->xyz); + *bottom /= len; + len = length(near->xyz); + *near /= len; + len = length(far->xyz); + *far /= len; +} + +__inline__ static bool __attribute__((overloadable, always_inline)) +rsIsSphereInFrustum(float4 *sphere, + float4 *left, float4 *right, + float4 *top, float4 *bottom, + float4 *near, float4 *far) { + + float distToCenter = dot(left->xyz, sphere->xyz) + left->w; + if (distToCenter < -sphere->w) { + return false; + } + distToCenter = dot(right->xyz, sphere->xyz) + right->w; + if (distToCenter < -sphere->w) { + return false; + } + distToCenter = dot(top->xyz, sphere->xyz) + top->w; + if (distToCenter < -sphere->w) { + return false; + } + distToCenter = dot(bottom->xyz, sphere->xyz) + bottom->w; + if (distToCenter < -sphere->w) { + return false; + } + distToCenter = dot(near->xyz, sphere->xyz) + near->w; + if (distToCenter < -sphere->w) { + return false; + } + distToCenter = dot(far->xyz, sphere->xyz) + far->w; + if (distToCenter < -sphere->w) { + return false; + } + return true; +} + + +///////////////////////////////////////////////////// // int ops ///////////////////////////////////////////////////// diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh index 367af4642e23..a010096507b1 100644 --- a/libs/rs/scriptc/rs_types.rsh +++ b/libs/rs/scriptc/rs_types.rsh @@ -73,6 +73,8 @@ typedef struct { float m[4]; } rs_matrix2x2; +typedef float4 rs_quaternion; + #define RS_PACKED __attribute__((packed, aligned(4))) #endif diff --git a/libs/surfaceflinger_client/ISurfaceComposerClient.cpp b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp index 2cc1f8e34383..7730eb1fa91b 100644 --- a/libs/surfaceflinger_client/ISurfaceComposerClient.cpp +++ b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp @@ -157,7 +157,7 @@ status_t BnSurfaceComposerClient::onTransact( const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); const int self_pid = getpid(); - if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS)) { + if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) { // we're called from a different process, do the real check if (!checkCallingPermission( String16("android.permission.ACCESS_SURFACE_FLINGER"))) diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index 0d55f0861d60..f9990bb7d9e3 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -60,7 +60,12 @@ LOCAL_SHARED_LIBRARIES := \ libEGL \ libpixelflinger \ libhardware \ - libhardware_legacy + libhardware_legacy \ + libskia \ + libbinder + +LOCAL_C_INCLUDES := \ + external/skia/include/core LOCAL_MODULE:= libui diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index b8d59e68e6c5..e3107d50fd8b 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -15,6 +15,16 @@ #include <ui/Input.h> +#include <math.h> + +#ifdef HAVE_ANDROID_OS +#include <binder/Parcel.h> + +#include "SkPoint.h" +#include "SkMatrix.h" +#include "SkScalar.h" +#endif + namespace android { static const char* CONFIGURATION_FILE_DIR[] = { @@ -237,6 +247,41 @@ void KeyEvent::initialize(const KeyEvent& from) { mEventTime = from.mEventTime; } + +// --- PointerCoords --- + +#ifdef HAVE_ANDROID_OS +status_t PointerCoords::readFromParcel(Parcel* parcel) { + bits = parcel->readInt32(); + + uint32_t count = __builtin_popcount(bits); + if (count > MAX_AXES) { + return BAD_VALUE; + } + + for (uint32_t i = 0; i < count; i++) { + values[i] = parcel->readInt32(); + } + return OK; +} + +status_t PointerCoords::writeToParcel(Parcel* parcel) const { + parcel->writeInt32(bits); + + uint32_t count = __builtin_popcount(bits); + for (uint32_t i = 0; i < count; i++) { + parcel->writeInt32(values[i]); + } + return OK; +} +#endif + +void PointerCoords::tooManyAxes(int axis) { + LOGW("Could not set value for axis %d because the PointerCoords structure is full and " + "cannot contain more than %d axis values.", axis, int(MAX_AXES)); +} + + // --- MotionEvent --- void MotionEvent::initialize( @@ -272,6 +317,33 @@ void MotionEvent::initialize( addSample(eventTime, pointerCoords); } +void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { + InputEvent::initialize(other->mDeviceId, other->mSource); + mAction = other->mAction; + mFlags = other->mFlags; + mEdgeFlags = other->mEdgeFlags; + mMetaState = other->mMetaState; + mXOffset = other->mXOffset; + mYOffset = other->mYOffset; + mXPrecision = other->mXPrecision; + mYPrecision = other->mYPrecision; + mDownTime = other->mDownTime; + mPointerIds = other->mPointerIds; + + if (keepHistory) { + mSampleEventTimes = other->mSampleEventTimes; + mSamplePointerCoords = other->mSamplePointerCoords; + } else { + mSampleEventTimes.clear(); + mSampleEventTimes.push(other->getEventTime()); + mSamplePointerCoords.clear(); + size_t pointerCount = other->getPointerCount(); + size_t historySize = other->getHistorySize(); + mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array() + + (historySize * pointerCount), pointerCount); + } +} + void MotionEvent::addSample( int64_t eventTime, const PointerCoords* pointerCoords) { @@ -279,11 +351,224 @@ void MotionEvent::addSample( mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); } +const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { + return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; +} + +float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const { + return getRawPointerCoords(pointerIndex)->getAxisValue(axis); +} + +float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { + float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis); + switch (axis) { + case AMOTION_EVENT_AXIS_X: + value += mXOffset; + break; + case AMOTION_EVENT_AXIS_Y: + value += mYOffset; + break; + } + return value; +} + +const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( + size_t pointerIndex, size_t historicalIndex) const { + return &mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex]; +} + +float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, + size_t historicalIndex) const { + return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); +} + +float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, + size_t historicalIndex) const { + float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); + switch (axis) { + case AMOTION_EVENT_AXIS_X: + value += mXOffset; + break; + case AMOTION_EVENT_AXIS_Y: + value += mYOffset; + break; + } + return value; +} + void MotionEvent::offsetLocation(float xOffset, float yOffset) { mXOffset += xOffset; mYOffset += yOffset; } +static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) { + float* value = c.editAxisValue(axis); + if (value) { + *value *= scaleFactor; + } +} + +void MotionEvent::scale(float scaleFactor) { + mXOffset *= scaleFactor; + mYOffset *= scaleFactor; + mXPrecision *= scaleFactor; + mYPrecision *= scaleFactor; + + size_t numSamples = mSamplePointerCoords.size(); + for (size_t i = 0; i < numSamples; i++) { + PointerCoords& c = mSamplePointerCoords.editItemAt(i); + // No need to scale pressure or size since they are normalized. + // No need to scale orientation since it is meaningless to do so. + scaleAxisValue(c, AMOTION_EVENT_AXIS_X, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_Y, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); + } +} + +#ifdef HAVE_ANDROID_OS +static inline float transformAngle(const SkMatrix* matrix, float angleRadians) { + // Construct and transform a vector oriented at the specified clockwise angle from vertical. + // Coordinate system: down is increasing Y, right is increasing X. + SkPoint vector; + vector.fX = SkFloatToScalar(sinf(angleRadians)); + vector.fY = SkFloatToScalar(-cosf(angleRadians)); + matrix->mapVectors(& vector, 1); + + // Derive the transformed vector's clockwise angle from vertical. + float result = atan2f(SkScalarToFloat(vector.fX), SkScalarToFloat(-vector.fY)); + if (result < - M_PI_2) { + result += M_PI; + } else if (result > M_PI_2) { + result -= M_PI; + } + return result; +} + +void MotionEvent::transform(const SkMatrix* matrix) { + float oldXOffset = mXOffset; + float oldYOffset = mYOffset; + + // The tricky part of this implementation is to preserve the value of + // rawX and rawY. So we apply the transformation to the first point + // then derive an appropriate new X/Y offset that will preserve rawX and rawY. + SkPoint point; + float rawX = getRawX(0); + float rawY = getRawY(0); + matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset), + & point); + float newX = SkScalarToFloat(point.fX); + float newY = SkScalarToFloat(point.fY); + float newXOffset = newX - rawX; + float newYOffset = newY - rawY; + + mXOffset = newXOffset; + mYOffset = newYOffset; + + // Apply the transformation to all samples. + size_t numSamples = mSamplePointerCoords.size(); + for (size_t i = 0; i < numSamples; i++) { + PointerCoords& c = mSamplePointerCoords.editItemAt(i); + float* xPtr = c.editAxisValue(AMOTION_EVENT_AXIS_X); + float* yPtr = c.editAxisValue(AMOTION_EVENT_AXIS_Y); + if (xPtr && yPtr) { + float x = *xPtr + oldXOffset; + float y = *yPtr + oldYOffset; + matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), & point); + *xPtr = SkScalarToFloat(point.fX) - newXOffset; + *yPtr = SkScalarToFloat(point.fY) - newYOffset; + } + + float* orientationPtr = c.editAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); + if (orientationPtr) { + *orientationPtr = transformAngle(matrix, *orientationPtr); + } + } +} + +status_t MotionEvent::readFromParcel(Parcel* parcel) { + size_t pointerCount = parcel->readInt32(); + size_t sampleCount = parcel->readInt32(); + if (pointerCount == 0 || pointerCount > MAX_POINTERS || sampleCount == 0) { + return BAD_VALUE; + } + + mDeviceId = parcel->readInt32(); + mSource = parcel->readInt32(); + mAction = parcel->readInt32(); + mFlags = parcel->readInt32(); + mEdgeFlags = parcel->readInt32(); + mMetaState = parcel->readInt32(); + mXOffset = parcel->readFloat(); + mYOffset = parcel->readFloat(); + mXPrecision = parcel->readFloat(); + mYPrecision = parcel->readFloat(); + mDownTime = parcel->readInt64(); + + mPointerIds.clear(); + mPointerIds.setCapacity(pointerCount); + mSampleEventTimes.clear(); + mSampleEventTimes.setCapacity(sampleCount); + mSamplePointerCoords.clear(); + mSamplePointerCoords.setCapacity(sampleCount * pointerCount); + + for (size_t i = 0; i < pointerCount; i++) { + mPointerIds.push(parcel->readInt32()); + } + + while (sampleCount-- > 0) { + mSampleEventTimes.push(parcel->readInt64()); + for (size_t i = 0; i < pointerCount; i++) { + mSamplePointerCoords.push(); + status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel); + if (status) { + return status; + } + } + } + return OK; +} + +status_t MotionEvent::writeToParcel(Parcel* parcel) const { + size_t pointerCount = mPointerIds.size(); + size_t sampleCount = mSampleEventTimes.size(); + + parcel->writeInt32(pointerCount); + parcel->writeInt32(sampleCount); + + parcel->writeInt32(mDeviceId); + parcel->writeInt32(mSource); + parcel->writeInt32(mAction); + parcel->writeInt32(mFlags); + parcel->writeInt32(mEdgeFlags); + parcel->writeInt32(mMetaState); + parcel->writeFloat(mXOffset); + parcel->writeFloat(mYOffset); + parcel->writeFloat(mXPrecision); + parcel->writeFloat(mYPrecision); + parcel->writeInt64(mDownTime); + + for (size_t i = 0; i < pointerCount; i++) { + parcel->writeInt32(mPointerIds.itemAt(i)); + } + + const PointerCoords* pc = mSamplePointerCoords.array(); + for (size_t h = 0; h < sampleCount; h++) { + parcel->writeInt64(mSampleEventTimes.itemAt(h)); + for (size_t i = 0; i < pointerCount; i++) { + status_t status = (pc++)->writeToParcel(parcel); + if (status) { + return status; + } + } + } + return OK; +} +#endif + + // --- InputDeviceInfo --- InputDeviceInfo::InputDeviceInfo() { @@ -307,8 +592,8 @@ void InputDeviceInfo::initialize(int32_t id, const String8& name) { mMotionRanges.clear(); } -const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t rangeType) const { - ssize_t index = mMotionRanges.indexOfKey(rangeType); +const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t axis) const { + ssize_t index = mMotionRanges.indexOfKey(axis); return index >= 0 ? & mMotionRanges.valueAt(index) : NULL; } @@ -316,14 +601,14 @@ void InputDeviceInfo::addSource(uint32_t source) { mSources |= source; } -void InputDeviceInfo::addMotionRange(int32_t rangeType, float min, float max, +void InputDeviceInfo::addMotionRange(int32_t axis, float min, float max, float flat, float fuzz) { MotionRange range = { min, max, flat, fuzz }; - addMotionRange(rangeType, range); + addMotionRange(axis, range); } -void InputDeviceInfo::addMotionRange(int32_t rangeType, const MotionRange& range) { - mMotionRanges.add(rangeType, range); +void InputDeviceInfo::addMotionRange(int32_t axis, const MotionRange& range) { + mMotionRanges.add(axis, range); } } // namespace android diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk index 580d73cf384c..e23197185c85 100644 --- a/libs/ui/tests/Android.mk +++ b/libs/ui/tests/Android.mk @@ -7,6 +7,7 @@ ifneq ($(TARGET_SIMULATOR),true) # Build the unit tests. test_src_files := \ InputChannel_test.cpp \ + InputEvent_test.cpp \ InputPublisherAndConsumer_test.cpp shared_libraries := \ @@ -18,7 +19,8 @@ shared_libraries := \ libhardware \ libhardware_legacy \ libui \ - libstlport + libstlport \ + libskia static_libraries := \ libgtest \ @@ -28,7 +30,8 @@ c_includes := \ bionic \ bionic/libstdc++/include \ external/gtest/include \ - external/stlport/stlport + external/stlport/stlport \ + external/skia/include/core module_tags := eng tests diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/ui/tests/InputChannel_test.cpp index 6cec1c02ea95..eff22ee5f0eb 100644 --- a/libs/ui/tests/InputChannel_test.cpp +++ b/libs/ui/tests/InputChannel_test.cpp @@ -1,6 +1,18 @@ -// -// Copyright 2010 The Android Open Source Project -// +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <ui/InputTransport.h> #include <utils/Timers.h> diff --git a/libs/ui/tests/InputEvent_test.cpp b/libs/ui/tests/InputEvent_test.cpp new file mode 100644 index 000000000000..7b15c381e63f --- /dev/null +++ b/libs/ui/tests/InputEvent_test.cpp @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ui/Input.h> +#include <gtest/gtest.h> +#include <binder/Parcel.h> + +namespace android { + +class BaseTest : public testing::Test { +protected: + virtual void SetUp() { } + virtual void TearDown() { } +}; + +// --- PointerCoordsTest --- + +class PointerCoordsTest : public BaseTest { +}; + +TEST_F(PointerCoordsTest, ClearSetsBitsToZero) { + PointerCoords coords; + coords.clear(); + + ASSERT_EQ(0U, coords.bits); +} + +TEST_F(PointerCoordsTest, AxisValues) { + float* valuePtr; + PointerCoords coords; + coords.clear(); + + // Check invariants when no axes are present. + ASSERT_EQ(0, coords.getAxisValue(0)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(0, coords.getAxisValue(1)) + << "getAxisValue should return zero because axis is not present"; + + ASSERT_EQ(NULL, coords.editAxisValue(0)) + << "editAxisValue should return null because axis is not present"; + + // Set first axis. + ASSERT_EQ(OK, coords.setAxisValue(1, 5)); + ASSERT_EQ(0x00000002U, coords.bits); + ASSERT_EQ(5, coords.values[0]); + + ASSERT_EQ(0, coords.getAxisValue(0)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(5, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + + // Set an axis with a higher id than all others. (appending value at the end) + ASSERT_EQ(OK, coords.setAxisValue(3, 2)); + ASSERT_EQ(0x0000000aU, coords.bits); + ASSERT_EQ(5, coords.values[0]); + ASSERT_EQ(2, coords.values[1]); + + ASSERT_EQ(0, coords.getAxisValue(0)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(5, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(0, coords.getAxisValue(2)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Set an axis with an id lower than all others. (prepending value at beginning) + ASSERT_EQ(OK, coords.setAxisValue(0, 4)); + ASSERT_EQ(0x0000000bU, coords.bits); + ASSERT_EQ(4, coords.values[0]); + ASSERT_EQ(5, coords.values[1]); + ASSERT_EQ(2, coords.values[2]); + + ASSERT_EQ(4, coords.getAxisValue(0)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(5, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(0, coords.getAxisValue(2)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Edit an existing axis value in place. + valuePtr = coords.editAxisValue(1); + ASSERT_EQ(5, *valuePtr) + << "editAxisValue should return pointer to axis value"; + + *valuePtr = 7; + ASSERT_EQ(7, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + + // Set an axis with an id between the others. (inserting value in the middle) + ASSERT_EQ(OK, coords.setAxisValue(2, 1)); + ASSERT_EQ(0x0000000fU, coords.bits); + ASSERT_EQ(4, coords.values[0]); + ASSERT_EQ(7, coords.values[1]); + ASSERT_EQ(1, coords.values[2]); + ASSERT_EQ(2, coords.values[3]); + + ASSERT_EQ(4, coords.getAxisValue(0)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(7, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(1, coords.getAxisValue(2)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Set an existing axis value in place. + ASSERT_EQ(OK, coords.setAxisValue(1, 6)); + ASSERT_EQ(0x0000000fU, coords.bits); + ASSERT_EQ(4, coords.values[0]); + ASSERT_EQ(6, coords.values[1]); + ASSERT_EQ(1, coords.values[2]); + ASSERT_EQ(2, coords.values[3]); + + ASSERT_EQ(4, coords.getAxisValue(0)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(6, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(1, coords.getAxisValue(2)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Set maximum number of axes. + for (size_t axis = 4; axis < PointerCoords::MAX_AXES; axis++) { + ASSERT_EQ(OK, coords.setAxisValue(axis, axis)); + } + ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcount(coords.bits)); + + // Try to set one more axis beyond maximum number. + // Ensure bits are unchanged. + ASSERT_EQ(NO_MEMORY, coords.setAxisValue(PointerCoords::MAX_AXES, 100)); + ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcount(coords.bits)); +} + +TEST_F(PointerCoordsTest, ReadAndWriteParcel) { + Parcel parcel; + + PointerCoords inCoords; + inCoords.clear(); + PointerCoords outCoords; + + // Round trip with empty coords. + inCoords.writeToParcel(&parcel); + parcel.setDataPosition(0); + outCoords.readFromParcel(&parcel); + + ASSERT_EQ(0U, outCoords.bits); + + // Round trip with some values. + parcel.freeData(); + inCoords.setAxisValue(2, 5); + inCoords.setAxisValue(5, 8); + + inCoords.writeToParcel(&parcel); + parcel.setDataPosition(0); + outCoords.readFromParcel(&parcel); + + ASSERT_EQ(outCoords.bits, inCoords.bits); + ASSERT_EQ(outCoords.values[0], inCoords.values[0]); + ASSERT_EQ(outCoords.values[1], inCoords.values[1]); +} + + +// --- KeyEventTest --- + +class KeyEventTest : public BaseTest { +}; + +TEST_F(KeyEventTest, Properties) { + KeyEvent event; + + // Initialize and get properties. + const nsecs_t ARBITRARY_DOWN_TIME = 1; + const nsecs_t ARBITRARY_EVENT_TIME = 2; + event.initialize(2, AINPUT_SOURCE_GAMEPAD, AKEY_EVENT_ACTION_DOWN, + AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, + AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); + + ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); + ASSERT_EQ(2, event.getDeviceId()); + ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource()); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags()); + ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode()); + ASSERT_EQ(121, event.getScanCode()); + ASSERT_EQ(AMETA_ALT_ON, event.getMetaState()); + ASSERT_EQ(1, event.getRepeatCount()); + ASSERT_EQ(ARBITRARY_DOWN_TIME, event.getDownTime()); + ASSERT_EQ(ARBITRARY_EVENT_TIME, event.getEventTime()); + + // Set source. + event.setSource(AINPUT_SOURCE_JOYSTICK); + ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource()); +} + + +// --- MotionEventTest --- + +class MotionEventTest : public BaseTest { +}; + +TEST_F(MotionEventTest, Properties) { + MotionEvent event; + + // Initialize, add samples and get properties. + const nsecs_t ARBITRARY_DOWN_TIME = 1; + const nsecs_t ARBITRARY_EVENT_TIME = 2; + const float X_OFFSET = 1.0f; + const float Y_OFFSET = 1.1f; + int32_t pointerIds[] = { 1, 2 }; + PointerCoords pointerCoords[2]; + pointerCoords[0].clear(); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18); + pointerCoords[1].clear(); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); + event.initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, + AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, + X_OFFSET, Y_OFFSET, 2.0f, 2.1f, + ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, + 2, pointerIds, pointerCoords); + + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128); + event.addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords); + + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228); + event.addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords); + + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event.getType()); + ASSERT_EQ(2, event.getDeviceId()); + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event.getSource()); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event.getAction()); + ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event.getFlags()); + ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event.getEdgeFlags()); + ASSERT_EQ(AMETA_ALT_ON, event.getMetaState()); + ASSERT_EQ(X_OFFSET, event.getXOffset()); + ASSERT_EQ(Y_OFFSET, event.getYOffset()); + ASSERT_EQ(2.0f, event.getXPrecision()); + ASSERT_EQ(2.1f, event.getYPrecision()); + ASSERT_EQ(ARBITRARY_DOWN_TIME, event.getDownTime()); + + ASSERT_EQ(2U, event.getPointerCount()); + ASSERT_EQ(1, event.getPointerId(0)); + ASSERT_EQ(2, event.getPointerId(1)); + + ASSERT_EQ(2U, event.getHistorySize()); + + // Get data. + ASSERT_EQ(ARBITRARY_EVENT_TIME, event.getHistoricalEventTime(0)); + ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event.getHistoricalEventTime(1)); + ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event.getEventTime()); + + ASSERT_EQ(11, event.getHistoricalRawPointerCoords(0, 0)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(21, event.getHistoricalRawPointerCoords(1, 0)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(111, event.getHistoricalRawPointerCoords(0, 1)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(121, event.getHistoricalRawPointerCoords(1, 1)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(211, event.getRawPointerCoords(0)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(221, event.getRawPointerCoords(1)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + + ASSERT_EQ(11, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0)); + ASSERT_EQ(21, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0)); + ASSERT_EQ(111, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1)); + ASSERT_EQ(121, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1)); + ASSERT_EQ(211, event.getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0)); + ASSERT_EQ(221, event.getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1)); + + ASSERT_EQ(10, event.getHistoricalRawX(0, 0)); + ASSERT_EQ(20, event.getHistoricalRawX(1, 0)); + ASSERT_EQ(110, event.getHistoricalRawX(0, 1)); + ASSERT_EQ(120, event.getHistoricalRawX(1, 1)); + ASSERT_EQ(210, event.getRawX(0)); + ASSERT_EQ(220, event.getRawX(1)); + + ASSERT_EQ(11, event.getHistoricalRawY(0, 0)); + ASSERT_EQ(21, event.getHistoricalRawY(1, 0)); + ASSERT_EQ(111, event.getHistoricalRawY(0, 1)); + ASSERT_EQ(121, event.getHistoricalRawY(1, 1)); + ASSERT_EQ(211, event.getRawY(0)); + ASSERT_EQ(221, event.getRawY(1)); + + ASSERT_EQ(X_OFFSET + 10, event.getHistoricalX(0, 0)); + ASSERT_EQ(X_OFFSET + 20, event.getHistoricalX(1, 0)); + ASSERT_EQ(X_OFFSET + 110, event.getHistoricalX(0, 1)); + ASSERT_EQ(X_OFFSET + 120, event.getHistoricalX(1, 1)); + ASSERT_EQ(X_OFFSET + 210, event.getX(0)); + ASSERT_EQ(X_OFFSET + 220, event.getX(1)); + + ASSERT_EQ(Y_OFFSET + 11, event.getHistoricalY(0, 0)); + ASSERT_EQ(Y_OFFSET + 21, event.getHistoricalY(1, 0)); + ASSERT_EQ(Y_OFFSET + 111, event.getHistoricalY(0, 1)); + ASSERT_EQ(Y_OFFSET + 121, event.getHistoricalY(1, 1)); + ASSERT_EQ(Y_OFFSET + 211, event.getY(0)); + ASSERT_EQ(Y_OFFSET + 221, event.getY(1)); + + ASSERT_EQ(12, event.getHistoricalPressure(0, 0)); + ASSERT_EQ(22, event.getHistoricalPressure(1, 0)); + ASSERT_EQ(112, event.getHistoricalPressure(0, 1)); + ASSERT_EQ(122, event.getHistoricalPressure(1, 1)); + ASSERT_EQ(212, event.getPressure(0)); + ASSERT_EQ(222, event.getPressure(1)); + + ASSERT_EQ(13, event.getHistoricalSize(0, 0)); + ASSERT_EQ(23, event.getHistoricalSize(1, 0)); + ASSERT_EQ(113, event.getHistoricalSize(0, 1)); + ASSERT_EQ(123, event.getHistoricalSize(1, 1)); + ASSERT_EQ(213, event.getSize(0)); + ASSERT_EQ(223, event.getSize(1)); + + ASSERT_EQ(14, event.getHistoricalTouchMajor(0, 0)); + ASSERT_EQ(24, event.getHistoricalTouchMajor(1, 0)); + ASSERT_EQ(114, event.getHistoricalTouchMajor(0, 1)); + ASSERT_EQ(124, event.getHistoricalTouchMajor(1, 1)); + ASSERT_EQ(214, event.getTouchMajor(0)); + ASSERT_EQ(224, event.getTouchMajor(1)); + + ASSERT_EQ(15, event.getHistoricalTouchMinor(0, 0)); + ASSERT_EQ(25, event.getHistoricalTouchMinor(1, 0)); + ASSERT_EQ(115, event.getHistoricalTouchMinor(0, 1)); + ASSERT_EQ(125, event.getHistoricalTouchMinor(1, 1)); + ASSERT_EQ(215, event.getTouchMinor(0)); + ASSERT_EQ(225, event.getTouchMinor(1)); + + ASSERT_EQ(16, event.getHistoricalToolMajor(0, 0)); + ASSERT_EQ(26, event.getHistoricalToolMajor(1, 0)); + ASSERT_EQ(116, event.getHistoricalToolMajor(0, 1)); + ASSERT_EQ(126, event.getHistoricalToolMajor(1, 1)); + ASSERT_EQ(216, event.getToolMajor(0)); + ASSERT_EQ(226, event.getToolMajor(1)); + + ASSERT_EQ(17, event.getHistoricalToolMinor(0, 0)); + ASSERT_EQ(27, event.getHistoricalToolMinor(1, 0)); + ASSERT_EQ(117, event.getHistoricalToolMinor(0, 1)); + ASSERT_EQ(127, event.getHistoricalToolMinor(1, 1)); + ASSERT_EQ(217, event.getToolMinor(0)); + ASSERT_EQ(227, event.getToolMinor(1)); + + ASSERT_EQ(18, event.getHistoricalOrientation(0, 0)); + ASSERT_EQ(28, event.getHistoricalOrientation(1, 0)); + ASSERT_EQ(118, event.getHistoricalOrientation(0, 1)); + ASSERT_EQ(128, event.getHistoricalOrientation(1, 1)); + ASSERT_EQ(218, event.getOrientation(0)); + ASSERT_EQ(228, event.getOrientation(1)); +} + +} // namespace android diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp index 903fcaf20a3e..6e18a4f1abb6 100644 --- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp @@ -1,6 +1,18 @@ -// -// Copyright 2010 The Android Open Source Project -// +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <ui/InputTransport.h> #include <utils/Timers.h> @@ -159,15 +171,17 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( sampleEventTimes.push(i + 10); for (size_t j = 0; j < pointerCount; j++) { samplePointerCoords.push(); - samplePointerCoords.editTop().x = 100 * i + j; - samplePointerCoords.editTop().y = 200 * i + j; - samplePointerCoords.editTop().pressure = 0.5 * i + j; - samplePointerCoords.editTop().size = 0.7 * i + j; - samplePointerCoords.editTop().touchMajor = 1.5 * i + j; - samplePointerCoords.editTop().touchMinor = 1.7 * i + j; - samplePointerCoords.editTop().toolMajor = 2.5 * i + j; - samplePointerCoords.editTop().toolMinor = 2.7 * i + j; - samplePointerCoords.editTop().orientation = 3.5 * i + j; + PointerCoords& pc = samplePointerCoords.editTop(); + pc.clear(); + pc.setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i + j); } } @@ -239,27 +253,27 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( for (size_t i = 0; i < pointerCount; i++) { SCOPED_TRACE(i); size_t offset = sampleIndex * pointerCount + i; - EXPECT_EQ(samplePointerCoords[offset].x, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X), motionEvent->getHistoricalRawX(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].y, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y), motionEvent->getHistoricalRawY(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].x + xOffset, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, motionEvent->getHistoricalX(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].y + yOffset, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, motionEvent->getHistoricalY(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].pressure, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getHistoricalPressure(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].size, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getHistoricalSize(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].touchMajor, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getHistoricalTouchMajor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].touchMinor, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getHistoricalTouchMinor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].toolMajor, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getHistoricalToolMajor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].toolMinor, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getHistoricalToolMinor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].orientation, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), motionEvent->getHistoricalOrientation(i, sampleIndex)); } } @@ -269,17 +283,28 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( for (size_t i = 0; i < pointerCount; i++) { SCOPED_TRACE(i); size_t offset = lastSampleIndex * pointerCount + i; - EXPECT_EQ(samplePointerCoords[offset].x, motionEvent->getRawX(i)); - EXPECT_EQ(samplePointerCoords[offset].y, motionEvent->getRawY(i)); - EXPECT_EQ(samplePointerCoords[offset].x + xOffset, motionEvent->getX(i)); - EXPECT_EQ(samplePointerCoords[offset].y + yOffset, motionEvent->getY(i)); - EXPECT_EQ(samplePointerCoords[offset].pressure, motionEvent->getPressure(i)); - EXPECT_EQ(samplePointerCoords[offset].size, motionEvent->getSize(i)); - EXPECT_EQ(samplePointerCoords[offset].touchMajor, motionEvent->getTouchMajor(i)); - EXPECT_EQ(samplePointerCoords[offset].touchMinor, motionEvent->getTouchMinor(i)); - EXPECT_EQ(samplePointerCoords[offset].toolMajor, motionEvent->getToolMajor(i)); - EXPECT_EQ(samplePointerCoords[offset].toolMinor, motionEvent->getToolMinor(i)); - EXPECT_EQ(samplePointerCoords[offset].orientation, motionEvent->getOrientation(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X), + motionEvent->getRawX(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y), + motionEvent->getRawY(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, + motionEvent->getX(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, + motionEvent->getY(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + motionEvent->getPressure(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + motionEvent->getSize(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + motionEvent->getTouchMajor(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + motionEvent->getTouchMinor(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + motionEvent->getToolMajor(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + motionEvent->getToolMinor(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), + motionEvent->getOrientation(i)); } status = mConsumer->sendFinishedSignal(false); @@ -328,7 +353,8 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenNotReset_ReturnsErr const size_t pointerCount = 1; int32_t pointerIds[pointerCount] = { 0 }; - PointerCoords pointerCoords[pointerCount] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; + PointerCoords pointerCoords[pointerCount]; + pointerCoords[0].clear(); status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp index 2bdc0ce278a4..062e6d76bfb8 100644 --- a/libs/utils/SystemClock.cpp +++ b/libs/utils/SystemClock.cpp @@ -19,7 +19,7 @@ * System clock functions. */ -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS #include <linux/ioctl.h> #include <linux/rtc.h> #include <utils/Atomic.h> @@ -50,7 +50,7 @@ int setCurrentTimeMillis(int64_t millis) return -1; #else struct timeval tv; -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS struct timespec ts; int fd; int res; @@ -66,7 +66,7 @@ int setCurrentTimeMillis(int64_t millis) LOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec); -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS fd = open("/dev/alarm", O_RDWR); if(fd < 0) { LOGW("Unable to open alarm driver: %s\n", strerror(errno)); @@ -106,7 +106,7 @@ int64_t uptimeMillis() */ int64_t elapsedRealtime() { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS static int s_fd = -1; if (s_fd == -1) { diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index b1bd8284430f..35dcbcb64d53 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -739,7 +739,7 @@ int Thread::_threadLoop(void* user) wp<Thread> weak(strong); self->mHoldSelf.clear(); -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS // this is very useful for debugging with gdb self->mTid = gettid(); #endif diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 5a59ef6ac809..cc2ffa074ba3 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -979,7 +979,7 @@ public class AudioManager { * false if otherwise */ public boolean isBluetoothA2dpOn() { - if (AudioSystem.getDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,"") + if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_BLUETOOTH_A2DP,"") == AudioSystem.DEVICE_STATE_UNAVAILABLE) { return false; } else { @@ -1004,9 +1004,9 @@ public class AudioManager { * false if otherwise */ public boolean isWiredHeadsetOn() { - if (AudioSystem.getDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,"") + if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_WIRED_HEADSET,"") == AudioSystem.DEVICE_STATE_UNAVAILABLE && - AudioSystem.getDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,"") + AudioSystem.getDeviceConnectionState(DEVICE_OUT_WIRED_HEADPHONE,"") == AudioSystem.DEVICE_STATE_UNAVAILABLE) { return false; } else { @@ -1679,4 +1679,105 @@ public class AudioManager { return silentMode; } + // This section re-defines new output device constants from AudioSystem, because the AudioSystem + // class is not used by other parts of the framework, which instead use definitions and methods + // from AudioManager. AudioSystem is an internal class used by AudioManager and AudioService. + + /** {@hide} The audio output device code for the small speaker at the front of the device used + * when placing calls. Does not refer to an in-ear headphone without attached microphone, + * such as earbuds, earphones, or in-ear monitors (IEM). Those would be handled as a + * {@link #DEVICE_OUT_WIRED_HEADPHONE}. + */ + public static final int DEVICE_OUT_EARPIECE = AudioSystem.DEVICE_OUT_EARPIECE; + /** {@hide} The audio output device code for the built-in speaker */ + public static final int DEVICE_OUT_SPEAKER = AudioSystem.DEVICE_OUT_SPEAKER; + /** {@hide} The audio output device code for a wired headset with attached microphone */ + public static final int DEVICE_OUT_WIRED_HEADSET = AudioSystem.DEVICE_OUT_WIRED_HEADSET; + /** {@hide} The audio output device code for a wired headphone without attached microphone */ + public static final int DEVICE_OUT_WIRED_HEADPHONE = AudioSystem.DEVICE_OUT_WIRED_HEADPHONE; + /** {@hide} The audio output device code for generic Bluetooth SCO, for voice */ + public static final int DEVICE_OUT_BLUETOOTH_SCO = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO; + /** {@hide} The audio output device code for Bluetooth SCO Headset Profile (HSP) and + * Hands-Free Profile (HFP), for voice + */ + public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET = + AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET; + /** {@hide} The audio output device code for Bluetooth SCO car audio, for voice */ + public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT = + AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT; + /** {@hide} The audio output device code for generic Bluetooth A2DP, for music */ + public static final int DEVICE_OUT_BLUETOOTH_A2DP = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP; + /** {@hide} The audio output device code for Bluetooth A2DP headphones, for music */ + public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + /** {@hide} The audio output device code for Bluetooth A2DP external speaker, for music */ + public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + /** {@hide} The audio output device code for S/PDIF or HDMI */ + public static final int DEVICE_OUT_AUX_DIGITAL = AudioSystem.DEVICE_OUT_AUX_DIGITAL; + /** {@hide} The audio output device code for an analog wired headset attached via a + * docking station + */ + public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET; + /** {@hide} The audio output device code for a digital wired headset attached via a + * docking station + */ + public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET; + /** {@hide} This is not used as a returned value from {@link #getDevicesForStream}, but could be + * used in the future in a set method to select whatever default device is chosen by the + * platform-specific implementation. + */ + public static final int DEVICE_OUT_DEFAULT = AudioSystem.DEVICE_OUT_DEFAULT; + + /** + * Return the enabled devices for the specified output stream type. + * + * @param streamType The stream type to query. One of + * {@link #STREAM_VOICE_CALL}, + * {@link #STREAM_SYSTEM}, + * {@link #STREAM_RING}, + * {@link #STREAM_MUSIC}, + * {@link #STREAM_ALARM}, + * {@link #STREAM_NOTIFICATION}, + * {@link #STREAM_DTMF}. + * + * @return The bit-mask "or" of audio output device codes for all enabled devices on this + * stream. Zero or more of + * {@link #DEVICE_OUT_EARPIECE}, + * {@link #DEVICE_OUT_SPEAKER}, + * {@link #DEVICE_OUT_WIRED_HEADSET}, + * {@link #DEVICE_OUT_WIRED_HEADPHONE}, + * {@link #DEVICE_OUT_BLUETOOTH_SCO}, + * {@link #DEVICE_OUT_BLUETOOTH_SCO_HEADSET}, + * {@link #DEVICE_OUT_BLUETOOTH_SCO_CARKIT}, + * {@link #DEVICE_OUT_BLUETOOTH_A2DP}, + * {@link #DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES}, + * {@link #DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER}, + * {@link #DEVICE_OUT_AUX_DIGITAL}, + * {@link #DEVICE_OUT_ANLG_DOCK_HEADSET}, + * {@link #DEVICE_OUT_DGTL_DOCK_HEADSET}. + * {@link #DEVICE_OUT_DEFAULT} is not used here. + * + * The implementation may support additional device codes beyond those listed, so + * the application should ignore any bits which it does not recognize. + * Note that the information may be imprecise when the implementation + * cannot distinguish whether a particular device is enabled. + * + * {@hide} + */ + public int getDevicesForStream(int streamType) { + switch (streamType) { + case STREAM_VOICE_CALL: + case STREAM_SYSTEM: + case STREAM_RING: + case STREAM_MUSIC: + case STREAM_ALARM: + case STREAM_NOTIFICATION: + case STREAM_DTMF: + return AudioSystem.getDevicesForStream(streamType); + default: + return 0; + } + } + } diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 6c85490bb1c5..1fe3ccc2c83c 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -1945,10 +1945,11 @@ public class AudioService extends IAudioService.Stub { break; case MSG_MEDIA_SERVER_DIED: - // Force creation of new IAudioflinger interface if (!mMediaServerOk) { Log.e(TAG, "Media server died."); - AudioSystem.isMicrophoneMuted(); + // Force creation of new IAudioFlinger interface so that we are notified + // when new media_server process is back to life. + AudioSystem.setErrorCallback(mAudioSystemCallback); sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0, null, 500); } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index e20bb2510d9f..2492d4771cac 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -18,7 +18,8 @@ package android.media; /* IF YOU CHANGE ANY OF THE CONSTANTS IN THIS FILE, DO NOT FORGET - * TO UPDATE THE CORRESPONDING NATIVE GLUE. THANK YOU FOR YOUR COOPERATION + * TO UPDATE THE CORRESPONDING NATIVE GLUE AND AudioManager.java. + * THANK YOU FOR YOUR COOPERATION. */ /** @@ -29,7 +30,7 @@ public class AudioSystem /* FIXME: Need to finalize this and correlate with native layer */ /* * If these are modified, please also update Settings.System.VOLUME_SETTINGS - * and attrs.xml + * and attrs.xml and AudioManager.java. */ /* The audio stream for phone calls */ public static final int STREAM_VOICE_CALL = 0; @@ -218,13 +219,26 @@ public class AudioSystem */ public static void setErrorCallback(ErrorCallback cb) { - mErrorCallback = cb; + synchronized (AudioSystem.class) { + mErrorCallback = cb; + } + // Calling a method on AudioFlinger here makes sure that we bind to IAudioFlinger + // binder interface death. Not doing that would result in not being notified of + // media_server process death if no other method is called on AudioSystem that reaches + // to AudioFlinger. + isMicrophoneMuted(); } private static void errorCallbackFromNative(int error) { - if (mErrorCallback != null) { - mErrorCallback.onError(error); + ErrorCallback errorCallback = null; + synchronized (AudioSystem.class) { + if (mErrorCallback != null) { + errorCallback = mErrorCallback; + } + } + if (errorCallback != null) { + errorCallback.onError(error); } } @@ -232,7 +246,7 @@ public class AudioSystem * AudioPolicyService methods */ - // output devices + // output devices, be sure to update AudioManager.java also public static final int DEVICE_OUT_EARPIECE = 0x1; public static final int DEVICE_OUT_SPEAKER = 0x2; public static final int DEVICE_OUT_WIRED_HEADSET = 0x4; @@ -295,4 +309,5 @@ public class AudioSystem public static native int initStreamVolume(int stream, int indexMin, int indexMax); public static native int setStreamVolumeIndex(int stream, int index); public static native int getStreamVolumeIndex(int stream); + public static native int getDevicesForStream(int stream); } diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 6a3ff7c0a18a..ee2c1e8cb2d0 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -46,8 +46,9 @@ public class MediaFile { public static final int FILE_TYPE_OGG = 7; public static final int FILE_TYPE_AAC = 8; public static final int FILE_TYPE_MKA = 9; + public static final int FILE_TYPE_FLAC = 10; private static final int FIRST_AUDIO_FILE_TYPE = FILE_TYPE_MP3; - private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_MKA; + private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_FLAC; // MIDI file types public static final int FILE_TYPE_MID = 11; @@ -99,8 +100,7 @@ public class MediaFile { public static final int FILE_TYPE_MS_WORD = 104; public static final int FILE_TYPE_MS_EXCEL = 105; public static final int FILE_TYPE_MS_POWERPOINT = 106; - public static final int FILE_TYPE_FLAC = 107; - public static final int FILE_TYPE_ZIP = 108; + public static final int FILE_TYPE_ZIP = 107; static class MediaFileType { diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 33c63856ce13..b2dc1e3f5122 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -276,7 +276,31 @@ public class MediaScanner "Drum Solo", "A capella", "Euro-House", - "Dance Hall" + "Dance Hall", + // The following ones seem to be fairly widely supported as well + "Goa", + "Drum & Bass", + "Club-House", + "Hardcore", + "Terror", + "Indie", + "Britpop", + "Negerpunk", + "Polsk Punk", + "Beat", + "Christian Gangsta", + "Heavy Metal", + "Black Metal", + "Crossover", + "Contemporary Christian", + "Christian Rock", + "Merengue", + "Salsa", + "Thrash Metal", + "Anime", + "JPop", + "Synthpop", + // 148 and up don't seem to have been defined yet. }; private int mNativeContext; @@ -588,23 +612,7 @@ public class MediaScanner } else if (name.equalsIgnoreCase("composer") || name.startsWith("composer;")) { mComposer = value.trim(); } else if (name.equalsIgnoreCase("genre") || name.startsWith("genre;")) { - // handle numeric genres, which PV sometimes encodes like "(20)" - if (value.length() > 0) { - int genreCode = -1; - char ch = value.charAt(0); - if (ch == '(') { - genreCode = parseSubstring(value, 1, -1); - } else if (ch >= '0' && ch <= '9') { - genreCode = parseSubstring(value, 0, -1); - } - if (genreCode >= 0 && genreCode < ID3_GENRES.length) { - value = ID3_GENRES[genreCode]; - } else if (genreCode == 255) { - // 255 is defined to be unknown - value = null; - } - } - mGenre = value; + mGenre = getGenreName(value); } else if (name.equalsIgnoreCase("year") || name.startsWith("year;")) { mYear = parseSubstring(value, 0, 0); } else if (name.equalsIgnoreCase("tracknumber") || name.startsWith("tracknumber;")) { @@ -627,6 +635,49 @@ public class MediaScanner } } + public String getGenreName(String genreTagValue) { + + if (genreTagValue == null) { + return null; + } + final int length = genreTagValue.length(); + + if (length > 0 && genreTagValue.charAt(0) == '(') { + StringBuffer number = new StringBuffer(); + int i = 1; + for (; i < length - 1; ++i) { + char c = genreTagValue.charAt(i); + if (Character.isDigit(c)) { + number.append(c); + } else { + break; + } + } + if (genreTagValue.charAt(i) == ')') { + try { + short genreIndex = Short.parseShort(number.toString()); + if (genreIndex >= 0) { + if (genreIndex < ID3_GENRES.length) { + return ID3_GENRES[genreIndex]; + } else if (genreIndex == 0xFF) { + return null; + } else if (genreIndex < 0xFF && (i + 1) < length) { + // genre is valid but unknown, + // if there is a string after the value we take it + return genreTagValue.substring(i + 1); + } else { + // else return the number, without parentheses + return number.toString(); + } + } + } catch (NumberFormatException e) { + } + } + } + + return genreTagValue; + } + public void setMimeType(String mimeType) { if ("audio/mp4".equals(mMimeType) && mimeType.startsWith("video")) { diff --git a/media/java/android/media/videoeditor/EffectKenBurns.java b/media/java/android/media/videoeditor/EffectKenBurns.java index 9ef458b09045..64be6b8e488e 100755 --- a/media/java/android/media/videoeditor/EffectKenBurns.java +++ b/media/java/android/media/videoeditor/EffectKenBurns.java @@ -53,6 +53,13 @@ public class EffectKenBurns extends Effect { Rect endRect, long startTimeMs, long durationMs) { super(mediaItem, effectId, startTimeMs, durationMs); + if ( (startRect.width() <= 0) || (startRect.height() <= 0) ) { + throw new IllegalArgumentException("Invalid Start rectangle"); + } + if ( (endRect.width() <= 0) || (endRect.height() <= 0) ) { + throw new IllegalArgumentException("Invalid End rectangle"); + } + mStartRect = startRect; mEndRect = endRect; } diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java index 8214e7fffeb4..ad2bf954543e 100644 --- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java +++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java @@ -786,92 +786,92 @@ class MediaArtistNativeHelper { /** Defines video profiles and levels. */ public final class VideoProfile { - /** MPEG4, Simple Profile, Level 0. */ - public static final int MPEG4_SP_LEVEL_0 = 0; - - /** MPEG4, Simple Profile, Level 0B. */ - public static final int MPEG4_SP_LEVEL_0B = 1; - - /** MPEG4, Simple Profile, Level 1. */ - public static final int MPEG4_SP_LEVEL_1 = 2; - - /** MPEG4, Simple Profile, Level 2. */ - public static final int MPEG4_SP_LEVEL_2 = 3; - - /** MPEG4, Simple Profile, Level 3. */ - public static final int MPEG4_SP_LEVEL_3 = 4; - /** H263, Profile 0, Level 10. */ - public static final int H263_PROFILE_0_LEVEL_10 = 5; + public static final int H263_PROFILE_0_LEVEL_10 = MediaProperties.H263_PROFILE_0_LEVEL_10; /** H263, Profile 0, Level 20. */ - public static final int H263_PROFILE_0_LEVEL_20 = 6; + public static final int H263_PROFILE_0_LEVEL_20 = MediaProperties.H263_PROFILE_0_LEVEL_20; /** H263, Profile 0, Level 30. */ - public static final int H263_PROFILE_0_LEVEL_30 = 7; + public static final int H263_PROFILE_0_LEVEL_30 = MediaProperties.H263_PROFILE_0_LEVEL_30; /** H263, Profile 0, Level 40. */ - public static final int H263_PROFILE_0_LEVEL_40 = 8; + public static final int H263_PROFILE_0_LEVEL_40 = MediaProperties.H263_PROFILE_0_LEVEL_40; /** H263, Profile 0, Level 45. */ - public static final int H263_PROFILE_0_LEVEL_45 = 9; + public static final int H263_PROFILE_0_LEVEL_45 = MediaProperties.H263_PROFILE_0_LEVEL_45; + + /** MPEG4, Simple Profile, Level 0. */ + public static final int MPEG4_SP_LEVEL_0 = MediaProperties.MPEG4_SP_LEVEL_0; + + /** MPEG4, Simple Profile, Level 0B. */ + public static final int MPEG4_SP_LEVEL_0B = MediaProperties.MPEG4_SP_LEVEL_0B; + + /** MPEG4, Simple Profile, Level 1. */ + public static final int MPEG4_SP_LEVEL_1 = MediaProperties.MPEG4_SP_LEVEL_1; + + /** MPEG4, Simple Profile, Level 2. */ + public static final int MPEG4_SP_LEVEL_2 = MediaProperties.MPEG4_SP_LEVEL_2; + + /** MPEG4, Simple Profile, Level 3. */ + public static final int MPEG4_SP_LEVEL_3 = MediaProperties.MPEG4_SP_LEVEL_3; /** MPEG4, Simple Profile, Level 4A. */ - public static final int MPEG4_SP_LEVEL_4A = 10; + public static final int MPEG4_SP_LEVEL_4A = MediaProperties.MPEG4_SP_LEVEL_4A; /** MPEG4, Simple Profile, Level 0. */ - public static final int MPEG4_SP_LEVEL_5 = 11; + public static final int MPEG4_SP_LEVEL_5 = MediaProperties.MPEG4_SP_LEVEL_5; /** H264, Profile 0, Level 1. */ - public static final int H264_PROFILE_0_LEVEL_1 = 12; + public static final int H264_PROFILE_0_LEVEL_1 = MediaProperties.H264_PROFILE_0_LEVEL_1; /** H264, Profile 0, Level 1b. */ - public static final int H264_PROFILE_0_LEVEL_1b = 13; + public static final int H264_PROFILE_0_LEVEL_1b = MediaProperties.H264_PROFILE_0_LEVEL_1B; /** H264, Profile 0, Level 1.1 */ - public static final int H264_PROFILE_0_LEVEL_1_1 = 14; + public static final int H264_PROFILE_0_LEVEL_1_1 = MediaProperties.H264_PROFILE_0_LEVEL_1_1; /** H264, Profile 0, Level 1.2 */ - public static final int H264_PROFILE_0_LEVEL_1_2 = 15; + public static final int H264_PROFILE_0_LEVEL_1_2 = MediaProperties.H264_PROFILE_0_LEVEL_1_2; /** H264, Profile 0, Level 1.3 */ - public static final int H264_PROFILE_0_LEVEL_1_3 = 16; + public static final int H264_PROFILE_0_LEVEL_1_3 = MediaProperties.H264_PROFILE_0_LEVEL_1_3; /** H264, Profile 0, Level 2. */ - public static final int H264_PROFILE_0_LEVEL_2 = 17; + public static final int H264_PROFILE_0_LEVEL_2 = MediaProperties.H264_PROFILE_0_LEVEL_2; /** H264, Profile 0, Level 2.1 */ - public static final int H264_PROFILE_0_LEVEL_2_1 = 18; + public static final int H264_PROFILE_0_LEVEL_2_1 = MediaProperties.H264_PROFILE_0_LEVEL_2_1; /** H264, Profile 0, Level 2.2 */ - public static final int H264_PROFILE_0_LEVEL_2_2 = 19; + public static final int H264_PROFILE_0_LEVEL_2_2 = MediaProperties.H264_PROFILE_0_LEVEL_2_2; /** H264, Profile 0, Level 3. */ - public static final int H264_PROFILE_0_LEVEL_3 = 20; + public static final int H264_PROFILE_0_LEVEL_3 = MediaProperties.H264_PROFILE_0_LEVEL_3; /** H264, Profile 0, Level 3.1 */ - public static final int H264_PROFILE_0_LEVEL_3_1 = 21; + public static final int H264_PROFILE_0_LEVEL_3_1 = MediaProperties.H264_PROFILE_0_LEVEL_3_1; /** H264, Profile 0, Level 3.2 */ - public static final int H264_PROFILE_0_LEVEL_3_2 = 22; + public static final int H264_PROFILE_0_LEVEL_3_2 = MediaProperties.H264_PROFILE_0_LEVEL_3_2; /** H264, Profile 0, Level 4. */ - public static final int H264_PROFILE_0_LEVEL_4 = 23; + public static final int H264_PROFILE_0_LEVEL_4 = MediaProperties.H264_PROFILE_0_LEVEL_4; /** H264, Profile 0, Level 4.1 */ - public static final int H264_PROFILE_0_LEVEL_4_1 = 24; + public static final int H264_PROFILE_0_LEVEL_4_1 = MediaProperties.H264_PROFILE_0_LEVEL_4_1; /** H264, Profile 0, Level 4.2 */ - public static final int H264_PROFILE_0_LEVEL_4_2 = 25; + public static final int H264_PROFILE_0_LEVEL_4_2 = MediaProperties.H264_PROFILE_0_LEVEL_4_2; /** H264, Profile 0, Level 5. */ - public static final int H264_PROFILE_0_LEVEL_5 = 26; + public static final int H264_PROFILE_0_LEVEL_5 = MediaProperties.H264_PROFILE_0_LEVEL_5; /** H264, Profile 0, Level 5.1 */ - public static final int H264_PROFILE_0_LEVEL_5_1 = 27; + public static final int H264_PROFILE_0_LEVEL_5_1 = MediaProperties.H264_PROFILE_0_LEVEL_5_1; /** Profile out of range. */ - public static final int OUT_OF_RANGE = 255; + public static final int OUT_OF_RANGE = MediaProperties.UNSUPPORTED_PROFILE_LEVEL; } /** Defines video frame sizes. */ @@ -3065,8 +3065,7 @@ class MediaArtistNativeHelper { * This function is responsible for stopping the preview */ long stopPreview() { - nativeStopPreview(); - return mPreviewProgress; + return nativeStopPreview(); } /** @@ -3999,7 +3998,7 @@ class MediaArtistNativeHelper { int framewidth, int frameheight, int surfacewidth, int surfaceheight, long timeMs) throws IllegalArgumentException, IllegalStateException, RuntimeException; - private native void nativeStopPreview(); + private native int nativeStopPreview(); private native int nativeGenerateAudioGraph(String pcmFilePath, String outGraphPath, int frameDuration, int channels, int sampleCount); diff --git a/media/java/android/media/videoeditor/MediaProperties.java b/media/java/android/media/videoeditor/MediaProperties.java index 34186e98de02..0b7ec081cca3 100755 --- a/media/java/android/media/videoeditor/MediaProperties.java +++ b/media/java/android/media/videoeditor/MediaProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -146,6 +146,75 @@ public class MediaProperties { VCODEC_MPEG4, }; + /* H.263 Profiles and levels */ + public static final int H263_PROFILE_0_LEVEL_10 = 0; + public static final int H263_PROFILE_0_LEVEL_20 = 1; + public static final int H263_PROFILE_0_LEVEL_30 = 2; + public static final int H263_PROFILE_0_LEVEL_40 = 3; + public static final int H263_PROFILE_0_LEVEL_45 = 4; + /* MPEG-4 Profiles and levels */ + public static final int MPEG4_SP_LEVEL_0 = 50; + public static final int MPEG4_SP_LEVEL_0B = 51; + public static final int MPEG4_SP_LEVEL_1 = 52; + public static final int MPEG4_SP_LEVEL_2 = 53; + public static final int MPEG4_SP_LEVEL_3 = 54; + public static final int MPEG4_SP_LEVEL_4A = 55; + public static final int MPEG4_SP_LEVEL_5 = 56; + /* AVC Profiles and levels */ + public static final int H264_PROFILE_0_LEVEL_1 = 150; + public static final int H264_PROFILE_0_LEVEL_1B = 151; + public static final int H264_PROFILE_0_LEVEL_1_1 = 152; + public static final int H264_PROFILE_0_LEVEL_1_2 = 153; + public static final int H264_PROFILE_0_LEVEL_1_3 = 154; + public static final int H264_PROFILE_0_LEVEL_2 = 155; + public static final int H264_PROFILE_0_LEVEL_2_1 = 156; + public static final int H264_PROFILE_0_LEVEL_2_2 = 157; + public static final int H264_PROFILE_0_LEVEL_3 = 158; + public static final int H264_PROFILE_0_LEVEL_3_1 = 159; + public static final int H264_PROFILE_0_LEVEL_3_2 = 160; + public static final int H264_PROFILE_0_LEVEL_4 = 161; + public static final int H264_PROFILE_0_LEVEL_4_1 = 162; + public static final int H264_PROFILE_0_LEVEL_4_2 = 163; + public static final int H264_PROFILE_0_LEVEL_5 = 164; + public static final int H264_PROFILE_0_LEVEL_5_1 = 165; + /* Unsupported profile and level */ + public static final int UNSUPPORTED_PROFILE_LEVEL = 255; + + /** + * The array of supported video codec Profile and Levels + */ + private static final int[] SUPPORTED_VCODEC_PROFILE_LEVELS = new int[] { + H263_PROFILE_0_LEVEL_10, + H263_PROFILE_0_LEVEL_20, + H263_PROFILE_0_LEVEL_30, + H263_PROFILE_0_LEVEL_40, + H263_PROFILE_0_LEVEL_45, + MPEG4_SP_LEVEL_0, + MPEG4_SP_LEVEL_0B, + MPEG4_SP_LEVEL_1, + MPEG4_SP_LEVEL_2, + MPEG4_SP_LEVEL_3, + MPEG4_SP_LEVEL_4A, + MPEG4_SP_LEVEL_5, + H264_PROFILE_0_LEVEL_1, + H264_PROFILE_0_LEVEL_1B, + H264_PROFILE_0_LEVEL_1_1, + H264_PROFILE_0_LEVEL_1_2, + H264_PROFILE_0_LEVEL_1_3, + H264_PROFILE_0_LEVEL_2, + H264_PROFILE_0_LEVEL_2_1, + H264_PROFILE_0_LEVEL_2_2, + H264_PROFILE_0_LEVEL_3, + H264_PROFILE_0_LEVEL_3_1, + H264_PROFILE_0_LEVEL_3_2, + H264_PROFILE_0_LEVEL_4, + H264_PROFILE_0_LEVEL_4_1, + H264_PROFILE_0_LEVEL_4_2, + H264_PROFILE_0_LEVEL_5, + H264_PROFILE_0_LEVEL_5_1, + UNSUPPORTED_PROFILE_LEVEL + }; + /** * Audio codec types */ @@ -161,7 +230,7 @@ public class MediaProperties { public static final int ACODEC_OGG = 9; /** - * The array of supported video codecs + * The array of supported audio codecs */ private static final int[] SUPPORTED_ACODECS = new int[] { ACODEC_AAC_LC, diff --git a/media/java/android/media/videoeditor/MediaVideoItem.java b/media/java/android/media/videoeditor/MediaVideoItem.java index d35058496b8b..c91d796c23cf 100755 --- a/media/java/android/media/videoeditor/MediaVideoItem.java +++ b/media/java/android/media/videoeditor/MediaVideoItem.java @@ -150,7 +150,7 @@ public class MediaVideoItem extends MediaItem { properties.height); mFileType = mMANativeHelper.getFileType(properties.fileType); mVideoType = mMANativeHelper.getVideoCodecType(properties.videoFormat); - mVideoProfile = 0; + mVideoProfile = properties.profileAndLevel; mDurationMs = properties.videoDuration; mVideoBitrate = properties.videoBitrate; mAudioBitrate = properties.audioBitrate; diff --git a/media/java/android/mtp/MtpClient.java b/media/java/android/mtp/MtpClient.java new file mode 100644 index 000000000000..568ac9400028 --- /dev/null +++ b/media/java/android/mtp/MtpClient.java @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.mtp; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.UsbConstants; +import android.hardware.UsbDevice; +import android.hardware.UsbInterface; +import android.hardware.UsbManager; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * This class helps an application manage a list of connected MTP or PTP devices. + * It listens for MTP devices being attached and removed from the USB host bus + * and notifies the application when the MTP device list changes. + */ +public class MtpClient { + + private static final String TAG = "MtpClient"; + + private final Context mContext; + private final UsbManager mUsbManager; + private final ArrayList<Listener> mListeners = new ArrayList<Listener>(); + private final ArrayList<MtpDevice> mDeviceList = new ArrayList<MtpDevice>(); + + private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String deviceName = intent.getStringExtra(UsbManager.EXTRA_DEVICE_NAME); + + synchronized (mDeviceList) { + MtpDevice mtpDevice = getDeviceLocked(deviceName); + + if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { + if (mtpDevice == null) { + UsbDevice usbDevice = + (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + mtpDevice = openDevice(usbDevice); + } + if (mtpDevice != null) { + mDeviceList.add(mtpDevice); + for (Listener listener : mListeners) { + listener.deviceAdded(mtpDevice); + } + } + } else if (mtpDevice != null) { + mDeviceList.remove(mtpDevice); + for (Listener listener : mListeners) { + listener.deviceRemoved(mtpDevice); + } + } + } + } + }; + + /** + * An interface for being notified when MTP or PTP devices are attached + * or removed. In the current implementation, only PTP devices are supported. + */ + public interface Listener { + /** + * Called when a new device has been added + * + * @param device the new device that was added + */ + public void deviceAdded(MtpDevice device); + + /** + * Called when a new device has been removed + * + * @param device the device that was removed + */ + public void deviceRemoved(MtpDevice device); + } + + /** + * Tests to see if a {@link android.hardware.UsbDevice} + * supports the PTP protocol (typically used by digital cameras) + * + * @param device the device to test + * @return true if the device is a PTP device. + */ + static public boolean isCamera(UsbDevice device) { + int count = device.getInterfaceCount(); + for (int i = 0; i < count; i++) { + UsbInterface intf = device.getInterface(i); + if (intf.getInterfaceClass() == UsbConstants.USB_CLASS_STILL_IMAGE && + intf.getInterfaceSubclass() == 1 && + intf.getInterfaceProtocol() == 1) { + return true; + } + } + return false; + } + + /** + * MtpClient constructor + * + * @param context the {@link android.content.Context} to use for the MtpClient + */ + public MtpClient(Context context) { + mContext = context; + mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE); + + IntentFilter filter = new IntentFilter(); + filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); + filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + context.registerReceiver(mUsbReceiver, filter); + + for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) { + MtpDevice mtpDevice = getDeviceLocked(usbDevice.getDeviceName()); + if (mtpDevice == null) { + mtpDevice = openDevice(usbDevice); + } + if (mtpDevice != null) { + mDeviceList.add(mtpDevice); + } + } + } + + /** + * Opens the {@link android.hardware.UsbDevice} for an MTP or PTP + * device and return an {@link android.mtp.MtpDevice} for it. + * + * @param device the device to open + * @return an MtpDevice for the device. + */ + private MtpDevice openDevice(UsbDevice usbDevice) { + if (isCamera(usbDevice)) { + MtpDevice mtpDevice = new MtpDevice(usbDevice); + if (mtpDevice.open(mUsbManager)) { + return mtpDevice; + } + } + return null; + } + + /** + * Closes all resources related to the MtpClient object + */ + public void close() { + mContext.unregisterReceiver(mUsbReceiver); + } + + @Override + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + /** + * Registers a {@link android.mtp.MtpClient.Listener} interface to receive + * notifications when MTP or PTP devices are added or removed. + * + * @param listener the listener to register + */ + public void addListener(Listener listener) { + synchronized (mDeviceList) { + if (!mListeners.contains(listener)) { + mListeners.add(listener); + } + } + } + + /** + * Unregisters a {@link android.mtp.MtpClient.Listener} interface. + * + * @param listener the listener to unregister + */ + public void removeListener(Listener listener) { + synchronized (mDeviceList) { + mListeners.remove(listener); + } + } + + /** + * Retrieves an {@link android.mtp.MtpDevice} object for the USB device + * with the given name. + * + * @param deviceName the name of the USB device + * @return the MtpDevice, or null if it does not exist + */ + public MtpDevice getDevice(String deviceName) { + synchronized (mDeviceList) { + return getDeviceLocked(deviceName); + } + } + + /** + * Retrieves an {@link android.mtp.MtpDevice} object for the USB device + * with the given ID. + * + * @param id the ID of the USB device + * @return the MtpDevice, or null if it does not exist + */ + public MtpDevice getDevice(int id) { + synchronized (mDeviceList) { + return getDeviceLocked(UsbDevice.getDeviceName(id)); + } + } + + private MtpDevice getDeviceLocked(String deviceName) { + for (MtpDevice device : mDeviceList) { + if (device.getDeviceName().equals(deviceName)) { + return device; + } + } + return null; + } + + /** + * Retrieves a list of all currently connected {@link android.mtp.MtpDevice}. + * + * @return the list of MtpDevices + */ + public List<MtpDevice> getDeviceList() { + synchronized (mDeviceList) { + return new ArrayList<MtpDevice>(mDeviceList); + } + } + + /** + * Retrieves a list of all {@link android.mtp.MtpStorageInfo} + * for the MTP or PTP device with the given USB device name + * + * @param deviceName the name of the USB device + * @return the list of MtpStorageInfo + */ + public List<MtpStorageInfo> getStorageList(String deviceName) { + MtpDevice device = getDevice(deviceName); + if (device == null) { + return null; + } + int[] storageIds = device.getStorageIds(); + if (storageIds == null) { + return null; + } + + int length = storageIds.length; + ArrayList<MtpStorageInfo> storageList = new ArrayList<MtpStorageInfo>(length); + for (int i = 0; i < length; i++) { + MtpStorageInfo info = device.getStorageInfo(storageIds[i]); + if (info == null) { + Log.w(TAG, "getStorageInfo failed"); + } else { + storageList.add(info); + } + } + return storageList; + } + + /** + * Retrieves the {@link android.mtp.MtpObjectInfo} for an object on + * the MTP or PTP device with the given USB device name with the given + * object handle + * + * @param deviceName the name of the USB device + * @param objectHandle handle of the object to query + * @return the MtpObjectInfo + */ + public MtpObjectInfo getObjectInfo(String deviceName, int objectHandle) { + MtpDevice device = getDevice(deviceName); + if (device == null) { + return null; + } + return device.getObjectInfo(objectHandle); + } + + /** + * Deletes an object on the MTP or PTP device with the given USB device name. + * + * @param deviceName the name of the USB device + * @param objectHandle handle of the object to delete + * @return true if the deletion succeeds + */ + public boolean deleteObject(String deviceName, int objectHandle) { + MtpDevice device = getDevice(deviceName); + if (device == null) { + return false; + } + return device.deleteObject(objectHandle); + } + + /** + * Retrieves a list of {@link android.mtp.MtpObjectInfo} for all objects + * on the MTP or PTP device with the given USB device name and given storage ID + * and/or object handle. + * If the object handle is zero, then all objects in the root of the storage unit + * will be returned. Otherwise, all immediate children of the object will be returned. + * If the storage ID is also zero, then all objects on all storage units will be returned. + * + * @param deviceName the name of the USB device + * @param storageId the ID of the storage unit to query, or zero for all + * @param objectHandle the handle of the parent object to query, or zero for the storage root + * @return the list of MtpObjectInfo + */ + public List<MtpObjectInfo> getObjectList(String deviceName, int storageId, int objectHandle) { + MtpDevice device = getDevice(deviceName); + if (device == null) { + return null; + } + if (objectHandle == 0) { + // all objects in root of storage + objectHandle = 0xFFFFFFFF; + } + int[] handles = device.getObjectHandles(storageId, 0, objectHandle); + if (handles == null) { + return null; + } + + int length = handles.length; + ArrayList<MtpObjectInfo> objectList = new ArrayList<MtpObjectInfo>(length); + for (int i = 0; i < length; i++) { + MtpObjectInfo info = device.getObjectInfo(handles[i]); + if (info == null) { + Log.w(TAG, "getObjectInfo failed"); + } else { + objectList.add(info); + } + } + return objectList; + } + + /** + * Returns the data for an object as a byte array. + * + * @param deviceName the name of the USB device containing the object + * @param objectHandle handle of the object to read + * @param objectSize the size of the object (this should match + * {@link android.mtp.MtpObjectInfo#getCompressedSize} + * @return the object's data, or null if reading fails + */ + public byte[] getObject(String deviceName, int objectHandle, int objectSize) { + MtpDevice device = getDevice(deviceName); + if (device == null) { + return null; + } + return device.getObject(objectHandle, objectSize); + } + + /** + * Returns the thumbnail data for an object as a byte array. + * + * @param deviceName the name of the USB device containing the object + * @param objectHandle handle of the object to read + * @return the object's thumbnail, or null if reading fails + */ + public byte[] getThumbnail(String deviceName, int objectHandle) { + MtpDevice device = getDevice(deviceName); + if (device == null) { + return null; + } + return device.getThumbnail(objectHandle); + } + + /** + * Copies the data for an object to a file in external storage. + * + * @param deviceName the name of the USB device containing the object + * @param objectHandle handle of the object to read + * @param destPath path to destination for the file transfer. + * This path should be in the external storage as defined by + * {@link android.os.Environment#getExternalStorageDirectory} + * @return true if the file transfer succeeds + */ + public boolean importFile(String deviceName, int objectHandle, String destPath) { + MtpDevice device = getDevice(deviceName); + if (device == null) { + return false; + } + return device.importFile(objectHandle, destPath); + } +} diff --git a/media/java/android/mtp/MtpConstants.java b/media/java/android/mtp/MtpConstants.java index 8fa47ee2db43..ad67bb916268 100644 --- a/media/java/android/mtp/MtpConstants.java +++ b/media/java/android/mtp/MtpConstants.java @@ -17,151 +17,265 @@ package android.mtp; /** - * {@hide} + * A class containing constants in the MTP and PTP specifications. */ public final class MtpConstants { -// MTP Data Types + // MTP Data Types + /** @hide */ public static final int TYPE_UNDEFINED = 0x0000; + /** @hide */ public static final int TYPE_INT8 = 0x0001; + /** @hide */ public static final int TYPE_UINT8 = 0x0002; + /** @hide */ public static final int TYPE_INT16 = 0x0003; + /** @hide */ public static final int TYPE_UINT16 = 0x0004; + /** @hide */ public static final int TYPE_INT32 = 0x0005; + /** @hide */ public static final int TYPE_UINT32 = 0x0006; + /** @hide */ public static final int TYPE_INT64 = 0x0007; + /** @hide */ public static final int TYPE_UINT64 = 0x0008; + /** @hide */ public static final int TYPE_INT128 = 0x0009; + /** @hide */ public static final int TYPE_UINT128 = 0x000A; + /** @hide */ public static final int TYPE_AINT8 = 0x4001; + /** @hide */ public static final int TYPE_AUINT8 = 0x4002; + /** @hide */ public static final int TYPE_AINT16 = 0x4003; + /** @hide */ public static final int TYPE_AUINT16 = 0x4004; + /** @hide */ public static final int TYPE_AINT32 = 0x4005; + /** @hide */ public static final int TYPE_AUINT32 = 0x4006; + /** @hide */ public static final int TYPE_AINT64 = 0x4007; + /** @hide */ public static final int TYPE_AUINT64 = 0x4008; + /** @hide */ public static final int TYPE_AINT128 = 0x4009; + /** @hide */ public static final int TYPE_AUINT128 = 0x400A; + /** @hide */ public static final int TYPE_STR = 0xFFFF; -// MTP Response Codes + // MTP Response Codes + /** @hide */ public static final int RESPONSE_UNDEFINED = 0x2000; + /** @hide */ public static final int RESPONSE_OK = 0x2001; + /** @hide */ public static final int RESPONSE_GENERAL_ERROR = 0x2002; + /** @hide */ public static final int RESPONSE_SESSION_NOT_OPEN = 0x2003; + /** @hide */ public static final int RESPONSE_INVALID_TRANSACTION_ID = 0x2004; + /** @hide */ public static final int RESPONSE_OPERATION_NOT_SUPPORTED = 0x2005; + /** @hide */ public static final int RESPONSE_PARAMETER_NOT_SUPPORTED = 0x2006; + /** @hide */ public static final int RESPONSE_INCOMPLETE_TRANSFER = 0x2007; + /** @hide */ public static final int RESPONSE_INVALID_STORAGE_ID = 0x2008; + /** @hide */ public static final int RESPONSE_INVALID_OBJECT_HANDLE = 0x2009; + /** @hide */ public static final int RESPONSE_DEVICE_PROP_NOT_SUPPORTED = 0x200A; + /** @hide */ public static final int RESPONSE_INVALID_OBJECT_FORMAT_CODE = 0x200B; + /** @hide */ public static final int RESPONSE_STORAGE_FULL = 0x200C; + /** @hide */ public static final int RESPONSE_OBJECT_WRITE_PROTECTED = 0x200D; + /** @hide */ public static final int RESPONSE_STORE_READ_ONLY = 0x200E; + /** @hide */ public static final int RESPONSE_ACCESS_DENIED = 0x200F; + /** @hide */ public static final int RESPONSE_NO_THUMBNAIL_PRESENT = 0x2010; + /** @hide */ public static final int RESPONSE_SELF_TEST_FAILED = 0x2011; + /** @hide */ public static final int RESPONSE_PARTIAL_DELETION = 0x2012; + /** @hide */ public static final int RESPONSE_STORE_NOT_AVAILABLE = 0x2013; + /** @hide */ public static final int RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED = 0x2014; + /** @hide */ public static final int RESPONSE_NO_VALID_OBJECT_INFO = 0x2015; + /** @hide */ public static final int RESPONSE_INVALID_CODE_FORMAT = 0x2016; + /** @hide */ public static final int RESPONSE_UNKNOWN_VENDOR_CODE = 0x2017; + /** @hide */ public static final int RESPONSE_CAPTURE_ALREADY_TERMINATED = 0x2018; + /** @hide */ public static final int RESPONSE_DEVICE_BUSY = 0x2019; + /** @hide */ public static final int RESPONSE_INVALID_PARENT_OBJECT = 0x201A; + /** @hide */ public static final int RESPONSE_INVALID_DEVICE_PROP_FORMAT = 0x201B; + /** @hide */ public static final int RESPONSE_INVALID_DEVICE_PROP_VALUE = 0x201C; + /** @hide */ public static final int RESPONSE_INVALID_PARAMETER = 0x201D; + /** @hide */ public static final int RESPONSE_SESSION_ALREADY_OPEN = 0x201E; + /** @hide */ public static final int RESPONSE_TRANSACTION_CANCELLED = 0x201F; + /** @hide */ public static final int RESPONSE_SPECIFICATION_OF_DESTINATION_UNSUPPORTED = 0x2020; + /** @hide */ public static final int RESPONSE_INVALID_OBJECT_PROP_CODE = 0xA801; + /** @hide */ public static final int RESPONSE_INVALID_OBJECT_PROP_FORMAT = 0xA802; + /** @hide */ public static final int RESPONSE_INVALID_OBJECT_PROP_VALUE = 0xA803; + /** @hide */ public static final int RESPONSE_INVALID_OBJECT_REFERENCE = 0xA804; + /** @hide */ public static final int RESPONSE_GROUP_NOT_SUPPORTED = 0xA805; + /** @hide */ public static final int RESPONSE_INVALID_DATASET = 0xA806; + /** @hide */ public static final int RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED = 0xA807; + /** @hide */ public static final int RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED = 0xA808; + /** @hide */ public static final int RESPONSE_OBJECT_TOO_LARGE = 0xA809; + /** @hide */ public static final int RESPONSE_OBJECT_PROP_NOT_SUPPORTED = 0xA80A; // MTP format codes + /** Undefined format code */ public static final int FORMAT_UNDEFINED = 0x3000; + /** Format code for associations (folders and directories) */ public static final int FORMAT_ASSOCIATION = 0x3001; + /** Format code for script files */ public static final int FORMAT_SCRIPT = 0x3002; + /** Format code for executable files */ public static final int FORMAT_EXECUTABLE = 0x3003; + /** Format code for text files */ public static final int FORMAT_TEXT = 0x3004; + /** Format code for HTML files */ public static final int FORMAT_HTML = 0x3005; + /** Format code for DPOF files */ public static final int FORMAT_DPOF = 0x3006; + /** Format code for AIFF audio files */ public static final int FORMAT_AIFF = 0x3007; + /** Format code for WAV audio files */ public static final int FORMAT_WAV = 0x3008; + /** Format code for MP3 audio files */ public static final int FORMAT_MP3 = 0x3009; + /** Format code for AVI video files */ public static final int FORMAT_AVI = 0x300A; + /** Format code for MPEG video files */ public static final int FORMAT_MPEG = 0x300B; + /** Format code for ASF files */ public static final int FORMAT_ASF = 0x300C; - public static final int FORMAT_DEFINED = 0x3800; + /** Format code for JPEG image files */ public static final int FORMAT_EXIF_JPEG = 0x3801; + /** Format code for TIFF EP image files */ public static final int FORMAT_TIFF_EP = 0x3802; - public static final int FORMAT_FLASHPIX = 0x3803; + /** Format code for BMP image files */ public static final int FORMAT_BMP = 0x3804; - public static final int FORMAT_CIFF = 0x3805; + /** Format code for GIF image files */ public static final int FORMAT_GIF = 0x3807; + /** Format code for JFIF image files */ public static final int FORMAT_JFIF = 0x3808; - public static final int FORMAT_CD = 0x3809; + /** Format code for PICT image files */ public static final int FORMAT_PICT = 0x380A; + /** Format code for PNG image files */ public static final int FORMAT_PNG = 0x380B; + /** Format code for TIFF image files */ public static final int FORMAT_TIFF = 0x380D; - public static final int FORMAT_TIFF_IT = 0x380E; + /** Format code for JP2 files */ public static final int FORMAT_JP2 = 0x380F; + /** Format code for JPX files */ public static final int FORMAT_JPX = 0x3810; + /** Format code for firmware files */ public static final int FORMAT_UNDEFINED_FIRMWARE = 0xB802; + /** Format code for Windows image files */ public static final int FORMAT_WINDOWS_IMAGE_FORMAT = 0xB881; + /** Format code for undefined audio files files */ public static final int FORMAT_UNDEFINED_AUDIO = 0xB900; + /** Format code for WMA audio files */ public static final int FORMAT_WMA = 0xB901; + /** Format code for OGG audio files */ public static final int FORMAT_OGG = 0xB902; + /** Format code for AAC audio files */ public static final int FORMAT_AAC = 0xB903; + /** Format code for Audible audio files */ public static final int FORMAT_AUDIBLE = 0xB904; + /** Format code for FLAC audio files */ public static final int FORMAT_FLAC = 0xB906; + /** Format code for undefined video files */ public static final int FORMAT_UNDEFINED_VIDEO = 0xB980; + /** Format code for WMV video files */ public static final int FORMAT_WMV = 0xB981; + /** Format code for MP4 files */ public static final int FORMAT_MP4_CONTAINER = 0xB982; + /** Format code for MP2 files */ public static final int FORMAT_MP2 = 0xB983; + /** Format code for 3GP files */ public static final int FORMAT_3GP_CONTAINER = 0xB984; + /** Format code for undefined collections */ public static final int FORMAT_UNDEFINED_COLLECTION = 0xBA00; + /** Format code for multimedia albums */ public static final int FORMAT_ABSTRACT_MULTIMEDIA_ALBUM = 0xBA01; + /** Format code for image albums */ public static final int FORMAT_ABSTRACT_IMAGE_ALBUM = 0xBA02; + /** Format code for audio albums */ public static final int FORMAT_ABSTRACT_AUDIO_ALBUM = 0xBA03; + /** Format code for video albums */ public static final int FORMAT_ABSTRACT_VIDEO_ALBUM = 0xBA04; + /** Format code for abstract AV playlists */ public static final int FORMAT_ABSTRACT_AV_PLAYLIST = 0xBA05; - public static final int FORMAT_ABSTRACT_CONTACT_GROUP = 0xBA06; - public static final int FORMAT_ABSTRACT_MESSAGE_FOLDER = 0xBA07; - public static final int FORMAT_ABSTRACT_CHAPTERED_PRODUCTION = 0xBA08; + /** Format code for abstract audio playlists */ public static final int FORMAT_ABSTRACT_AUDIO_PLAYLIST = 0xBA09; + /** Format code for abstract video playlists */ public static final int FORMAT_ABSTRACT_VIDEO_PLAYLIST = 0xBA0A; + /** Format code for abstract mediacasts */ public static final int FORMAT_ABSTRACT_MEDIACAST = 0xBA0B; + /** Format code for WPL playlist files */ public static final int FORMAT_WPL_PLAYLIST = 0xBA10; + /** Format code for M3u playlist files */ public static final int FORMAT_M3U_PLAYLIST = 0xBA11; + /** Format code for MPL playlist files */ public static final int FORMAT_MPL_PLAYLIST = 0xBA12; + /** Format code for ASX playlist files */ public static final int FORMAT_ASX_PLAYLIST = 0xBA13; + /** Format code for PLS playlist files */ public static final int FORMAT_PLS_PLAYLIST = 0xBA14; + /** Format code for undefined document files */ public static final int FORMAT_UNDEFINED_DOCUMENT = 0xBA80; + /** Format code for abstract documents */ public static final int FORMAT_ABSTRACT_DOCUMENT = 0xBA81; + /** Format code for XML documents */ public static final int FORMAT_XML_DOCUMENT = 0xBA82; + /** Format code for MS Word documents */ public static final int FORMAT_MS_WORD_DOCUMENT = 0xBA83; - public static final int FORMAT_MHT_COMPILED_HTML_DOCUMENT = 0xBA84; + /** Format code for MS Excel spreadsheets */ public static final int FORMAT_MS_EXCEL_SPREADSHEET = 0xBA85; + /** Format code for MS PowerPoint presentatiosn */ public static final int FORMAT_MS_POWERPOINT_PRESENTATION = 0xBA86; - public static final int FORMAT_UNDEFINED_MESSAGE = 0xBB00; - public static final int FORMAT_ABSTRACT_MESSSAGE = 0xBB01; - public static final int FORMAT_UNDEFINED_CONTACT = 0xBB80; - public static final int FORMAT_ABSTRACT_CONTACT = 0xBB81; - public static final int FORMAT_VCARD_2 = 0xBB82; + /** + * Returns true if the object is abstract (that is, it has no representation + * in the underlying file system. + * + * @param format the format of the object + * @return true if the object is abstract + */ public static boolean isAbstractObject(int format) { switch (format) { case FORMAT_ABSTRACT_MULTIMEDIA_ALBUM: @@ -169,15 +283,10 @@ public final class MtpConstants { case FORMAT_ABSTRACT_AUDIO_ALBUM: case FORMAT_ABSTRACT_VIDEO_ALBUM: case FORMAT_ABSTRACT_AV_PLAYLIST: - case FORMAT_ABSTRACT_CONTACT_GROUP: - case FORMAT_ABSTRACT_MESSAGE_FOLDER: - case FORMAT_ABSTRACT_CHAPTERED_PRODUCTION: case FORMAT_ABSTRACT_AUDIO_PLAYLIST: case FORMAT_ABSTRACT_VIDEO_PLAYLIST: case FORMAT_ABSTRACT_MEDIACAST: case FORMAT_ABSTRACT_DOCUMENT: - case FORMAT_ABSTRACT_MESSSAGE: - case FORMAT_ABSTRACT_CONTACT: return true; default: return false; @@ -185,223 +294,259 @@ public final class MtpConstants { } // MTP object properties + /** @hide */ public static final int PROPERTY_STORAGE_ID = 0xDC01; + /** @hide */ public static final int PROPERTY_OBJECT_FORMAT = 0xDC02; + /** @hide */ public static final int PROPERTY_PROTECTION_STATUS = 0xDC03; + /** @hide */ public static final int PROPERTY_OBJECT_SIZE = 0xDC04; + /** @hide */ public static final int PROPERTY_ASSOCIATION_TYPE = 0xDC05; + /** @hide */ public static final int PROPERTY_ASSOCIATION_DESC = 0xDC06; + /** @hide */ public static final int PROPERTY_OBJECT_FILE_NAME = 0xDC07; + /** @hide */ public static final int PROPERTY_DATE_CREATED = 0xDC08; + /** @hide */ public static final int PROPERTY_DATE_MODIFIED = 0xDC09; + /** @hide */ public static final int PROPERTY_KEYWORDS = 0xDC0A; + /** @hide */ public static final int PROPERTY_PARENT_OBJECT = 0xDC0B; + /** @hide */ public static final int PROPERTY_ALLOWED_FOLDER_CONTENTS = 0xDC0C; + /** @hide */ public static final int PROPERTY_HIDDEN = 0xDC0D; + /** @hide */ public static final int PROPERTY_SYSTEM_OBJECT = 0xDC0E; + /** @hide */ public static final int PROPERTY_PERSISTENT_UID = 0xDC41; + /** @hide */ public static final int PROPERTY_SYNC_ID = 0xDC42; + /** @hide */ public static final int PROPERTY_PROPERTY_BAG = 0xDC43; + /** @hide */ public static final int PROPERTY_NAME = 0xDC44; + /** @hide */ public static final int PROPERTY_CREATED_BY = 0xDC45; + /** @hide */ public static final int PROPERTY_ARTIST = 0xDC46; + /** @hide */ public static final int PROPERTY_DATE_AUTHORED = 0xDC47; + /** @hide */ public static final int PROPERTY_DESCRIPTION = 0xDC48; + /** @hide */ public static final int PROPERTY_URL_REFERENCE = 0xDC49; + /** @hide */ public static final int PROPERTY_LANGUAGE_LOCALE = 0xDC4A; + /** @hide */ public static final int PROPERTY_COPYRIGHT_INFORMATION = 0xDC4B; + /** @hide */ public static final int PROPERTY_SOURCE = 0xDC4C; + /** @hide */ public static final int PROPERTY_ORIGIN_LOCATION = 0xDC4D; + /** @hide */ public static final int PROPERTY_DATE_ADDED = 0xDC4E; + /** @hide */ public static final int PROPERTY_NON_CONSUMABLE = 0xDC4F; + /** @hide */ public static final int PROPERTY_CORRUPT_UNPLAYABLE = 0xDC50; + /** @hide */ public static final int PROPERTY_PRODUCER_SERIAL_NUMBER = 0xDC51; + /** @hide */ public static final int PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT = 0xDC81; + /** @hide */ public static final int PROPERTY_REPRESENTATIVE_SAMPLE_SIZE = 0xDC82; + /** @hide */ public static final int PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT = 0xDC83; + /** @hide */ public static final int PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH = 0xDC84; + /** @hide */ public static final int PROPERTY_REPRESENTATIVE_SAMPLE_DURATION = 0xDC85; + /** @hide */ public static final int PROPERTY_REPRESENTATIVE_SAMPLE_DATA = 0xDC86; + /** @hide */ public static final int PROPERTY_WIDTH = 0xDC87; + /** @hide */ public static final int PROPERTY_HEIGHT = 0xDC88; + /** @hide */ public static final int PROPERTY_DURATION = 0xDC89; + /** @hide */ public static final int PROPERTY_RATING = 0xDC8A; + /** @hide */ public static final int PROPERTY_TRACK = 0xDC8B; + /** @hide */ public static final int PROPERTY_GENRE = 0xDC8C; + /** @hide */ public static final int PROPERTY_CREDITS = 0xDC8D; + /** @hide */ public static final int PROPERTY_LYRICS = 0xDC8E; + /** @hide */ public static final int PROPERTY_SUBSCRIPTION_CONTENT_ID = 0xDC8F; + /** @hide */ public static final int PROPERTY_PRODUCED_BY = 0xDC90; + /** @hide */ public static final int PROPERTY_USE_COUNT = 0xDC91; + /** @hide */ public static final int PROPERTY_SKIP_COUNT = 0xDC92; + /** @hide */ public static final int PROPERTY_LAST_ACCESSED = 0xDC93; + /** @hide */ public static final int PROPERTY_PARENTAL_RATING = 0xDC94; + /** @hide */ public static final int PROPERTY_META_GENRE = 0xDC95; + /** @hide */ public static final int PROPERTY_COMPOSER = 0xDC96; + /** @hide */ public static final int PROPERTY_EFFECTIVE_RATING = 0xDC97; + /** @hide */ public static final int PROPERTY_SUBTITLE = 0xDC98; + /** @hide */ public static final int PROPERTY_ORIGINAL_RELEASE_DATE = 0xDC99; + /** @hide */ public static final int PROPERTY_ALBUM_NAME = 0xDC9A; + /** @hide */ public static final int PROPERTY_ALBUM_ARTIST = 0xDC9B; + /** @hide */ public static final int PROPERTY_MOOD = 0xDC9C; + /** @hide */ public static final int PROPERTY_DRM_STATUS = 0xDC9D; + /** @hide */ public static final int PROPERTY_SUB_DESCRIPTION = 0xDC9E; + /** @hide */ public static final int PROPERTY_IS_CROPPED = 0xDCD1; + /** @hide */ public static final int PROPERTY_IS_COLOUR_CORRECTED = 0xDCD2; + /** @hide */ public static final int PROPERTY_IMAGE_BIT_DEPTH = 0xDCD3; + /** @hide */ public static final int PROPERTY_F_NUMBER = 0xDCD4; + /** @hide */ public static final int PROPERTY_EXPOSURE_TIME = 0xDCD5; + /** @hide */ public static final int PROPERTY_EXPOSURE_INDEX = 0xDCD6; + /** @hide */ public static final int PROPERTY_TOTAL_BITRATE = 0xDE91; + /** @hide */ public static final int PROPERTY_BITRATE_TYPE = 0xDE92; + /** @hide */ public static final int PROPERTY_SAMPLE_RATE = 0xDE93; + /** @hide */ public static final int PROPERTY_NUMBER_OF_CHANNELS = 0xDE94; + /** @hide */ public static final int PROPERTY_AUDIO_BIT_DEPTH = 0xDE95; + /** @hide */ public static final int PROPERTY_SCAN_TYPE = 0xDE97; + /** @hide */ public static final int PROPERTY_AUDIO_WAVE_CODEC = 0xDE99; + /** @hide */ public static final int PROPERTY_AUDIO_BITRATE = 0xDE9A; + /** @hide */ public static final int PROPERTY_VIDEO_FOURCC_CODEC = 0xDE9B; + /** @hide */ public static final int PROPERTY_VIDEO_BITRATE = 0xDE9C; + /** @hide */ public static final int PROPERTY_FRAMES_PER_THOUSAND_SECONDS = 0xDE9D; + /** @hide */ public static final int PROPERTY_KEYFRAME_DISTANCE = 0xDE9E; + /** @hide */ public static final int PROPERTY_BUFFER_SIZE = 0xDE9F; + /** @hide */ public static final int PROPERTY_ENCODING_QUALITY = 0xDEA0; + /** @hide */ public static final int PROPERTY_ENCODING_PROFILE = 0xDEA1; + /** @hide */ public static final int PROPERTY_DISPLAY_NAME = 0xDCE0; - public static final int PROPERTY_BODY_TEXT = 0xDCE1; - public static final int PROPERTY_SUBJECT = 0xDCE2; - public static final int PROPERTY_PRIORITY = 0xDCE3; - public static final int PROPERTY_GIVEN_NAME = 0xDD00; - public static final int PROPERTY_MIDDLE_NAMES = 0xDD01; - public static final int PROPERTY_FAMILY_NAME = 0xDD02; - public static final int PROPERTY_PREFIX = 0xDD03; - public static final int PROPERTY_SUFFIX = 0xDD04; - public static final int PROPERTY_PHONETIC_GIVEN_NAME = 0xDD05; - public static final int PROPERTY_PHONETIC_FAMILY_NAME = 0xDD06; - public static final int PROPERTY_EMAIL_PRIMARY = 0xDD07; - public static final int PROPERTY_EMAIL_PERSONAL_1 = 0xDD08; - public static final int PROPERTY_EMAIL_PERSONAL_2 = 0xDD09; - public static final int PROPERTY_EMAIL_BUSINESS_1 = 0xDD0A; - public static final int PROPERTY_EMAIL_BUSINESS_2 = 0xDD0B; - public static final int PROPERTY_EMAIL_OTHERS = 0xDD0C; - public static final int PROPERTY_PHONE_NUMBER_PRIMARY = 0xDD0D; - public static final int PROPERTY_PHONE_NUMBER_PERSONAL = 0xDD0E; - public static final int PROPERTY_PHONE_NUMBER_PERSONAL_2 = 0xDD0F; - public static final int PROPERTY_PHONE_NUMBER_BUSINESS = 0xDD10; - public static final int PROPERTY_PHONE_NUMBER_BUSINESS_2 = 0xDD11; - public static final int PROPERTY_PHONE_NUMBER_MOBILE= 0xDD12; - public static final int PROPERTY_PHONE_NUMBER_MOBILE_2 = 0xDD13; - public static final int PROPERTY_FAX_NUMBER_PRIMARY = 0xDD14; - public static final int PROPERTY_FAX_NUMBER_PERSONAL= 0xDD15; - public static final int PROPERTY_FAX_NUMBER_BUSINESS= 0xDD16; - public static final int PROPERTY_PAGER_NUMBER = 0xDD17; - public static final int PROPERTY_PHONE_NUMBER_OTHERS= 0xDD18; - public static final int PROPERTY_PRIMARY_WEB_ADDRESS= 0xDD19; - public static final int PROPERTY_PERSONAL_WEB_ADDRESS = 0xDD1A; - public static final int PROPERTY_BUSINESS_WEB_ADDRESS = 0xDD1B; - public static final int PROPERTY_INSTANT_MESSANGER_ADDRESS = 0xDD1C; - public static final int PROPERTY_INSTANT_MESSANGER_ADDRESS_2 = 0xDD1D; - public static final int PROPERTY_INSTANT_MESSANGER_ADDRESS_3 = 0xDD1E; - public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL = 0xDD1F; - public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1 = 0xDD20; - public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2 = 0xDD21; - public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY = 0xDD22; - public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION = 0xDD23; - public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE = 0xDD24; - public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY = 0xDD25; - public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL = 0xDD26; - public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1 = 0xDD27; - public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2 = 0xDD28; - public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY = 0xDD29; - public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION = 0xDD2A; - public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE = 0xDD2B; - public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY = 0xDD2C; - public static final int PROPERTY_POSTAL_ADDRESS_OTHER_FULL = 0xDD2D; - public static final int PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1 = 0xDD2E; - public static final int PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2 = 0xDD2F; - public static final int PROPERTY_POSTAL_ADDRESS_OTHER_CITY = 0xDD30; - public static final int PROPERTY_POSTAL_ADDRESS_OTHER_REGION = 0xDD31; - public static final int PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE = 0xDD32; - public static final int PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY = 0xDD33; - public static final int PROPERTY_ORGANIZATION_NAME = 0xDD34; - public static final int PROPERTY_PHONETIC_ORGANIZATION_NAME = 0xDD35; - public static final int PROPERTY_ROLE = 0xDD36; - public static final int PROPERTY_BIRTHDATE = 0xDD37; - public static final int PROPERTY_MESSAGE_TO = 0xDD40; - public static final int PROPERTY_MESSAGE_CC = 0xDD41; - public static final int PROPERTY_MESSAGE_BCC = 0xDD42; - public static final int PROPERTY_MESSAGE_READ = 0xDD43; - public static final int PROPERTY_MESSAGE_RECEIVED_TIME = 0xDD44; - public static final int PROPERTY_MESSAGE_SENDER = 0xDD45; - public static final int PROPERTY_ACTIVITY_BEGIN_TIME = 0xDD50; - public static final int PROPERTY_ACTIVITY_END_TIME = 0xDD51; - public static final int PROPERTY_ACTIVITY_LOCATION = 0xDD52; - public static final int PROPERTY_ACTIVITY_REQUIRED_ATTENDEES = 0xDD54; - public static final int PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES = 0xDD55; - public static final int PROPERTY_ACTIVITY_RESOURCES = 0xDD56; - public static final int PROPERTY_ACTIVITY_ACCEPTED = 0xDD57; - public static final int PROPERTY_ACTIVITY_TENTATIVE = 0xDD58; - public static final int PROPERTY_ACTIVITY_DECLINED = 0xDD59; - public static final int PROPERTY_ACTIVITY_REMAINDER_TIME = 0xDD5A; - public static final int PROPERTY_ACTIVITY_OWNER = 0xDD5B; - public static final int PROPERTY_ACTIVITY_STATUS = 0xDD5C; - public static final int PROPERTY_OWNER = 0xDD5D; - public static final int PROPERTY_EDITOR = 0xDD5E; - public static final int PROPERTY_WEBMASTER = 0xDD5F; - public static final int PROPERTY_URL_SOURCE = 0xDD60; - public static final int PROPERTY_URL_DESTINATION = 0xDD61; - public static final int PROPERTY_TIME_BOOKMARK = 0xDD62; - public static final int PROPERTY_OBJECT_BOOKMARK = 0xDD63; - public static final int PROPERTY_BYTE_BOOKMARK = 0xDD64; - public static final int PROPERTY_LAST_BUILD_DATE = 0xDD70; - public static final int PROPERTY_TIME_TO_LIVE = 0xDD71; - public static final int PROPERTY_MEDIA_GUID = 0xDD72; // MTP device properties + /** @hide */ public static final int DEVICE_PROPERTY_UNDEFINED = 0x5000; + /** @hide */ public static final int DEVICE_PROPERTY_BATTERY_LEVEL = 0x5001; + /** @hide */ public static final int DEVICE_PROPERTY_FUNCTIONAL_MODE = 0x5002; + /** @hide */ public static final int DEVICE_PROPERTY_IMAGE_SIZE = 0x5003; + /** @hide */ public static final int DEVICE_PROPERTY_COMPRESSION_SETTING = 0x5004; + /** @hide */ public static final int DEVICE_PROPERTY_WHITE_BALANCE = 0x5005; + /** @hide */ public static final int DEVICE_PROPERTY_RGB_GAIN = 0x5006; + /** @hide */ public static final int DEVICE_PROPERTY_F_NUMBER = 0x5007; + /** @hide */ public static final int DEVICE_PROPERTY_FOCAL_LENGTH = 0x5008; + /** @hide */ public static final int DEVICE_PROPERTY_FOCUS_DISTANCE = 0x5009; + /** @hide */ public static final int DEVICE_PROPERTY_FOCUS_MODE = 0x500A; + /** @hide */ public static final int DEVICE_PROPERTY_EXPOSURE_METERING_MODE = 0x500B; + /** @hide */ public static final int DEVICE_PROPERTY_FLASH_MODE = 0x500C; + /** @hide */ public static final int DEVICE_PROPERTY_EXPOSURE_TIME = 0x500D; + /** @hide */ public static final int DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE = 0x500E; + /** @hide */ public static final int DEVICE_PROPERTY_EXPOSURE_INDEX = 0x500F; + /** @hide */ public static final int DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION = 0x5010; + /** @hide */ public static final int DEVICE_PROPERTY_DATETIME = 0x5011; + /** @hide */ public static final int DEVICE_PROPERTY_CAPTURE_DELAY = 0x5012; + /** @hide */ public static final int DEVICE_PROPERTY_STILL_CAPTURE_MODE = 0x5013; + /** @hide */ public static final int DEVICE_PROPERTY_CONTRAST = 0x5014; + /** @hide */ public static final int DEVICE_PROPERTY_SHARPNESS = 0x5015; + /** @hide */ public static final int DEVICE_PROPERTY_DIGITAL_ZOOM = 0x5016; + /** @hide */ public static final int DEVICE_PROPERTY_EFFECT_MODE = 0x5017; + /** @hide */ public static final int DEVICE_PROPERTY_BURST_NUMBER= 0x5018; + /** @hide */ public static final int DEVICE_PROPERTY_BURST_INTERVAL = 0x5019; + /** @hide */ public static final int DEVICE_PROPERTY_TIMELAPSE_NUMBER = 0x501A; + /** @hide */ public static final int DEVICE_PROPERTY_TIMELAPSE_INTERVAL = 0x501B; + /** @hide */ public static final int DEVICE_PROPERTY_FOCUS_METERING_MODE = 0x501C; + /** @hide */ public static final int DEVICE_PROPERTY_UPLOAD_URL = 0x501D; + /** @hide */ public static final int DEVICE_PROPERTY_ARTIST = 0x501E; + /** @hide */ public static final int DEVICE_PROPERTY_COPYRIGHT_INFO = 0x501F; + /** @hide */ public static final int DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER = 0xD401; + /** @hide */ public static final int DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME = 0xD402; + /** @hide */ public static final int DEVICE_PROPERTY_VOLUME = 0xD403; + /** @hide */ public static final int DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED = 0xD404; + /** @hide */ public static final int DEVICE_PROPERTY_DEVICE_ICON = 0xD405; + /** @hide */ public static final int DEVICE_PROPERTY_PLAYBACK_RATE = 0xD410; + /** @hide */ public static final int DEVICE_PROPERTY_PLAYBACK_OBJECT = 0xD411; + /** @hide */ public static final int DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX = 0xD412; + /** @hide */ public static final int DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO = 0xD406; + /** @hide */ public static final int DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE = 0xD407; - /** * Object is not protected. It may be modified and deleted, and its properties * may be modified. @@ -424,5 +569,8 @@ public final class MtpConstants { */ public static final int PROTECTION_STATUS_NON_TRANSFERABLE_DATA = 0x8003; + /** + * Association type for objects representing file system directories. + */ public static final int ASSOCIATION_TYPE_GENERIC_FOLDER = 0x0001; } diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index a59556242a21..98de2f700999 100644 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -309,6 +309,7 @@ public class MtpDatabase { MtpConstants.FORMAT_M3U_PLAYLIST, MtpConstants.FORMAT_PLS_PLAYLIST, MtpConstants.FORMAT_XML_DOCUMENT, + MtpConstants.FORMAT_FLAC, }; } diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java new file mode 100644 index 000000000000..78b2253d1da5 --- /dev/null +++ b/media/java/android/mtp/MtpDevice.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.mtp; + +import android.hardware.UsbDevice; +import android.hardware.UsbManager; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +/** + * This class represents an MTP or PTP device connected on the USB host bus. + */ +public final class MtpDevice { + + private static final String TAG = "MtpDevice"; + + private final UsbDevice mDevice; + + static { + System.loadLibrary("media_jni"); + } + + /** + * MtpClient constructor + * + * @param device the {@link android.hardware.UsbDevice} for the MTP or PTP device + */ + public MtpDevice(UsbDevice device) { + mDevice = device; + } + + /** + * Opens the MTP or PTP device and return an {@link android.mtp.MtpDevice} for it. + * + * @param manager reference to {@link android.hardware.UsbManager} + * @return true if the device was successfully opened. + */ + public boolean open(UsbManager manager) { + if (manager.openDevice(mDevice)) { + return native_open(mDevice.getDeviceName(), mDevice.getFileDescriptor()); + } else { + return false; + } + } + + /** + * Closes all resources related to the MtpDevice object + */ + public void close() { + native_close(); + } + + @Override + protected void finalize() throws Throwable { + try { + native_close(); + } finally { + super.finalize(); + } + } + + /** + * Returns the name of the USB device + * + * @return the device name + */ + public String getDeviceName() { + return mDevice.getDeviceName(); + } + + /** + * Returns the ID of the USB device + * + * @return the device ID + */ + public int getDeviceId() { + return mDevice.getDeviceId(); + } + + @Override + public String toString() { + return mDevice.getDeviceName(); + } + + /** + * Returns the {@link android.mtp.MtpDeviceInfo} for this device + * + * @return the device info + */ + public MtpDeviceInfo getDeviceInfo() { + return native_get_device_info(); + } + + /** + * Returns the list of IDs for all storage units on this device + * + * @return the storage IDs + */ + public int[] getStorageIds() { + return native_get_storage_ids(); + } + + /** + * Returns the list of object handles for all objects on the given storage unit, + * with the given format and parent. + * + * @param storageId the storage unit to query + * @param format the format of the object to return, or zero for all formats + * @param objectHandle the parent object to query, or zero for the storage root + * @return the object handles + */ + public int[] getObjectHandles(int storageId, int format, int objectHandle) { + return native_get_object_handles(storageId, format, objectHandle); + } + + /** + * Returns the data for an object as a byte array. + * + * @param objectHandle handle of the object to read + * @param objectSize the size of the object (this should match + * {@link android.mtp.MtpObjectInfo#getCompressedSize} + * @return the object's data, or null if reading fails + */ + public byte[] getObject(int objectHandle, int objectSize) { + return native_get_object(objectHandle, objectSize); + } + + /** + * Returns the thumbnail data for an object as a byte array. + * + * @param objectHandle handle of the object to read + * @return the object's thumbnail, or null if reading fails + */ + public byte[] getThumbnail(int objectHandle) { + return native_get_thumbnail(objectHandle); + } + + /** + * Retrieves the {@link android.mtp.MtpStorageInfo} for a storage unit. + * + * @param storageId the ID of the storage unit + * @return the MtpStorageInfo + */ + public MtpStorageInfo getStorageInfo(int storageId) { + return native_get_storage_info(storageId); + } + + /** + * Retrieves the {@link android.mtp.MtpObjectInfo} for an object. + * + * @param objectHandle the handle of the object + * @return the MtpObjectInfo + */ + public MtpObjectInfo getObjectInfo(int objectHandle) { + return native_get_object_info(objectHandle); + } + + /** + * Deletes an object on the device. + * + * @param objectHandle handle of the object to delete + * @return true if the deletion succeeds + */ + public boolean deleteObject(int objectHandle) { + return native_delete_object(objectHandle); + } + + /** + * Retrieves the object handle for the parent of an object on the device. + * + * @param objectHandle handle of the object to query + * @return the parent's handle, or zero if it is in the root of the storage + */ + public long getParent(int objectHandle) { + return native_get_parent(objectHandle); + } + + /** + * Retrieves the ID of the storage unit containing the given object on the device. + * + * @param objectHandle handle of the object to query + * @return the object's storage unit ID + */ + public long getStorageID(int objectHandle) { + return native_get_storage_id(objectHandle); + } + + /** + * Copies the data for an object to a file in external storage. + * + * @param objectHandle handle of the object to read + * @param destPath path to destination for the file transfer. + * This path should be in the external storage as defined by + * {@link android.os.Environment#getExternalStorageDirectory} + * @return true if the file transfer succeeds + */ + public boolean importFile(int objectHandle, String destPath) { + return native_import_file(objectHandle, destPath); + } + + // used by the JNI code + private int mNativeContext; + + private native boolean native_open(String deviceName, int fd); + private native void native_close(); + private native MtpDeviceInfo native_get_device_info(); + private native int[] native_get_storage_ids(); + private native MtpStorageInfo native_get_storage_info(int storageId); + private native int[] native_get_object_handles(int storageId, int format, int objectHandle); + private native MtpObjectInfo native_get_object_info(int objectHandle); + private native byte[] native_get_object(int objectHandle, int objectSize); + private native byte[] native_get_thumbnail(int objectHandle); + private native boolean native_delete_object(int objectHandle); + private native long native_get_parent(int objectHandle); + private native long native_get_storage_id(int objectHandle); + private native boolean native_import_file(int objectHandle, String destPath); +} diff --git a/media/java/android/mtp/MtpDeviceInfo.java b/media/java/android/mtp/MtpDeviceInfo.java new file mode 100644 index 000000000000..ef9436dfa2ce --- /dev/null +++ b/media/java/android/mtp/MtpDeviceInfo.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.mtp; + +/** + * This class encapsulates information about an MTP device. + * This corresponds to the DeviceInfo Dataset described in + * section 5.1.1 of the MTP specification. + */ +public class MtpDeviceInfo { + + private String mManufacturer; + private String mModel; + private String mVersion; + private String mSerialNumber; + + // only instantiated via JNI + private MtpDeviceInfo() { + } + + /** + * Returns the manufacturer's name for the MTP device + * + * @return the manufacturer name + */ + public final String getManufacturer() { + return mManufacturer; + } + + /** + * Returns the model name for the MTP device + * + * @return the model name + */ + public final String getModel() { + return mModel; + } + + /** + * Returns the version string the MTP device + * + * @return the device version + */ + public final String getVersion() { + return mVersion; + } + + /** + * Returns the unique serial number for the MTP device + * + * @return the serial number + */ + public final String getSerialNumber() { + return mSerialNumber; + } +}
\ No newline at end of file diff --git a/media/java/android/mtp/MtpObjectInfo.java b/media/java/android/mtp/MtpObjectInfo.java new file mode 100644 index 000000000000..5bbfe9a6888f --- /dev/null +++ b/media/java/android/mtp/MtpObjectInfo.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.mtp; + +/** + * This class encapsulates information about an object on an MTP device. + * This corresponds to the ObjectInfo Dataset described in + * section 5.3.1 of the MTP specification. + */ +public final class MtpObjectInfo { + private int mHandle; + private int mStorageId; + private int mFormat; + private int mProtectionStatus; + private int mCompressedSize; + private int mThumbFormat; + private int mThumbCompressedSize; + private int mThumbPixWidth; + private int mThumbPixHeight; + private int mImagePixWidth; + private int mImagePixHeight; + private int mImagePixDepth; + private int mParent; + private int mAssociationType; + private int mAssociationDesc; + private int mSequenceNumber; + private String mName; + private long mDateCreated; + private long mDateModified; + private String mKeywords; + + // only instantiated via JNI + private MtpObjectInfo() { + } + + /** + * Returns the object handle for the MTP object + * + * @return the object handle + */ + public final int getObjectHandle() { + return mHandle; + } + + /** + * Returns the storage ID for the MTP object's storage unit + * + * @return the storage ID + */ + public final int getStorageId() { + return mStorageId; + } + + /** + * Returns the format code for the MTP object + * + * @return the format code + */ + public final int getFormat() { + return mFormat; + } + + /** + * Returns the protection status for the MTP object + * Possible values are: + * + * <ul> + * <li> {@link android.mtp.MtpConstants#PROTECTION_STATUS_NONE} + * <li> {@link android.mtp.MtpConstants#PROTECTION_STATUS_READ_ONLY} + * <li> {@link android.mtp.MtpConstants#PROTECTION_STATUS_NON_TRANSFERABLE_DATA} + * </ul> + * + * @return the protection status + */ + public final int getProtectionStatus() { + return mProtectionStatus; + } + + /** + * Returns the size of the MTP object + * + * @return the object size + */ + public final int getCompressedSize() { + return mCompressedSize; + } + + /** + * Returns the format code for the MTP object's thumbnail + * Will be zero for objects with no thumbnail + * + * @return the thumbnail format code + */ + public final int getThumbFormat() { + return mThumbFormat; + } + + /** + * Returns the size of the MTP object's thumbnail + * Will be zero for objects with no thumbnail + * + * @return the thumbnail size + */ + public final int getThumbCompressedSize() { + return mThumbCompressedSize; + } + + /** + * Returns the width of the MTP object's thumbnail in pixels + * Will be zero for objects with no thumbnail + * + * @return the thumbnail width + */ + public final int getThumbPixWidth() { + return mThumbPixWidth; + } + + /** + * Returns the height of the MTP object's thumbnail in pixels + * Will be zero for objects with no thumbnail + * + * @return the thumbnail height + */ + public final int getThumbPixHeight() { + return mThumbPixHeight; + } + + /** + * Returns the width of the MTP object in pixels + * Will be zero for non-image objects + * + * @return the image width + */ + public final int getImagePixWidth() { + return mImagePixWidth; + } + + /** + * Returns the height of the MTP object in pixels + * Will be zero for non-image objects + * + * @return the image height + */ + public final int getImagePixHeight() { + return mImagePixHeight; + } + + /** + * Returns the depth of the MTP object in bits per pixel + * Will be zero for non-image objects + * + * @return the image depth + */ + public final int getImagePixDepth() { + return mImagePixDepth; + } + + /** + * Returns the object handle for the object's parent + * Will be zero for the root directory of a storage unit + * + * @return the object's parent + */ + public final int getParent() { + return mParent; + } + + /** + * Returns the association type for the MTP object + * Will be zero objects that are not of format + * {@link android.mtp.MtpConstants#FORMAT_ASSOCIATION} + * For directories the association type is typically + * {@link android.mtp.MtpConstants#ASSOCIATION_TYPE_GENERIC_FOLDER} + * + * @return the object's association type + */ + public final int getAssociationType() { + return mAssociationType; + } + + /** + * Returns the association description for the MTP object + * Will be zero objects that are not of format + * {@link android.mtp.MtpConstants#FORMAT_ASSOCIATION} + * + * @return the object's association description + */ + public final int getAssociationDesc() { + return mAssociationDesc; + } + + /** + * Returns the sequence number for the MTP object + * This field is typically not used for MTP devices, + * but is sometimes used to define a sequence of photos + * on PTP cameras. + * + * @return the object's sequence number + */ + public final int getSequenceNumber() { + return mSequenceNumber; + } + + /** + * Returns the name of the MTP object + * + * @return the object's name + */ + public final String getName() { + return mName; + } + + /** + * Returns the creation date of the MTP object + * The value is represented as milliseconds since January 1, 1970 + * + * @return the object's creation date + */ + public final long getDateCreated() { + return mDateCreated; + } + + /** + * Returns the modification date of the MTP object + * The value is represented as milliseconds since January 1, 1970 + * + * @return the object's modification date + */ + public final long getDateModified() { + return mDateModified; + } + + /** + * Returns a comma separated list of keywords for the MTP object + * + * @return the object's keyword list + */ + public final String getKeywords() { + return mKeywords; + } +} diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java index 57e23042d432..fceedd28e040 100644 --- a/media/java/android/mtp/MtpPropertyGroup.java +++ b/media/java/android/mtp/MtpPropertyGroup.java @@ -60,11 +60,10 @@ class MtpPropertyGroup { private String[] mColumns; private static final String ID_WHERE = Files.FileColumns._ID + "=?"; - private static final String ID_FORMAT_WHERE = ID_WHERE + " AND " - + Files.FileColumns.FORMAT + "=?"; + private static final String FORMAT_WHERE = Files.FileColumns.FORMAT + "=?"; + private static final String ID_FORMAT_WHERE = ID_WHERE + " AND " + FORMAT_WHERE; private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?"; - private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND " - + Files.FileColumns.FORMAT + "=?"; + private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND " + FORMAT_WHERE; // constructs a property group for a list of properties public MtpPropertyGroup(MtpDatabase database, IContentProvider provider, String volume, int[] properties) { @@ -292,25 +291,37 @@ class MtpPropertyGroup { String where; String[] whereArgs; if (format == 0) { - whereArgs = new String[] { Integer.toString(handle) }; - if (depth == 1) { - where = PARENT_WHERE; + if (handle == 0xFFFFFFFF) { + // select all objects + where = null; + whereArgs = null; } else { - where = ID_WHERE; + whereArgs = new String[] { Integer.toString(handle) }; + if (depth == 1) { + where = PARENT_WHERE; + } else { + where = ID_WHERE; + } } } else { - whereArgs = new String[] { Integer.toString(handle), Integer.toString(format) }; - if (depth == 1) { - where = PARENT_FORMAT_WHERE; + if (handle == 0xFFFFFFFF) { + // select all objects with given format + where = FORMAT_WHERE; + whereArgs = new String[] { Integer.toString(format) }; } else { - where = ID_FORMAT_WHERE; + whereArgs = new String[] { Integer.toString(handle), Integer.toString(format) }; + if (depth == 1) { + where = PARENT_FORMAT_WHERE; + } else { + where = ID_FORMAT_WHERE; + } } } Cursor c = null; try { // don't query if not necessary - if (depth > 0 || mColumns.length > 1) { + if (depth > 0 || handle == 0xFFFFFFFF || mColumns.length > 1) { c = mProvider.query(mUri, mColumns, where, whereArgs, null); if (c == null) { return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); @@ -318,6 +329,7 @@ class MtpPropertyGroup { } int count = (c == null ? 1 : c.getCount()); + Log.d(TAG, "count: " + count); MtpPropertyList result = new MtpPropertyList(count * mProperties.length, MtpConstants.RESPONSE_OK); @@ -325,8 +337,6 @@ class MtpPropertyGroup { for (int objectIndex = 0; objectIndex < count; objectIndex++) { if (c != null) { c.moveToNext(); - } - if (depth == 1) { handle = (int)c.getLong(0); } diff --git a/media/java/android/mtp/MtpStorageInfo.java b/media/java/android/mtp/MtpStorageInfo.java new file mode 100644 index 000000000000..09736a825f32 --- /dev/null +++ b/media/java/android/mtp/MtpStorageInfo.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.mtp; + +/** + * This class encapsulates information about a storage unit on an MTP device. + * This corresponds to the StorageInfo Dataset described in + * section 5.2.2 of the MTP specification. + */ +public final class MtpStorageInfo { + + private int mStorageId; + private long mMaxCapacity; + private long mFreeSpace; + private String mDescription; + private String mVolumeIdentifier; + + // only instantiated via JNI + private MtpStorageInfo() { + } + + /** + * Returns the storage ID for the storage unit + * + * @return the storage ID + */ + public final int getStorageId() { + return mStorageId; + } + + /** + * Returns the maximum storage capacity for the storage unit in bytes + * + * @return the maximum capacity + */ + public final long getMaxCapacity() { + return mMaxCapacity; + } + + /** + * Returns the amount of free space in the storage unit in bytes + * + * @return the amount of free space + */ + public final long getFreeSpace() { + return mFreeSpace; + } + + /** + * Returns the description string for the storage unit + * + * @return the storage unit description + */ + public final String getDescription() { + return mDescription; + } + + /** + * Returns the volume identifier for the storage unit + * + * @return the storage volume identifier + */ + public final String getVolumeIdentifier() { + return mVolumeIdentifier; + } +} diff --git a/media/jni/Android.mk b/media/jni/Android.mk index ab6e51283c74..2a89a2a9dfe5 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -10,6 +10,7 @@ LOCAL_SRC_FILES:= \ android_media_MediaProfiles.cpp \ android_media_AmrInputStream.cpp \ android_mtp_MtpDatabase.cpp \ + android_mtp_MtpDevice.cpp \ android_mtp_MtpServer.cpp \ LOCAL_SHARED_LIBRARIES := \ diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index f0609b2b69d3..0884e350d769 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -776,6 +776,7 @@ extern int register_android_media_ResampleInputStream(JNIEnv *env); extern int register_android_media_MediaProfiles(JNIEnv *env); extern int register_android_media_AmrInputStream(JNIEnv *env); extern int register_android_mtp_MtpDatabase(JNIEnv *env); +extern int register_android_mtp_MtpDevice(JNIEnv *env); extern int register_android_mtp_MtpServer(JNIEnv *env); jint JNI_OnLoad(JavaVM* vm, void* reserved) @@ -829,6 +830,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) goto bail; } + if (register_android_mtp_MtpDevice(env) < 0) { + LOGE("ERROR: MtpDevice native registration failed"); + goto bail; + } + if (register_android_mtp_MtpServer(env) < 0) { LOGE("ERROR: MtpServer native registration failed"); goto bail; diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp new file mode 100644 index 000000000000..fd3266592ca0 --- /dev/null +++ b/media/jni/android_mtp_MtpDevice.cpp @@ -0,0 +1,663 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "MtpDeviceJNI" +#include "utils/Log.h" + +#include <stdio.h> +#include <assert.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" +#include "private/android_filesystem_config.h" + +#include "MtpTypes.h" +#include "MtpDevice.h" +#include "MtpDeviceInfo.h" +#include "MtpStorageInfo.h" +#include "MtpObjectInfo.h" + +using namespace android; + +// ---------------------------------------------------------------------------- + +static jfieldID field_context; + +jclass clazz_deviceInfo; +jclass clazz_storageInfo; +jclass clazz_objectInfo; + +jmethodID constructor_deviceInfo; +jmethodID constructor_storageInfo; +jmethodID constructor_objectInfo; + +// MtpDeviceInfo fields +static jfieldID field_deviceInfo_manufacturer; +static jfieldID field_deviceInfo_model; +static jfieldID field_deviceInfo_version; +static jfieldID field_deviceInfo_serialNumber; + +// MtpStorageInfo fields +static jfieldID field_storageInfo_storageId; +static jfieldID field_storageInfo_maxCapacity; +static jfieldID field_storageInfo_freeSpace; +static jfieldID field_storageInfo_description; +static jfieldID field_storageInfo_volumeIdentifier; + +// MtpObjectInfo fields +static jfieldID field_objectInfo_handle; +static jfieldID field_objectInfo_storageId; +static jfieldID field_objectInfo_format; +static jfieldID field_objectInfo_protectionStatus; +static jfieldID field_objectInfo_compressedSize; +static jfieldID field_objectInfo_thumbFormat; +static jfieldID field_objectInfo_thumbCompressedSize; +static jfieldID field_objectInfo_thumbPixWidth; +static jfieldID field_objectInfo_thumbPixHeight; +static jfieldID field_objectInfo_imagePixWidth; +static jfieldID field_objectInfo_imagePixHeight; +static jfieldID field_objectInfo_imagePixDepth; +static jfieldID field_objectInfo_parent; +static jfieldID field_objectInfo_associationType; +static jfieldID field_objectInfo_associationDesc; +static jfieldID field_objectInfo_sequenceNumber; +static jfieldID field_objectInfo_name; +static jfieldID field_objectInfo_dateCreated; +static jfieldID field_objectInfo_dateModified; +static jfieldID field_objectInfo_keywords; + +#ifdef HAVE_ANDROID_OS + +MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice) +{ + return (MtpDevice*)env->GetIntField(javaDevice, field_context); +} + +static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + LOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + } +} + +#endif // HAVE_ANDROID_OS + +// ---------------------------------------------------------------------------- + +static jboolean +android_mtp_MtpDevice_open(JNIEnv *env, jobject thiz, jstring deviceName, jint fd) +{ +#ifdef HAVE_ANDROID_OS + LOGD("open\n"); + const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL); + MtpDevice* device = MtpDevice::open(deviceNameStr, fd); + env->ReleaseStringUTFChars(deviceName, deviceNameStr); + + if (device) + env->SetIntField(thiz, field_context, (int)device); + return (device != NULL); +#endif +} + +static void +android_mtp_MtpDevice_close(JNIEnv *env, jobject thiz) +{ +#ifdef HAVE_ANDROID_OS + LOGD("close\n"); + MtpDevice* device = get_device_from_object(env, thiz); + if (device) { + device->close(); + delete device; + env->SetIntField(thiz, field_context, 0); + } +#endif +} + +static jobject +android_mtp_MtpDevice_get_device_info(JNIEnv *env, jobject thiz) +{ +#ifdef HAVE_ANDROID_OS + MtpDevice* device = get_device_from_object(env, thiz); + if (!device) { + LOGD("android_mtp_MtpDevice_get_device_info device is null"); + return NULL; + } + MtpDeviceInfo* deviceInfo = device->getDeviceInfo(); + if (!deviceInfo) { + LOGD("android_mtp_MtpDevice_get_device_info deviceInfo is null"); + return NULL; + } + jobject info = env->NewObject(clazz_deviceInfo, constructor_deviceInfo); + if (info == NULL) { + LOGE("Could not create a MtpDeviceInfo object"); + delete deviceInfo; + return NULL; + } + + if (deviceInfo->mManufacturer) + env->SetObjectField(info, field_deviceInfo_manufacturer, + env->NewStringUTF(deviceInfo->mManufacturer)); + if (deviceInfo->mModel) + env->SetObjectField(info, field_deviceInfo_model, + env->NewStringUTF(deviceInfo->mModel)); + if (deviceInfo->mVersion) + env->SetObjectField(info, field_deviceInfo_version, + env->NewStringUTF(deviceInfo->mVersion)); + if (deviceInfo->mSerial) + env->SetObjectField(info, field_deviceInfo_serialNumber, + env->NewStringUTF(deviceInfo->mSerial)); + + delete deviceInfo; + return info; +#else + return NULL; +#endif +} + +static jintArray +android_mtp_MtpDevice_get_storage_ids(JNIEnv *env, jobject thiz) +{ +#ifdef HAVE_ANDROID_OS + MtpDevice* device = get_device_from_object(env, thiz); + if (!device) + return NULL; + MtpStorageIDList* storageIDs = device->getStorageIDs(); + if (!storageIDs) + return NULL; + + int length = storageIDs->size(); + jintArray array = env->NewIntArray(length); + // FIXME is this cast safe? + env->SetIntArrayRegion(array, 0, length, (const jint *)storageIDs->array()); + + delete storageIDs; + return array; +#else + return NULL; +#endif +} + +static jobject +android_mtp_MtpDevice_get_storage_info(JNIEnv *env, jobject thiz, jint storageID) +{ +#ifdef HAVE_ANDROID_OS + MtpDevice* device = get_device_from_object(env, thiz); + if (!device) + return NULL; + MtpStorageInfo* storageInfo = device->getStorageInfo(storageID); + if (!storageInfo) + return NULL; + + jobject info = env->NewObject(clazz_storageInfo, constructor_storageInfo); + if (info == NULL) { + LOGE("Could not create a MtpStorageInfo object"); + delete storageInfo; + return NULL; + } + + if (storageInfo->mStorageID) + env->SetIntField(info, field_storageInfo_storageId, storageInfo->mStorageID); + if (storageInfo->mMaxCapacity) + env->SetLongField(info, field_storageInfo_maxCapacity, storageInfo->mMaxCapacity); + if (storageInfo->mFreeSpaceBytes) + env->SetLongField(info, field_storageInfo_freeSpace, storageInfo->mFreeSpaceBytes); + if (storageInfo->mStorageDescription) + env->SetObjectField(info, field_storageInfo_description, + env->NewStringUTF(storageInfo->mStorageDescription)); + if (storageInfo->mVolumeIdentifier) + env->SetObjectField(info, field_storageInfo_volumeIdentifier, + env->NewStringUTF(storageInfo->mVolumeIdentifier)); + + delete storageInfo; + return info; +#else + return NULL; +#endif +} + +static jintArray +android_mtp_MtpDevice_get_object_handles(JNIEnv *env, jobject thiz, + jint storageID, jint format, jint objectID) +{ +#ifdef HAVE_ANDROID_OS + MtpDevice* device = get_device_from_object(env, thiz); + if (!device) + return NULL; + MtpObjectHandleList* handles = device->getObjectHandles(storageID, format, objectID); + if (!handles) + return NULL; + + int length = handles->size(); + jintArray array = env->NewIntArray(length); + // FIXME is this cast safe? + env->SetIntArrayRegion(array, 0, length, (const jint *)handles->array()); + + delete handles; + return array; +#else + return NULL; +#endif +} + +static jobject +android_mtp_MtpDevice_get_object_info(JNIEnv *env, jobject thiz, jint objectID) +{ +#ifdef HAVE_ANDROID_OS + MtpDevice* device = get_device_from_object(env, thiz); + if (!device) + return NULL; + MtpObjectInfo* objectInfo = device->getObjectInfo(objectID); + if (!objectInfo) + return NULL; + jobject info = env->NewObject(clazz_objectInfo, constructor_objectInfo); + if (info == NULL) { + LOGE("Could not create a MtpObjectInfo object"); + delete objectInfo; + return NULL; + } + + if (objectInfo->mHandle) + env->SetIntField(info, field_objectInfo_handle, objectInfo->mHandle); + if (objectInfo->mStorageID) + env->SetIntField(info, field_objectInfo_storageId, objectInfo->mStorageID); + if (objectInfo->mFormat) + env->SetIntField(info, field_objectInfo_format, objectInfo->mFormat); + if (objectInfo->mProtectionStatus) + env->SetIntField(info, field_objectInfo_protectionStatus, objectInfo->mProtectionStatus); + if (objectInfo->mCompressedSize) + env->SetIntField(info, field_objectInfo_compressedSize, objectInfo->mCompressedSize); + if (objectInfo->mThumbFormat) + env->SetIntField(info, field_objectInfo_thumbFormat, objectInfo->mThumbFormat); + if (objectInfo->mThumbCompressedSize) + env->SetIntField(info, field_objectInfo_thumbCompressedSize, objectInfo->mThumbCompressedSize); + if (objectInfo->mThumbPixWidth) + env->SetIntField(info, field_objectInfo_thumbPixWidth, objectInfo->mThumbPixWidth); + if (objectInfo->mThumbPixHeight) + env->SetIntField(info, field_objectInfo_thumbPixHeight, objectInfo->mThumbPixHeight); + if (objectInfo->mImagePixWidth) + env->SetIntField(info, field_objectInfo_imagePixWidth, objectInfo->mImagePixWidth); + if (objectInfo->mImagePixHeight) + env->SetIntField(info, field_objectInfo_imagePixHeight, objectInfo->mImagePixHeight); + if (objectInfo->mImagePixDepth) + env->SetIntField(info, field_objectInfo_imagePixDepth, objectInfo->mImagePixDepth); + if (objectInfo->mParent) + env->SetIntField(info, field_objectInfo_parent, objectInfo->mParent); + if (objectInfo->mAssociationType) + env->SetIntField(info, field_objectInfo_associationType, objectInfo->mAssociationType); + if (objectInfo->mAssociationDesc) + env->SetIntField(info, field_objectInfo_associationDesc, objectInfo->mAssociationDesc); + if (objectInfo->mSequenceNumber) + env->SetIntField(info, field_objectInfo_sequenceNumber, objectInfo->mSequenceNumber); + if (objectInfo->mName) + env->SetObjectField(info, field_objectInfo_name, env->NewStringUTF(objectInfo->mName)); + if (objectInfo->mDateCreated) + env->SetLongField(info, field_objectInfo_dateCreated, objectInfo->mDateCreated); + if (objectInfo->mDateModified) + env->SetLongField(info, field_objectInfo_dateModified, objectInfo->mDateModified); + if (objectInfo->mKeywords) + env->SetObjectField(info, field_objectInfo_keywords, + env->NewStringUTF(objectInfo->mKeywords)); + + delete objectInfo; + return info; +#else + return NULL; +#endif +} + +struct get_object_callback_data { + JNIEnv *env; + jbyteArray array; +}; + +static bool get_object_callback(void* data, int offset, int length, void* clientData) +{ + get_object_callback_data* cbData = (get_object_callback_data *)clientData; + cbData->env->SetByteArrayRegion(cbData->array, offset, length, (jbyte *)data); + return true; +} + +static jbyteArray +android_mtp_MtpDevice_get_object(JNIEnv *env, jobject thiz, jint objectID, jint objectSize) +{ +#ifdef HAVE_ANDROID_OS + MtpDevice* device = get_device_from_object(env, thiz); + if (!device) + return NULL; + + jbyteArray array = env->NewByteArray(objectSize); + if (!array) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return NULL; + } + + get_object_callback_data data; + data.env = env; + data.array = array; + + if (device->readObject(objectID, get_object_callback, objectSize, &data)) + return array; +#endif + return NULL; +} + +static jbyteArray +android_mtp_MtpDevice_get_thumbnail(JNIEnv *env, jobject thiz, jint objectID) +{ +#ifdef HAVE_ANDROID_OS + MtpDevice* device = get_device_from_object(env, thiz); + if (!device) + return NULL; + + int length; + void* thumbnail = device->getThumbnail(objectID, length); + if (! thumbnail) + return NULL; + jbyteArray array = env->NewByteArray(length); + env->SetByteArrayRegion(array, 0, length, (const jbyte *)thumbnail); + + free(thumbnail); + return array; +#else + return NULL; +#endif +} + +static jboolean +android_mtp_MtpDevice_delete_object(JNIEnv *env, jobject thiz, jint object_id) +{ +#ifdef HAVE_ANDROID_OS + MtpDevice* device = get_device_from_object(env, thiz); + if (device) + return device->deleteObject(object_id); + else + #endif + return NULL; +} + +static jlong +android_mtp_MtpDevice_get_parent(JNIEnv *env, jobject thiz, jint object_id) +{ +#ifdef HAVE_ANDROID_OS + MtpDevice* device = get_device_from_object(env, thiz); + if (device) + return device->getParent(object_id); + else +#endif + return -1; +} + +static jlong +android_mtp_MtpDevice_get_storage_id(JNIEnv *env, jobject thiz, jint object_id) +{ + #ifdef HAVE_ANDROID_OS + MtpDevice* device = get_device_from_object(env, thiz); + if (device) + return device->getStorageID(object_id); + else +#endif + return -1; +} + +static jboolean +android_mtp_MtpDevice_import_file(JNIEnv *env, jobject thiz, jint object_id, jstring dest_path) +{ +#ifdef HAVE_ANDROID_OS + MtpDevice* device = get_device_from_object(env, thiz); + if (device) { + const char *destPathStr = env->GetStringUTFChars(dest_path, NULL); + bool result = device->readObject(object_id, destPathStr, AID_SDCARD_RW, 0664); + env->ReleaseStringUTFChars(dest_path, destPathStr); + return result; + } +#endif + return NULL; +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gMethods[] = { + {"native_open", "(Ljava/lang/String;I)Z", + (void *)android_mtp_MtpDevice_open}, + {"native_close", "()V", (void *)android_mtp_MtpDevice_close}, + {"native_get_device_info", "()Landroid/mtp/MtpDeviceInfo;", + (void *)android_mtp_MtpDevice_get_device_info}, + {"native_get_storage_ids", "()[I", (void *)android_mtp_MtpDevice_get_storage_ids}, + {"native_get_storage_info", "(I)Landroid/mtp/MtpStorageInfo;", + (void *)android_mtp_MtpDevice_get_storage_info}, + {"native_get_object_handles","(III)[I", + (void *)android_mtp_MtpDevice_get_object_handles}, + {"native_get_object_info", "(I)Landroid/mtp/MtpObjectInfo;", + (void *)android_mtp_MtpDevice_get_object_info}, + {"native_get_object", "(II)[B",(void *)android_mtp_MtpDevice_get_object}, + {"native_get_thumbnail", "(I)[B",(void *)android_mtp_MtpDevice_get_thumbnail}, + {"native_delete_object", "(I)Z", (void *)android_mtp_MtpDevice_delete_object}, + {"native_get_parent", "(I)J", (void *)android_mtp_MtpDevice_get_parent}, + {"native_get_storage_id", "(I)J", (void *)android_mtp_MtpDevice_get_storage_id}, + {"native_import_file", "(ILjava/lang/String;)Z", + (void *)android_mtp_MtpDevice_import_file}, +}; + +static const char* const kClassPathName = "android/mtp/MtpDevice"; + +int register_android_mtp_MtpDevice(JNIEnv *env) +{ + jclass clazz; + + LOGD("register_android_mtp_MtpDevice\n"); + + clazz = env->FindClass("android/mtp/MtpDeviceInfo"); + if (clazz == NULL) { + LOGE("Can't find android/mtp/MtpDeviceInfo"); + return -1; + } + constructor_deviceInfo = env->GetMethodID(clazz, "<init>", "()V"); + if (constructor_deviceInfo == NULL) { + LOGE("Can't find android/mtp/MtpDeviceInfo constructor"); + return -1; + } + field_deviceInfo_manufacturer = env->GetFieldID(clazz, "mManufacturer", "Ljava/lang/String;"); + if (field_deviceInfo_manufacturer == NULL) { + LOGE("Can't find MtpDeviceInfo.mManufacturer"); + return -1; + } + field_deviceInfo_model = env->GetFieldID(clazz, "mModel", "Ljava/lang/String;"); + if (field_deviceInfo_model == NULL) { + LOGE("Can't find MtpDeviceInfo.mModel"); + return -1; + } + field_deviceInfo_version = env->GetFieldID(clazz, "mVersion", "Ljava/lang/String;"); + if (field_deviceInfo_version == NULL) { + LOGE("Can't find MtpDeviceInfo.mVersion"); + return -1; + } + field_deviceInfo_serialNumber = env->GetFieldID(clazz, "mSerialNumber", "Ljava/lang/String;"); + if (field_deviceInfo_serialNumber == NULL) { + LOGE("Can't find MtpDeviceInfo.mSerialNumber"); + return -1; + } + clazz_deviceInfo = (jclass)env->NewGlobalRef(clazz); + + clazz = env->FindClass("android/mtp/MtpStorageInfo"); + if (clazz == NULL) { + LOGE("Can't find android/mtp/MtpStorageInfo"); + return -1; + } + constructor_storageInfo = env->GetMethodID(clazz, "<init>", "()V"); + if (constructor_storageInfo == NULL) { + LOGE("Can't find android/mtp/MtpStorageInfo constructor"); + return -1; + } + field_storageInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I"); + if (field_storageInfo_storageId == NULL) { + LOGE("Can't find MtpStorageInfo.mStorageId"); + return -1; + } + field_storageInfo_maxCapacity = env->GetFieldID(clazz, "mMaxCapacity", "J"); + if (field_storageInfo_maxCapacity == NULL) { + LOGE("Can't find MtpStorageInfo.mMaxCapacity"); + return -1; + } + field_storageInfo_freeSpace = env->GetFieldID(clazz, "mFreeSpace", "J"); + if (field_storageInfo_freeSpace == NULL) { + LOGE("Can't find MtpStorageInfo.mFreeSpace"); + return -1; + } + field_storageInfo_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;"); + if (field_storageInfo_description == NULL) { + LOGE("Can't find MtpStorageInfo.mDescription"); + return -1; + } + field_storageInfo_volumeIdentifier = env->GetFieldID(clazz, "mVolumeIdentifier", "Ljava/lang/String;"); + if (field_storageInfo_volumeIdentifier == NULL) { + LOGE("Can't find MtpStorageInfo.mVolumeIdentifier"); + return -1; + } + clazz_storageInfo = (jclass)env->NewGlobalRef(clazz); + + clazz = env->FindClass("android/mtp/MtpObjectInfo"); + if (clazz == NULL) { + LOGE("Can't find android/mtp/MtpObjectInfo"); + return -1; + } + constructor_objectInfo = env->GetMethodID(clazz, "<init>", "()V"); + if (constructor_objectInfo == NULL) { + LOGE("Can't find android/mtp/MtpObjectInfo constructor"); + return -1; + } + field_objectInfo_handle = env->GetFieldID(clazz, "mHandle", "I"); + if (field_objectInfo_handle == NULL) { + LOGE("Can't find MtpObjectInfo.mHandle"); + return -1; + } + field_objectInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I"); + if (field_objectInfo_storageId == NULL) { + LOGE("Can't find MtpObjectInfo.mStorageId"); + return -1; + } + field_objectInfo_format = env->GetFieldID(clazz, "mFormat", "I"); + if (field_objectInfo_format == NULL) { + LOGE("Can't find MtpObjectInfo.mFormat"); + return -1; + } + field_objectInfo_protectionStatus = env->GetFieldID(clazz, "mProtectionStatus", "I"); + if (field_objectInfo_protectionStatus == NULL) { + LOGE("Can't find MtpObjectInfo.mProtectionStatus"); + return -1; + } + field_objectInfo_compressedSize = env->GetFieldID(clazz, "mCompressedSize", "I"); + if (field_objectInfo_compressedSize == NULL) { + LOGE("Can't find MtpObjectInfo.mCompressedSize"); + return -1; + } + field_objectInfo_thumbFormat = env->GetFieldID(clazz, "mThumbFormat", "I"); + if (field_objectInfo_thumbFormat == NULL) { + LOGE("Can't find MtpObjectInfo.mThumbFormat"); + return -1; + } + field_objectInfo_thumbCompressedSize = env->GetFieldID(clazz, "mThumbCompressedSize", "I"); + if (field_objectInfo_thumbCompressedSize == NULL) { + LOGE("Can't find MtpObjectInfo.mThumbCompressedSize"); + return -1; + } + field_objectInfo_thumbPixWidth = env->GetFieldID(clazz, "mThumbPixWidth", "I"); + if (field_objectInfo_thumbPixWidth == NULL) { + LOGE("Can't find MtpObjectInfo.mThumbPixWidth"); + return -1; + } + field_objectInfo_thumbPixHeight = env->GetFieldID(clazz, "mThumbPixHeight", "I"); + if (field_objectInfo_thumbPixHeight == NULL) { + LOGE("Can't find MtpObjectInfo.mThumbPixHeight"); + return -1; + } + field_objectInfo_imagePixWidth = env->GetFieldID(clazz, "mImagePixWidth", "I"); + if (field_objectInfo_imagePixWidth == NULL) { + LOGE("Can't find MtpObjectInfo.mImagePixWidth"); + return -1; + } + field_objectInfo_imagePixHeight = env->GetFieldID(clazz, "mImagePixHeight", "I"); + if (field_objectInfo_imagePixHeight == NULL) { + LOGE("Can't find MtpObjectInfo.mImagePixHeight"); + return -1; + } + field_objectInfo_imagePixDepth = env->GetFieldID(clazz, "mImagePixDepth", "I"); + if (field_objectInfo_imagePixDepth == NULL) { + LOGE("Can't find MtpObjectInfo.mImagePixDepth"); + return -1; + } + field_objectInfo_parent = env->GetFieldID(clazz, "mParent", "I"); + if (field_objectInfo_parent == NULL) { + LOGE("Can't find MtpObjectInfo.mParent"); + return -1; + } + field_objectInfo_associationType = env->GetFieldID(clazz, "mAssociationType", "I"); + if (field_objectInfo_associationType == NULL) { + LOGE("Can't find MtpObjectInfo.mAssociationType"); + return -1; + } + field_objectInfo_associationDesc = env->GetFieldID(clazz, "mAssociationDesc", "I"); + if (field_objectInfo_associationDesc == NULL) { + LOGE("Can't find MtpObjectInfo.mAssociationDesc"); + return -1; + } + field_objectInfo_sequenceNumber = env->GetFieldID(clazz, "mSequenceNumber", "I"); + if (field_objectInfo_sequenceNumber == NULL) { + LOGE("Can't find MtpObjectInfo.mSequenceNumber"); + return -1; + } + field_objectInfo_name = env->GetFieldID(clazz, "mName", "Ljava/lang/String;"); + if (field_objectInfo_name == NULL) { + LOGE("Can't find MtpObjectInfo.mName"); + return -1; + } + field_objectInfo_dateCreated = env->GetFieldID(clazz, "mDateCreated", "J"); + if (field_objectInfo_dateCreated == NULL) { + LOGE("Can't find MtpObjectInfo.mDateCreated"); + return -1; + } + field_objectInfo_dateModified = env->GetFieldID(clazz, "mDateModified", "J"); + if (field_objectInfo_dateModified == NULL) { + LOGE("Can't find MtpObjectInfo.mDateModified"); + return -1; + } + field_objectInfo_keywords = env->GetFieldID(clazz, "mKeywords", "Ljava/lang/String;"); + if (field_objectInfo_keywords == NULL) { + LOGE("Can't find MtpObjectInfo.mKeywords"); + return -1; + } + clazz_objectInfo = (jclass)env->NewGlobalRef(clazz); + + clazz = env->FindClass("android/mtp/MtpDevice"); + if (clazz == NULL) { + LOGE("Can't find android/mtp/MtpDevice"); + return -1; + } + field_context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (field_context == NULL) { + LOGE("Can't find MtpDevice.mNativeContext"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, + "android/mtp/MtpDevice", gMethods, NELEM(gMethods)); +} diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp index c23169a863bb..23081f868fcb 100755 --- a/media/jni/mediaeditor/VideoEditorMain.cpp +++ b/media/jni/mediaeditor/VideoEditorMain.cpp @@ -208,7 +208,7 @@ videoEditor_populateSettings( jobject object, jobject audioSettingObject); -static void videoEditor_stopPreview(JNIEnv* pEnv, +static int videoEditor_stopPreview(JNIEnv* pEnv, jobject thiz); static jobject @@ -283,8 +283,8 @@ static JNINativeMethod gManualEditMethods[] = { {"nativeRenderMediaItemPreviewFrame", "(Landroid/view/Surface;Ljava/lang/String;IIIIJ)I", (int *)videoEditor_renderMediaItemPreviewFrame }, - {"nativeStopPreview", "()V", - (void *)videoEditor_stopPreview }, + {"nativeStopPreview", "()I", + (int *)videoEditor_stopPreview }, {"stopEncoding", "()V", (void *)videoEditor_stopEncoding }, {"release", "()V", @@ -444,7 +444,7 @@ static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType, pContext->mOverlayRenderingMode = pContext->pEditSettings->\ pClipList[pCurrEditInfo->clipIndex]->xVSS.MediaRendering; - LOGI("rendering mode %d ", pContext->mOverlayRenderingMode); + LOGV("rendering mode %d ", pContext->mOverlayRenderingMode); } @@ -482,11 +482,13 @@ static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType, pContext->pVM->DetachCurrentThread(); } -static void videoEditor_stopPreview(JNIEnv* pEnv, +static int videoEditor_stopPreview(JNIEnv* pEnv, jobject thiz) { ManualEditContext* pContext = M4OSA_NULL; bool needToBeLoaded = true; + M4OSA_UInt32 lastProgressTimeMs = 0; + // Get the context. pContext = (ManualEditContext*)videoEditClasses_getContext(&needToBeLoaded, pEnv, thiz); @@ -495,12 +497,14 @@ static void videoEditor_stopPreview(JNIEnv* pEnv, videoEditJava_checkAndThrowIllegalStateException(&needToBeLoaded, pEnv, (M4OSA_NULL == pContext), "not initialized"); - pContext->mPreviewController->stopPreview(); + lastProgressTimeMs = pContext->mPreviewController->stopPreview(); if (pContext->mOverlayFileName != NULL) { M4OSA_free((M4OSA_MemAddr32)pContext->mOverlayFileName); pContext->mOverlayFileName = NULL; } + + return lastProgressTimeMs; } static void videoEditor_clearSurface(JNIEnv* pEnv, @@ -653,7 +657,7 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, } for (i = 0; i < uiNumberOfClipsInStoryBoard; i++) { - if (timeMs < (iIncrementedDuration + + if (timeMs <= (iIncrementedDuration + (pContext->pEditSettings->pClipList[i]->uiEndCutTime - pContext->pEditSettings->pClipList[i]->uiBeginCutTime))) { @@ -696,6 +700,7 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, pContext->pEditSettings->pClipList[iCurrentClipIndex]->ClipProperties.uiVideoHeight, pContext->pEditSettings->pClipList[iCurrentClipIndex]->ClipProperties.uiVideoWidth, (M4OSA_Void **)&frameStr.pBuffer); + tnTimeMs = (M4OSA_UInt32)timeMs; } else { /* Handle 3gp/mp4 Clips here */ /* get thumbnail*/ @@ -1053,6 +1058,10 @@ static int videoEditor_renderMediaItemPreviewFrame(JNIEnv* pEnv, ThumbnailClose(tnContext); + if (pString != NULL) { + pEnv->ReleaseStringUTFChars(filePath, pString); + } + return timeMs; } @@ -1093,6 +1102,13 @@ int videoEditor_generateAudioRawFile( JNIEnv* pEnv, result = videoEditor_generateAudio( pEnv, pContext, (M4OSA_Char*)pInputFile, (M4OSA_Char*)pStringOutPCMFilePath); + if (pInputFile != NULL) { + pEnv->ReleaseStringUTFChars(infilePath, pInputFile); + } + if (pStringOutPCMFilePath != NULL) { + pEnv->ReleaseStringUTFChars(pcmfilePath, pStringOutPCMFilePath); + } + return result; } @@ -1458,7 +1474,8 @@ videoEditor_populateSettings( bool needToBeLoaded = true; ManualEditContext* pContext = M4OSA_NULL; M4OSA_ERR result = M4NO_ERROR; - jstring str = M4OSA_NULL; + jstring strPath = M4OSA_NULL; + jstring strPCMPath = M4OSA_NULL; jobjectArray propertiesClipsArray = M4OSA_NULL; jobject properties = M4OSA_NULL; jint* bitmapArray = M4OSA_NULL; @@ -1470,6 +1487,7 @@ videoEditor_populateSettings( int nbOverlays = 0; int i,j = 0; int *pOverlayIndex = M4OSA_NULL; + M4OSA_Char* pTempChar = M4OSA_NULL; // Add a code marker (the condition must always be true). ADD_CODE_MARKER_FUN(NULL != pEnv) @@ -1797,20 +1815,63 @@ videoEditor_populateSettings( pContext->mAudioSettings->fileType = pEnv->GetIntField(audioSettingObject,fid); M4OSA_TRACE1_1("fileType = %d",pContext->mAudioSettings->fileType); + + /* free previous allocations , if any */ + if (pContext->mAudioSettings->pFile != NULL) { + M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pFile); + pContext->mAudioSettings->pFile = M4OSA_NULL; + } + if (pContext->mAudioSettings->pPCMFilePath != NULL) { + M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pPCMFilePath); + pContext->mAudioSettings->pPCMFilePath = M4OSA_NULL; + } + fid = pEnv->GetFieldID(audioSettingClazz,"pFile","Ljava/lang/String;"); - str = (jstring)pEnv->GetObjectField(audioSettingObject,fid); - pContext->mAudioSettings->pFile - = (M4OSA_Char*)pEnv->GetStringUTFChars(str, M4OSA_NULL); + strPath = (jstring)pEnv->GetObjectField(audioSettingObject,fid); + pTempChar = (M4OSA_Char*)pEnv->GetStringUTFChars(strPath, M4OSA_NULL); + if (pTempChar != NULL) { + pContext->mAudioSettings->pFile = (M4OSA_Char*) M4OSA_malloc( + (M4OSA_UInt32)(strlen((const char*)pTempChar))+1 /* +1 for NULL termination */, 0, + (M4OSA_Char*)"strPath allocation " ); + if (pContext->mAudioSettings->pFile != M4OSA_NULL) { + M4OSA_memcpy((M4OSA_Int8 *)pContext->mAudioSettings->pFile , + (M4OSA_Int8 *)pTempChar , strlen((const char*)pTempChar)); + ((M4OSA_Int8 *)(pContext->mAudioSettings->pFile))[strlen((const char*)pTempChar)] = '\0'; + pEnv->ReleaseStringUTFChars(strPath,(const char *)pTempChar); + } else { + pEnv->ReleaseStringUTFChars(strPath,(const char *)pTempChar); + VIDEOEDIT_LOG_ERROR(ANDROID_LOG_INFO, "VIDEO_EDITOR", + "regenerateAudio() Malloc failed for pContext->mAudioSettings->pFile "); + videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, + M4OSA_TRUE, M4ERR_ALLOC); + goto videoEditor_populateSettings_cleanup; + } + } M4OSA_TRACE1_1("file name = %s",pContext->mAudioSettings->pFile); VIDEOEDIT_LOG_API(ANDROID_LOG_INFO, "VIDEOEDITOR", "regenerateAudio() file name = %s",\ pContext->mAudioSettings->pFile); fid = pEnv->GetFieldID(audioSettingClazz,"pcmFilePath","Ljava/lang/String;"); - str = (jstring)pEnv->GetObjectField(audioSettingObject,fid); - - pContext->mAudioSettings->pPCMFilePath = - (M4OSA_Char*)pEnv->GetStringUTFChars(str, M4OSA_NULL); - + strPCMPath = (jstring)pEnv->GetObjectField(audioSettingObject,fid); + pTempChar = (M4OSA_Char*)pEnv->GetStringUTFChars(strPCMPath, M4OSA_NULL); + if (pTempChar != NULL) { + pContext->mAudioSettings->pPCMFilePath = (M4OSA_Char*) M4OSA_malloc( + (M4OSA_UInt32)(strlen((const char*)pTempChar))+1 /* +1 for NULL termination */, 0, + (M4OSA_Char*)"strPCMPath allocation " ); + if (pContext->mAudioSettings->pPCMFilePath != M4OSA_NULL) { + M4OSA_memcpy((M4OSA_Int8 *)pContext->mAudioSettings->pPCMFilePath , + (M4OSA_Int8 *)pTempChar , strlen((const char*)pTempChar)); + ((M4OSA_Int8 *)(pContext->mAudioSettings->pPCMFilePath))[strlen((const char*)pTempChar)] = '\0'; + pEnv->ReleaseStringUTFChars(strPCMPath,(const char *)pTempChar); + } else { + pEnv->ReleaseStringUTFChars(strPCMPath,(const char *)pTempChar); + VIDEOEDIT_LOG_ERROR(ANDROID_LOG_INFO, "VIDEO_EDITOR", + "regenerateAudio() Malloc failed for pContext->mAudioSettings->pPCMFilePath "); + videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, + M4OSA_TRUE, M4ERR_ALLOC); + goto videoEditor_populateSettings_cleanup; + } + } VIDEOEDIT_LOG_API(ANDROID_LOG_INFO, "VIDEOEDITOR", "pPCMFilePath -- %s ",\ pContext->mAudioSettings->pPCMFilePath); @@ -1861,6 +1922,7 @@ videoEditor_populateSettings( } else { if (pContext->mAudioSettings != M4OSA_NULL) { pContext->mAudioSettings->pFile = M4OSA_NULL; + pContext->mAudioSettings->pPCMFilePath = M4OSA_NULL; pContext->mAudioSettings->bRemoveOriginal = 0; pContext->mAudioSettings->uiNbChannels = 0; pContext->mAudioSettings->uiSamplingFrequency = 0; @@ -1869,7 +1931,7 @@ videoEditor_populateSettings( pContext->mAudioSettings->uiAddVolume = 0; pContext->mAudioSettings->beginCutMs = 0; pContext->mAudioSettings->endCutMs = 0; - pContext->mAudioSettings->fileType = 0; + pContext->mAudioSettings->fileType = 0; pContext->mAudioSettings->bLoop = 0; pContext->mAudioSettings->uiInDucking_lowVolume = 0; pContext->mAudioSettings->bInDucking_enable = 0; @@ -2483,6 +2545,7 @@ videoEditor_init( (M4OSA_NULL == pContext->mAudioSettings), "not initialized"); pContext->mAudioSettings->pFile = M4OSA_NULL; + pContext->mAudioSettings->pPCMFilePath = M4OSA_NULL; pContext->mAudioSettings->bRemoveOriginal = 0; pContext->mAudioSettings->uiNbChannels = 0; pContext->mAudioSettings->uiSamplingFrequency = 0; @@ -2974,6 +3037,15 @@ videoEditor_release( pContext->mPreviewController = M4OSA_NULL; } + if (pContext->mAudioSettings->pFile != NULL) { + M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pFile); + pContext->mAudioSettings->pFile = M4OSA_NULL; + } + if (pContext->mAudioSettings->pPCMFilePath != NULL) { + M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pPCMFilePath); + pContext->mAudioSettings->pPCMFilePath = M4OSA_NULL; + } + // Free the context. if(pContext->mAudioSettings != M4OSA_NULL) { @@ -3350,6 +3422,10 @@ static int videoEditor_generateAudioWaveFormSync (JNIEnv* pEnv, jobject thiz, pEnv->ReleaseStringUTFChars(outGraphfilePath, pStringOutAudioGraphFile); } + if (pPCMFilePath != NULL) { + pEnv->ReleaseStringUTFChars(pcmfilePath, pPCMFilePath); + } + VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", "videoEditor_generateAudioWaveFormSync pContext->bSkipState "); diff --git a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp index 014cd95a8812..73a7c9c8a272 100755 --- a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp +++ b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp @@ -122,12 +122,26 @@ jobject videoEditProp_getProperties( &gotten, pEnv, file, NULL, M4OSA_NULL); result = M4OSA_fileReadOpen(&context, (M4OSA_Void*)pFile, M4OSA_kFileRead); + + if(M4NO_ERROR != result) { + // Free the file path. + videoEditOsal_free(pFile); + pFile = M4OSA_NULL; + } + videoEditJava_checkAndThrowIllegalArgumentException(&gotten, pEnv, (M4NO_ERROR != result), "file not found"); - if(M4NO_ERROR != result) - return(properties); - result = M4OSA_fileReadClose(context); - context = M4OSA_NULL; + + // Close the file and free the file context + if (context != NULL) { + result = M4OSA_fileReadClose(context); + context = M4OSA_NULL; + } + + // Return if Error + if (M4NO_ERROR != result) { + return (properties); // NULL + } // Check if the file path is valid. if (gotten) diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp index 26c5aca56b25..9097e20ee3c1 100755 --- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp +++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp @@ -166,7 +166,7 @@ enum { REVERB_VOLUME_RAMP, }; -#define REVERB_DEFAULT_PRESET REVERB_PRESET_MEDIUMROOM +#define REVERB_DEFAULT_PRESET REVERB_PRESET_NONE #define REVERB_SEND_LEVEL (0x0C00) // 0.75 in 4.12 format diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 9d9b3c08ac9d..2f694ba59cba 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -668,6 +668,13 @@ uint32_t AudioSystem::getStrategyForStream(AudioSystem::stream_type stream) return aps->getStrategyForStream(stream); } +uint32_t AudioSystem::getDevicesForStream(AudioSystem::stream_type stream) +{ + const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return 0; + return aps->getDevicesForStream(stream); +} + audio_io_handle_t AudioSystem::getOutputForEffect(effect_descriptor_t *desc) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index 457f7ed7af5a..b89a27891fca 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -49,7 +49,8 @@ enum { GET_OUTPUT_FOR_EFFECT, REGISTER_EFFECT, UNREGISTER_EFFECT, - IS_STREAM_ACTIVE + IS_STREAM_ACTIVE, + GET_DEVICES_FOR_STREAM, }; class BpAudioPolicyService : public BpInterface<IAudioPolicyService> @@ -263,6 +264,15 @@ public: return reply.readInt32(); } + virtual uint32_t getDevicesForStream(AudioSystem::stream_type stream) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32(static_cast <uint32_t>(stream)); + remote()->transact(GET_DEVICES_FOR_STREAM, data, &reply); + return (uint32_t) reply.readInt32(); + } + virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc) { Parcel data, reply; @@ -495,6 +505,14 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } break; + case GET_DEVICES_FOR_STREAM: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + AudioSystem::stream_type stream = + static_cast <AudioSystem::stream_type>(data.readInt32()); + reply->writeInt32(static_cast <int>(getDevicesForStream(stream))); + return NO_ERROR; + } break; + case GET_OUTPUT_FOR_EFFECT: { CHECK_INTERFACE(IAudioPolicyService, data, reply); effect_descriptor_t desc; diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 439e4ce37a2f..60bdd6233b91 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -732,18 +732,14 @@ player_type getPlayerType(const char* url) return TEST_PLAYER; } - char value[PROPERTY_VALUE_MAX]; - if (!property_get("media.httplive.disable-nuplayer", value, NULL) - || (strcasecmp(value, "true") && strcmp(value, "1"))) { - if (!strncasecmp("http://", url, 7)) { - size_t len = strlen(url); - if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) { - return NU_PLAYER; - } + if (!strncasecmp("http://", url, 7)) { + size_t len = strlen(url); + if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) { + return NU_PLAYER; + } - if (strstr(url,"m3u8")) { - return NU_PLAYER; - } + if (strstr(url,"m3u8")) { + return NU_PLAYER; } } diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index f134cba9b287..87fdbf248c37 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -244,6 +244,10 @@ status_t StagefrightRecorder::setOutputFileAuxiliary(int fd) { // returns true on success, false otherwise. static bool safe_strtoi64(const char *s, int64_t *val) { char *end; + + // It is lame, but according to man page, we have to set errno to 0 + // before calling strtoll(). + errno = 0; *val = strtoll(s, &end, 10); if (end == s || errno == ERANGE) { diff --git a/media/libstagefright/AACExtractor.cpp b/media/libstagefright/AACExtractor.cpp new file mode 100644 index 000000000000..4203b6e44d67 --- /dev/null +++ b/media/libstagefright/AACExtractor.cpp @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "AACExtractor" +#include <utils/Log.h> + +#include "include/AACExtractor.h" +#include "include/avc_utils.h" + +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <utils/String8.h> + +namespace android { + +#define ADTS_HEADER_LENGTH 7 + +class AACSource : public MediaSource { +public: + AACSource(const sp<DataSource> &source, + const sp<MetaData> &meta, + const Vector<uint64_t> &offset_vector, + int64_t frame_duration_us); + + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + + virtual sp<MetaData> getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options = NULL); + +protected: + virtual ~AACSource(); + +private: + static const size_t kMaxFrameSize; + sp<DataSource> mDataSource; + sp<MetaData> mMeta; + + off64_t mOffset; + int64_t mCurrentTimeUs; + bool mStarted; + MediaBufferGroup *mGroup; + + Vector<uint64_t> mOffsetVector; + int64_t mFrameDurationUs; + + AACSource(const AACSource &); + AACSource &operator=(const AACSource &); +}; + +//////////////////////////////////////////////////////////////////////////////// + +// Returns the sample rate based on the sampling frequency index +uint32_t get_sample_rate(const uint8_t sf_index) +{ + static const uint32_t sample_rates[] = + { + 96000, 88200, 64000, 48000, 44100, 32000, + 24000, 22050, 16000, 12000, 11025, 8000 + }; + + if (sf_index < sizeof(sample_rates) / sizeof(sample_rates[0])) { + return sample_rates[sf_index]; + } + + return 0; +} + +static size_t getFrameSize(const sp<DataSource> &source, off64_t offset) { + size_t frameSize = 0; + + uint8_t syncword[2]; + if (source->readAt(0, &syncword, 2) != 2) { + return 0; + } + if ((syncword[0] != 0xff) || ((syncword[1] & 0xf6) != 0xf0)) { + return 0; + } + + uint8_t protectionAbsent; + if (source->readAt(offset + 1, &protectionAbsent, 1) < 1) { + return 0; + } + protectionAbsent &= 0x1; + + uint8_t header[3]; + if (source->readAt(offset + 3, &header, 3) < 3) { + return 0; + } + + frameSize = (header[0] & 0x3) << 11 | header[1] << 3 | header[2] >> 5; + frameSize += ADTS_HEADER_LENGTH + protectionAbsent ? 0 : 2; + + return frameSize; +} + +AACExtractor::AACExtractor(const sp<DataSource> &source) + : mDataSource(source), + mInitCheck(NO_INIT), + mFrameDurationUs(0) { + String8 mimeType; + float confidence; + if (!SniffAAC(mDataSource, &mimeType, &confidence, NULL)) { + return; + } + + uint8_t profile, sf_index, channel, header[2]; + if (mDataSource->readAt(2, &header, 2) < 2) { + return; + } + + profile = (header[0] >> 6) & 0x3; + sf_index = (header[0] >> 2) & 0xf; + uint32_t sr = get_sample_rate(sf_index); + if (sr == 0) { + return; + } + channel = (header[0] & 0x1) << 2 | (header[1] >> 6); + + mMeta = MakeAACCodecSpecificData(profile, sf_index, channel); + + off64_t offset = 0; + off64_t streamSize, numFrames = 0; + size_t frameSize = 0; + int64_t duration = 0; + + if (mDataSource->getSize(&streamSize) == OK) { + while (offset < streamSize) { + if ((frameSize = getFrameSize(source, offset)) == 0) { + return; + } + + mOffsetVector.push(offset); + + offset += frameSize; + numFrames ++; + } + + // Round up and get the duration + mFrameDurationUs = (1024 * 1000000ll + (sr - 1)) / sr; + duration = numFrames * mFrameDurationUs; + mMeta->setInt64(kKeyDuration, duration); + } + + mInitCheck = OK; +} + +AACExtractor::~AACExtractor() { +} + +sp<MetaData> AACExtractor::getMetaData() { + sp<MetaData> meta = new MetaData; + + if (mInitCheck != OK) { + return meta; + } + + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC_ADTS); + + return meta; +} + +size_t AACExtractor::countTracks() { + return mInitCheck == OK ? 1 : 0; +} + +sp<MediaSource> AACExtractor::getTrack(size_t index) { + if (mInitCheck != OK || index != 0) { + return NULL; + } + + return new AACSource(mDataSource, mMeta, mOffsetVector, mFrameDurationUs); +} + +sp<MetaData> AACExtractor::getTrackMetaData(size_t index, uint32_t flags) { + if (mInitCheck != OK || index != 0) { + return NULL; + } + + return mMeta; +} + +//////////////////////////////////////////////////////////////////////////////// + +// 8192 = 2^13, 13bit AAC frame size (in bytes) +const size_t AACSource::kMaxFrameSize = 8192; + +AACSource::AACSource( + const sp<DataSource> &source, const sp<MetaData> &meta, + const Vector<uint64_t> &offset_vector, + int64_t frame_duration_us) + : mDataSource(source), + mMeta(meta), + mOffset(0), + mCurrentTimeUs(0), + mStarted(false), + mGroup(NULL), + mOffsetVector(offset_vector), + mFrameDurationUs(frame_duration_us) { +} + +AACSource::~AACSource() { + if (mStarted) { + stop(); + } +} + +status_t AACSource::start(MetaData *params) { + CHECK(!mStarted); + + mOffset = 0; + mCurrentTimeUs = 0; + mGroup = new MediaBufferGroup; + mGroup->add_buffer(new MediaBuffer(kMaxFrameSize)); + mStarted = true; + + return OK; +} + +status_t AACSource::stop() { + CHECK(mStarted); + + delete mGroup; + mGroup = NULL; + + mStarted = false; + return OK; +} + +sp<MetaData> AACSource::getFormat() { + return mMeta; +} + +status_t AACSource::read( + MediaBuffer **out, const ReadOptions *options) { + *out = NULL; + + int64_t seekTimeUs; + ReadOptions::SeekMode mode; + if (options && options->getSeekTo(&seekTimeUs, &mode)) { + if (mFrameDurationUs > 0) { + int64_t seekFrame = seekTimeUs / mFrameDurationUs; + mCurrentTimeUs = seekFrame * mFrameDurationUs; + + mOffset = mOffsetVector.itemAt(seekFrame); + } + } + + size_t frameSize, frameSizeWithoutHeader; + if ((frameSize = getFrameSize(mDataSource, mOffset)) == 0) { + return ERROR_END_OF_STREAM; + } + + MediaBuffer *buffer; + status_t err = mGroup->acquire_buffer(&buffer); + if (err != OK) { + return err; + } + + frameSizeWithoutHeader = frameSize - ADTS_HEADER_LENGTH; + if (mDataSource->readAt(mOffset + ADTS_HEADER_LENGTH, buffer->data(), + frameSizeWithoutHeader) != (ssize_t)frameSizeWithoutHeader) { + buffer->release(); + buffer = NULL; + + return ERROR_IO; + } + + buffer->set_range(0, frameSizeWithoutHeader); + buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs); + buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); + + mOffset += frameSize; + mCurrentTimeUs += mFrameDurationUs; + + *out = buffer; + return OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +bool SniffAAC( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) { + uint8_t header[2]; + + if (source->readAt(0, &header, 2) != 2) { + return false; + } + + // ADTS syncword + if ((header[0] == 0xff) && ((header[1] & 0xf6) == 0xf0)) { + *mimeType = MEDIA_MIMETYPE_AUDIO_AAC_ADTS; + *confidence = 0.2; + return true; + } + + return false; +} + +} // namespace android diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index dfb4e000e6bf..505d9d46ab98 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -197,6 +197,9 @@ struct ACodec::ExecutingState : public ACodec::BaseState { // to fill with data. void resume(); + // Returns true iff input and output buffers are in play. + bool active() const { return mActive; } + protected: virtual PortMode getPortMode(OMX_U32 portIndex); virtual bool onMessageReceived(const sp<AMessage> &msg); @@ -205,6 +208,8 @@ protected: virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2); private: + bool mActive; + DISALLOW_EVIL_CONSTRUCTORS(ExecutingState); }; @@ -564,13 +569,17 @@ status_t ACodec::freeBuffersOnPort(OMX_U32 portIndex) { return OK; } -status_t ACodec::freeOutputBuffersOwnedByNativeWindow() { +status_t ACodec::freeOutputBuffersNotOwnedByComponent() { for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); - if (info->mStatus == - BufferInfo::OWNED_BY_NATIVE_WINDOW) { + if (info->mStatus != + BufferInfo::OWNED_BY_COMPONENT) { + // We shouldn't have sent out any buffers to the client at this + // point. + CHECK_NE((int)info->mStatus, (int)BufferInfo::OWNED_BY_DOWNSTREAM); + CHECK_EQ((status_t)OK, freeBuffer(kPortIndexOutput, i)); } } @@ -1195,6 +1204,9 @@ bool ACodec::BaseState::onOMXEvent( } bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) { + LOGV("[%s] onOMXEmptyBufferDone %p", + mCodec->mComponentName.c_str(), bufferID); + BufferInfo *info = mCodec->findBufferByID(kPortIndexInput, bufferID); @@ -1295,7 +1307,7 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { } if (buffer != info->mData) { - if (!(flags & OMX_BUFFERFLAG_CODECCONFIG)) { + if (0 && !(flags & OMX_BUFFERFLAG_CODECCONFIG)) { LOGV("[%s] Needs to copy input data.", mCodec->mComponentName.c_str()); } @@ -1304,6 +1316,9 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { memcpy(info->mData->data(), buffer->data(), buffer->size()); } + LOGV("[%s] calling emptyBuffer %p", + mCodec->mComponentName.c_str(), bufferID); + CHECK_EQ(mCodec->mOMX->emptyBuffer( mCodec->mNode, bufferID, @@ -1320,6 +1335,9 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { LOGV("[%s] Signalling EOS on the input port", mCodec->mComponentName.c_str()); + LOGV("[%s] calling emptyBuffer %p", + mCodec->mComponentName.c_str(), bufferID); + CHECK_EQ(mCodec->mOMX->emptyBuffer( mCodec->mNode, bufferID, @@ -1378,6 +1396,9 @@ bool ACodec::BaseState::onOMXFillBufferDone( int64_t timeUs, void *platformPrivate, void *dataPtr) { + LOGV("[%s] onOMXFillBufferDone %p", + mCodec->mComponentName.c_str(), bufferID); + ssize_t index; BufferInfo *info = mCodec->findBufferByID(kPortIndexOutput, bufferID, &index); @@ -1396,6 +1417,9 @@ bool ACodec::BaseState::onOMXFillBufferDone( { if (rangeLength == 0) { if (!(flags & OMX_BUFFERFLAG_EOS)) { + LOGV("[%s] calling fillBuffer %p", + mCodec->mComponentName.c_str(), info->mBufferID); + CHECK_EQ(mCodec->mOMX->fillBuffer( mCodec->mNode, info->mBufferID), (status_t)OK); @@ -1503,6 +1527,9 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) { info = mCodec->dequeueBufferFromNativeWindow(); } + LOGV("[%s] calling fillBuffer %p", + mCodec->mComponentName.c_str(), info->mBufferID); + CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID), (status_t)OK); @@ -1600,6 +1627,9 @@ void ACodec::UninitializedState::onSetup( mCodec->mOMX = omx; mCodec->mNode = node; + mCodec->mPortEOS[kPortIndexInput] = + mCodec->mPortEOS[kPortIndexOutput] = false; + mCodec->configureCodec(mime.c_str(), msg); sp<RefBase> obj; @@ -1717,7 +1747,8 @@ bool ACodec::IdleToExecutingState::onOMXEvent( //////////////////////////////////////////////////////////////////////////////// ACodec::ExecutingState::ExecutingState(ACodec *codec) - : BaseState(codec) { + : BaseState(codec), + mActive(false) { } ACodec::BaseState::PortMode ACodec::ExecutingState::getPortMode( @@ -1745,6 +1776,9 @@ void ACodec::ExecutingState::submitOutputBuffers() { CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US); } + LOGV("[%s] calling fillBuffer %p", + mCodec->mComponentName.c_str(), info->mBufferID); + CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID), (status_t)OK); @@ -1753,6 +1787,13 @@ void ACodec::ExecutingState::submitOutputBuffers() { } void ACodec::ExecutingState::resume() { + if (mActive) { + LOGV("[%s] We're already active, no need to resume.", + mCodec->mComponentName.c_str()); + + return; + } + submitOutputBuffers(); // Post the first input buffer. @@ -1760,6 +1801,8 @@ void ACodec::ExecutingState::resume() { BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(0); postFillThisBuffer(info); + + mActive = true; } void ACodec::ExecutingState::stateEntered() { @@ -1774,6 +1817,8 @@ bool ACodec::ExecutingState::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatShutdown: { + mActive = false; + CHECK_EQ(mCodec->mOMX->sendCommand( mCodec->mNode, OMX_CommandStateSet, OMX_StateIdle), (status_t)OK); @@ -1786,6 +1831,8 @@ bool ACodec::ExecutingState::onMessageReceived(const sp<AMessage> &msg) { case kWhatFlush: { + mActive = false; + CHECK_EQ(mCodec->mOMX->sendCommand( mCodec->mNode, OMX_CommandFlush, OMX_ALL), (status_t)OK); @@ -1825,10 +1872,7 @@ bool ACodec::ExecutingState::onOMXEvent( OMX_CommandPortDisable, kPortIndexOutput), (status_t)OK); - if (mCodec->mNativeWindow != NULL) { - CHECK_EQ((status_t)OK, - mCodec->freeOutputBuffersOwnedByNativeWindow()); - } + mCodec->freeOutputBuffersNotOwnedByComponent(); mCodec->changeState(mCodec->mOutputPortSettingsChangedState); } else if (data2 == OMX_IndexConfigCommonOutputCrop) { @@ -1876,7 +1920,12 @@ bool ACodec::OutputPortSettingsChangedState::onMessageReceived( switch (msg->what()) { case kWhatFlush: case kWhatShutdown: + case kWhatResume: { + if (msg->what() == kWhatResume) { + LOGV("[%s] Deferring resume", mCodec->mComponentName.c_str()); + } + mCodec->deferMessage(msg); handled = true; break; @@ -1925,7 +1974,10 @@ bool ACodec::OutputPortSettingsChangedState::onOMXEvent( LOGV("[%s] Output port now reenabled.", mCodec->mComponentName.c_str()); - mCodec->mExecutingState->submitOutputBuffers(); + if (mCodec->mExecutingState->active()) { + mCodec->mExecutingState->submitOutputBuffers(); + } + mCodec->changeState(mCodec->mExecutingState); return true; @@ -1992,6 +2044,13 @@ bool ACodec::ExecutingToIdleState::onOMXEvent( return true; } + case OMX_EventPortSettingsChanged: + case OMX_EventBufferFlag: + { + // We're shutting down and don't care about this anymore. + return true; + } + default: return BaseState::onOMXEvent(event, data1, data2); } @@ -2170,6 +2229,23 @@ bool ACodec::FlushingState::onOMXEvent( return true; } + case OMX_EventPortSettingsChanged: + { + sp<AMessage> msg = new AMessage(kWhatOMXMessage, mCodec->id()); + msg->setInt32("type", omx_message::EVENT); + msg->setPointer("node", mCodec->mNode); + msg->setInt32("event", event); + msg->setInt32("data1", data1); + msg->setInt32("data2", data2); + + LOGV("[%s] Deferring OMX_EventPortSettingsChanged", + mCodec->mComponentName.c_str()); + + mCodec->deferMessage(msg); + + return true; + } + default: return BaseState::onOMXEvent(event, data1, data2); } diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp index ac87c29680c8..7eca5e4b9213 100644 --- a/media/libstagefright/AMRExtractor.cpp +++ b/media/libstagefright/AMRExtractor.cpp @@ -35,8 +35,9 @@ class AMRSource : public MediaSource { public: AMRSource(const sp<DataSource> &source, const sp<MetaData> &meta, - size_t frameSize, - bool isWide); + bool isWide, + const off64_t *offset_table, + size_t offset_table_length); virtual status_t start(MetaData *params = NULL); virtual status_t stop(); @@ -52,7 +53,6 @@ protected: private: sp<DataSource> mDataSource; sp<MetaData> mMeta; - size_t mFrameSize; bool mIsWide; off64_t mOffset; @@ -60,6 +60,9 @@ private: bool mStarted; MediaBufferGroup *mGroup; + off64_t mOffsetTable[OFFSET_TABLE_LEN]; + size_t mOffsetTableLength; + AMRSource(const AMRSource &); AMRSource &operator=(const AMRSource &); }; @@ -67,13 +70,25 @@ private: //////////////////////////////////////////////////////////////////////////////// static size_t getFrameSize(bool isWide, unsigned FT) { - static const size_t kFrameSizeNB[8] = { - 95, 103, 118, 134, 148, 159, 204, 244 + static const size_t kFrameSizeNB[16] = { + 95, 103, 118, 134, 148, 159, 204, 244, + 39, 43, 38, 37, // SID + 0, 0, 0, // future use + 0 // no data }; - static const size_t kFrameSizeWB[9] = { - 132, 177, 253, 285, 317, 365, 397, 461, 477 + static const size_t kFrameSizeWB[16] = { + 132, 177, 253, 285, 317, 365, 397, 461, 477, + 40, // SID + 0, 0, 0, 0, // future use + 0, // speech lost + 0 // no data }; + if (FT > 15 || (isWide && FT > 9 && FT < 14) || (!isWide && FT > 11 && FT < 15)) { + LOGE("illegal AMR frame type %d", FT); + return 0; + } + size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT]; // Round up bits to bytes and add 1 for the header byte. @@ -82,9 +97,26 @@ static size_t getFrameSize(bool isWide, unsigned FT) { return frameSize; } +static status_t getFrameSizeByOffset(const sp<DataSource> &source, + off64_t offset, bool isWide, size_t *frameSize) { + uint8_t header; + if (source->readAt(offset, &header, 1) < 1) { + return ERROR_IO; + } + + unsigned FT = (header >> 3) & 0x0f; + + *frameSize = getFrameSize(isWide, FT); + if (*frameSize == 0) { + return ERROR_MALFORMED; + } + return OK; +} + AMRExtractor::AMRExtractor(const sp<DataSource> &source) : mDataSource(source), - mInitCheck(NO_INIT) { + mInitCheck(NO_INIT), + mOffsetTableLength(0) { String8 mimeType; float confidence; if (!SniffAMR(mDataSource, &mimeType, &confidence, NULL)) { @@ -101,25 +133,29 @@ AMRExtractor::AMRExtractor(const sp<DataSource> &source) mMeta->setInt32(kKeyChannelCount, 1); mMeta->setInt32(kKeySampleRate, mIsWide ? 16000 : 8000); - size_t offset = mIsWide ? 9 : 6; - uint8_t header; - if (mDataSource->readAt(offset, &header, 1) != 1) { - return; - } - - unsigned FT = (header >> 3) & 0x0f; - - if (FT > 8 || (!mIsWide && FT > 7)) { - return; - } - - mFrameSize = getFrameSize(mIsWide, FT); - + off64_t offset = mIsWide ? 9 : 6; off64_t streamSize; - if (mDataSource->getSize(&streamSize) == OK) { - off64_t numFrames = streamSize / mFrameSize; + size_t frameSize, numFrames = 0; + int64_t duration = 0; - mMeta->setInt64(kKeyDuration, 20000ll * numFrames); + if (mDataSource->getSize(&streamSize) == OK) { + while (offset < streamSize) { + if (getFrameSizeByOffset(source, offset, mIsWide, &frameSize) != OK) { + return; + } + + if ((numFrames % 50 == 0) && (numFrames / 50 < OFFSET_TABLE_LEN)) { + CHECK_EQ(mOffsetTableLength, numFrames / 50); + mOffsetTable[mOffsetTableLength] = offset - (mIsWide ? 9: 6); + mOffsetTableLength ++; + } + + offset += frameSize; + duration += 20000; // Each frame is 20ms + numFrames ++; + } + + mMeta->setInt64(kKeyDuration, duration); } mInitCheck = OK; @@ -149,7 +185,8 @@ sp<MediaSource> AMRExtractor::getTrack(size_t index) { return NULL; } - return new AMRSource(mDataSource, mMeta, mFrameSize, mIsWide); + return new AMRSource(mDataSource, mMeta, mIsWide, + mOffsetTable, mOffsetTableLength); } sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t flags) { @@ -164,15 +201,18 @@ sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t flags) { AMRSource::AMRSource( const sp<DataSource> &source, const sp<MetaData> &meta, - size_t frameSize, bool isWide) + bool isWide, const off64_t *offset_table, size_t offset_table_length) : mDataSource(source), mMeta(meta), - mFrameSize(frameSize), mIsWide(isWide), mOffset(mIsWide ? 9 : 6), mCurrentTimeUs(0), mStarted(false), - mGroup(NULL) { + mGroup(NULL), + mOffsetTableLength(offset_table_length) { + if (mOffsetTableLength > 0 && mOffsetTableLength <= OFFSET_TABLE_LEN) { + memcpy ((char*)mOffsetTable, (char*)offset_table, sizeof(off64_t) * mOffsetTableLength); + } } AMRSource::~AMRSource() { @@ -214,9 +254,25 @@ status_t AMRSource::read( int64_t seekTimeUs; ReadOptions::SeekMode mode; if (options && options->getSeekTo(&seekTimeUs, &mode)) { + size_t size; int64_t seekFrame = seekTimeUs / 20000ll; // 20ms per frame. mCurrentTimeUs = seekFrame * 20000ll; - mOffset = seekFrame * mFrameSize + (mIsWide ? 9 : 6); + + int index = seekFrame / 50; + if (index >= mOffsetTableLength) { + index = mOffsetTableLength - 1; + } + + mOffset = mOffsetTable[index] + (mIsWide ? 9 : 6); + + for (int i = 0; i< seekFrame - index * 50; i++) { + status_t err; + if ((err = getFrameSizeByOffset(mDataSource, mOffset, + mIsWide, &size)) != OK) { + return err; + } + mOffset += size; + } } uint8_t header; @@ -236,16 +292,11 @@ status_t AMRSource::read( unsigned FT = (header >> 3) & 0x0f; - if (FT > 8 || (!mIsWide && FT > 7)) { - - LOGE("illegal AMR frame type %d", FT); - + size_t frameSize = getFrameSize(mIsWide, FT); + if (frameSize == 0) { return ERROR_MALFORMED; } - size_t frameSize = getFrameSize(mIsWide, FT); - CHECK_EQ(frameSize, mFrameSize); - MediaBuffer *buffer; status_t err = mGroup->acquire_buffer(&buffer); if (err != OK) { diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 2d486e313106..80eb59ffc06e 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -5,6 +5,7 @@ include frameworks/base/media/libstagefright/codecs/common/Config.mk LOCAL_SRC_FILES:= \ ACodec.cpp \ + AACExtractor.cpp \ AMRExtractor.cpp \ AMRWriter.cpp \ AudioPlayer.cpp \ @@ -17,6 +18,7 @@ LOCAL_SRC_FILES:= \ DRMExtractor.cpp \ ESDS.cpp \ FileSource.cpp \ + FLACExtractor.cpp \ HTTPStream.cpp \ JPEGSource.cpp \ MP3Extractor.cpp \ @@ -54,8 +56,10 @@ LOCAL_SRC_FILES:= \ LOCAL_C_INCLUDES:= \ $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/include/media/stagefright/openmax \ + $(TOP)/external/flac/include \ $(TOP)/external/tremolo \ - $(TOP)/frameworks/base/media/libstagefright/rtsp + $(TOP)/frameworks/base/media/libstagefright/rtsp \ + $(TOP)/external/openssl/include \ LOCAL_SHARED_LIBRARIES := \ libbinder \ @@ -69,7 +73,8 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright_yuv \ libcamera_client \ libdrmframework \ - libcrypto + libcrypto \ + libssl LOCAL_STATIC_LIBRARIES := \ libstagefright_color_conversion \ @@ -93,6 +98,7 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_rtsp \ libstagefright_id3 \ libstagefright_g711dec \ + libFLAC \ LOCAL_SHARED_LIBRARIES += \ libstagefright_amrnb_common \ diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index f96df180e179..bbdec024b148 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -18,38 +18,54 @@ #define LOG_TAG "AudioSource" #include <utils/Log.h> -#include <media/stagefright/AudioSource.h> - #include <media/AudioRecord.h> -#include <media/stagefright/MediaBufferGroup.h> -#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/AudioSource.h> +#include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MetaData.h> +#include <media/stagefright/foundation/ADebug.h> #include <cutils/properties.h> #include <stdlib.h> namespace android { +static void AudioRecordCallbackFunction(int event, void *user, void *info) { + AudioSource *source = (AudioSource *) user; + switch (event) { + case AudioRecord::EVENT_MORE_DATA: { + source->dataCallbackTimestamp(*((AudioRecord::Buffer *) info), systemTime() / 1000); + break; + } + case AudioRecord::EVENT_OVERRUN: { + LOGW("AudioRecord reported overrun!"); + break; + } + default: + // does nothing + break; + } +} + AudioSource::AudioSource( int inputSource, uint32_t sampleRate, uint32_t channels) : mStarted(false), - mCollectStats(false), + mSampleRate(sampleRate), mPrevSampleTimeUs(0), - mTotalLostFrames(0), - mPrevLostBytes(0), - mGroup(NULL) { + mNumFramesReceived(0), + mNumClientOwnedBuffers(0) { LOGV("sampleRate: %d, channels: %d", sampleRate, channels); CHECK(channels == 1 || channels == 2); uint32_t flags = AudioRecord::RECORD_AGC_ENABLE | AudioRecord::RECORD_NS_ENABLE | AudioRecord::RECORD_IIR_ENABLE; - mRecord = new AudioRecord( inputSource, sampleRate, AudioSystem::PCM_16_BIT, channels > 1? AudioSystem::CHANNEL_IN_STEREO: AudioSystem::CHANNEL_IN_MONO, 4 * kMaxBufferSize / sizeof(int16_t), /* Enable ping-pong buffers */ - flags); + flags, + AudioRecordCallbackFunction, + this); mInitCheck = mRecord->initCheck(); } @@ -68,6 +84,7 @@ status_t AudioSource::initCheck() const { } status_t AudioSource::start(MetaData *params) { + Mutex::Autolock autoLock(mLock); if (mStarted) { return UNKNOWN_ERROR; } @@ -76,12 +93,6 @@ status_t AudioSource::start(MetaData *params) { return NO_INIT; } - char value[PROPERTY_VALUE_MAX]; - if (property_get("media.stagefright.record-stats", value, NULL) - && (!strcmp(value, "1") || !strcasecmp(value, "true"))) { - mCollectStats = true; - } - mTrackMaxAmplitude = false; mMaxAmplitude = 0; mInitialReadTimeUs = 0; @@ -92,9 +103,6 @@ status_t AudioSource::start(MetaData *params) { } status_t err = mRecord->start(); if (err == OK) { - mGroup = new MediaBufferGroup; - mGroup->add_buffer(new MediaBuffer(kMaxBufferSize)); - mStarted = true; } else { delete mRecord; @@ -105,7 +113,25 @@ status_t AudioSource::start(MetaData *params) { return err; } +void AudioSource::releaseQueuedFrames_l() { + LOGV("releaseQueuedFrames_l"); + List<MediaBuffer *>::iterator it; + while (!mBuffersReceived.empty()) { + it = mBuffersReceived.begin(); + (*it)->release(); + mBuffersReceived.erase(it); + } +} + +void AudioSource::waitOutstandingEncodingFrames_l() { + LOGV("waitOutstandingEncodingFrames_l: %lld", mNumClientOwnedBuffers); + while (mNumClientOwnedBuffers > 0) { + mFrameEncodingCompletionCondition.wait(mLock); + } +} + status_t AudioSource::stop() { + Mutex::Autolock autoLock(mLock); if (!mStarted) { return UNKNOWN_ERROR; } @@ -114,29 +140,23 @@ status_t AudioSource::stop() { return NO_INIT; } - mRecord->stop(); - - delete mGroup; - mGroup = NULL; - mStarted = false; - - if (mCollectStats) { - LOGI("Total lost audio frames: %lld", - mTotalLostFrames + (mPrevLostBytes >> 1)); - } + mRecord->stop(); + waitOutstandingEncodingFrames_l(); + releaseQueuedFrames_l(); return OK; } sp<MetaData> AudioSource::getFormat() { + Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { return 0; } sp<MetaData> meta = new MetaData; meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); - meta->setInt32(kKeySampleRate, mRecord->getSampleRate()); + meta->setInt32(kKeySampleRate, mSampleRate); meta->setInt32(kKeyChannelCount, mRecord->channelCount()); meta->setInt32(kKeyMaxInputSize, kMaxBufferSize); @@ -177,122 +197,131 @@ void AudioSource::rampVolume( status_t AudioSource::read( MediaBuffer **out, const ReadOptions *options) { + Mutex::Autolock autoLock(mLock); + *out = NULL; if (mInitCheck != OK) { return NO_INIT; } - int64_t readTimeUs = systemTime() / 1000; - *out = NULL; - - MediaBuffer *buffer; - CHECK_EQ(mGroup->acquire_buffer(&buffer), OK); - - int err = 0; - if (mStarted) { - - uint32_t numFramesRecorded; - mRecord->getPosition(&numFramesRecorded); + while (mStarted && mBuffersReceived.empty()) { + mFrameAvailableCondition.wait(mLock); + } + if (!mStarted) { + return OK; + } + MediaBuffer *buffer = *mBuffersReceived.begin(); + mBuffersReceived.erase(mBuffersReceived.begin()); + ++mNumClientOwnedBuffers; + buffer->setObserver(this); + buffer->add_ref(); + + // Mute/suppress the recording sound + int64_t timeUs; + CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); + int64_t elapsedTimeUs = timeUs - mStartTimeUs; + if (elapsedTimeUs < kAutoRampStartUs) { + memset((uint8_t *) buffer->data(), 0, buffer->range_length()); + } else if (elapsedTimeUs < kAutoRampStartUs + kAutoRampDurationUs) { + int32_t autoRampDurationFrames = + (kAutoRampDurationUs * mSampleRate + 500000LL) / 1000000LL; + + int32_t autoRampStartFrames = + (kAutoRampStartUs * mSampleRate + 500000LL) / 1000000LL; + + int32_t nFrames = mNumFramesReceived - autoRampStartFrames; + rampVolume(nFrames, autoRampDurationFrames, + (uint8_t *) buffer->data(), buffer->range_length()); + } + // Track the max recording signal amplitude. + if (mTrackMaxAmplitude) { + trackMaxAmplitude( + (int16_t *) buffer->data(), buffer->range_length() >> 1); + } - if (numFramesRecorded == 0 && mPrevSampleTimeUs == 0) { - mInitialReadTimeUs = readTimeUs; - // Initial delay - if (mStartTimeUs > 0) { - mStartTimeUs = readTimeUs - mStartTimeUs; - } else { - // Assume latency is constant. - mStartTimeUs += mRecord->latency() * 1000; - } - mPrevSampleTimeUs = mStartTimeUs; - } + *out = buffer; + return OK; +} - uint32_t sampleRate = mRecord->getSampleRate(); +void AudioSource::signalBufferReturned(MediaBuffer *buffer) { + LOGV("signalBufferReturned: %p", buffer->data()); + Mutex::Autolock autoLock(mLock); + --mNumClientOwnedBuffers; + buffer->setObserver(0); + buffer->release(); + mFrameEncodingCompletionCondition.signal(); + return; +} - // Insert null frames when lost frames are detected. - int64_t timestampUs = mPrevSampleTimeUs; - uint32_t numLostBytes = mRecord->getInputFramesLost() << 1; - numLostBytes += mPrevLostBytes; -#if 0 - // Simulate lost frames - numLostBytes = ((rand() * 1.0 / RAND_MAX)) * 2 * kMaxBufferSize; - numLostBytes &= 0xFFFFFFFE; // Alignment requirement +status_t AudioSource::dataCallbackTimestamp( + const AudioRecord::Buffer& audioBuffer, int64_t timeUs) { + LOGV("dataCallbackTimestamp: %lld us", timeUs); + Mutex::Autolock autoLock(mLock); + if (!mStarted) { + LOGW("Spurious callback from AudioRecord. Drop the audio data."); + return OK; + } - // Reduce the chance to lose - if (rand() * 1.0 / RAND_MAX >= 0.05) { - numLostBytes = 0; - } -#endif - if (numLostBytes > 0) { - if (numLostBytes > kMaxBufferSize) { - mPrevLostBytes = numLostBytes - kMaxBufferSize; - numLostBytes = kMaxBufferSize; - } else { - mPrevLostBytes = 0; - } - - CHECK_EQ(numLostBytes & 1, 0); - timestampUs += ((1000000LL * (numLostBytes >> 1)) + - (sampleRate >> 1)) / sampleRate; - - CHECK(timestampUs > mPrevSampleTimeUs); - if (mCollectStats) { - mTotalLostFrames += (numLostBytes >> 1); - } - memset(buffer->data(), 0, numLostBytes); - buffer->set_range(0, numLostBytes); - if (numFramesRecorded == 0) { - buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs); - } - buffer->meta_data()->setInt64(kKeyTime, mStartTimeUs + mPrevSampleTimeUs); - buffer->meta_data()->setInt64(kKeyDriftTime, readTimeUs - mInitialReadTimeUs); - mPrevSampleTimeUs = timestampUs; - *out = buffer; - return OK; - } + // Drop retrieved and previously lost audio data. + if (mNumFramesReceived == 0 && timeUs < mStartTimeUs) { + mRecord->getInputFramesLost(); + LOGV("Drop audio data at %lld/%lld us", timeUs, mStartTimeUs); + return OK; + } - ssize_t n = mRecord->read(buffer->data(), buffer->size()); - if (n < 0) { - buffer->release(); - return (status_t)n; + if (mNumFramesReceived == 0 && mPrevSampleTimeUs == 0) { + mInitialReadTimeUs = timeUs; + // Initial delay + if (mStartTimeUs > 0) { + mStartTimeUs = timeUs - mStartTimeUs; + } else { + // Assume latency is constant. + mStartTimeUs += mRecord->latency() * 1000; } + mPrevSampleTimeUs = mStartTimeUs; + } - int64_t recordDurationUs = (1000000LL * n >> 1) / sampleRate; - timestampUs += recordDurationUs; - - if (mPrevSampleTimeUs - mStartTimeUs < kAutoRampStartUs) { - // Mute the initial video recording signal - memset((uint8_t *) buffer->data(), 0, n); - } else if (mPrevSampleTimeUs - mStartTimeUs < kAutoRampStartUs + kAutoRampDurationUs) { - int32_t autoRampDurationFrames = - (kAutoRampDurationUs * sampleRate + 500000LL) / 1000000LL; - - int32_t autoRampStartFrames = - (kAutoRampStartUs * sampleRate + 500000LL) / 1000000LL; + int64_t timestampUs = mPrevSampleTimeUs; - int32_t nFrames = numFramesRecorded - autoRampStartFrames; - rampVolume(nFrames, autoRampDurationFrames, (uint8_t *) buffer->data(), n); - } - if (mTrackMaxAmplitude) { - trackMaxAmplitude((int16_t *) buffer->data(), n >> 1); - } + size_t numLostBytes = 0; + if (mNumFramesReceived > 0) { // Ignore earlier frame lost + // getInputFramesLost() returns the number of lost frames. + // Convert number of frames lost to number of bytes lost. + numLostBytes = mRecord->getInputFramesLost() * mRecord->frameSize(); + } - if (numFramesRecorded == 0) { - buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs); + CHECK_EQ(numLostBytes & 1, 0u); + CHECK_EQ(audioBuffer.size & 1, 0u); + size_t bufferSize = numLostBytes + audioBuffer.size; + MediaBuffer *buffer = new MediaBuffer(bufferSize); + if (numLostBytes > 0) { + memset(buffer->data(), 0, numLostBytes); + memcpy((uint8_t *) buffer->data() + numLostBytes, + audioBuffer.i16, audioBuffer.size); + } else { + if (audioBuffer.size == 0) { + LOGW("Nothing is available from AudioRecord callback buffer"); + buffer->release(); + return OK; } + memcpy((uint8_t *) buffer->data(), + audioBuffer.i16, audioBuffer.size); + } - buffer->meta_data()->setInt64(kKeyTime, mStartTimeUs + mPrevSampleTimeUs); - buffer->meta_data()->setInt64(kKeyDriftTime, readTimeUs - mInitialReadTimeUs); - CHECK(timestampUs > mPrevSampleTimeUs); - mPrevSampleTimeUs = timestampUs; - LOGV("initial delay: %lld, sample rate: %d, timestamp: %lld", - mStartTimeUs, sampleRate, timestampUs); - - buffer->set_range(0, n); + buffer->set_range(0, bufferSize); + timestampUs += ((1000000LL * (bufferSize >> 1)) + + (mSampleRate >> 1)) / mSampleRate; - *out = buffer; - return OK; + if (mNumFramesReceived == 0) { + buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs); } + buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs); + buffer->meta_data()->setInt64(kKeyDriftTime, timeUs - mInitialReadTimeUs); + mPrevSampleTimeUs = timestampUs; + mNumFramesReceived += buffer->range_length() / sizeof(int16_t); + mBuffersReceived.push_back(buffer); + mFrameAvailableCondition.signal(); return OK; } diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 89b3dab46d73..b1d3630c67df 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -27,11 +27,6 @@ #include "include/ThrottledSource.h" #include "include/MPEG2TSExtractor.h" -#include "ARTPSession.h" -#include "APacketSource.h" -#include "ASessionDescription.h" -#include "UDPPusher.h" - #include <binder/IPCThreadState.h> #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/foundation/ADebug.h> @@ -49,7 +44,6 @@ #include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/foundation/AMessage.h> -#include "include/LiveSession.h" #define USE_SURFACE_ALLOC 1 #define FRAME_DROP_FREQ 7 @@ -58,6 +52,7 @@ namespace android { static int64_t kLowWaterMarkUs = 2000000ll; // 2secs static int64_t kHighWaterMarkUs = 10000000ll; // 10secs +static int64_t kHighWaterMarkRTSPUs = 4000000ll; // 4secs static const size_t kLowWaterMarkBytes = 40000; static const size_t kHighWaterMarkBytes = 200000; @@ -237,17 +232,6 @@ status_t AwesomePlayer::setDataSource_l( mUri = uri; - if (!strncmp("http://", uri, 7)) { - // Hack to support http live. - - size_t len = strlen(uri); - if (!strcasecmp(&uri[len - 5], ".m3u8") - || strstr(&uri[7], "m3u8") != NULL) { - mUri = "httplive://"; - mUri.append(&uri[7]); - } - } - if (headers) { mUriHeaders = *headers; } @@ -291,9 +275,11 @@ status_t AwesomePlayer::setDataSource_l( } dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient); - if (mDecryptHandle != NULL - && RightsStatus::RIGHTS_VALID != mDecryptHandle->status) { - notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE); + if (mDecryptHandle != NULL) { + CHECK(mDrmManagerClient); + if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) { + notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE); + } } return setDataSource_l(extractor); @@ -389,7 +375,6 @@ void AwesomePlayer::reset_l() { if (mDecryptHandle != NULL) { mDrmManagerClient->setPlaybackStatus(mDecryptHandle, Playback::STOP, 0); - mDrmManagerClient->closeDecryptSession(mDecryptHandle); mDecryptHandle = NULL; mDrmManagerClient = NULL; } @@ -399,6 +384,9 @@ void AwesomePlayer::reset_l() { if (mConnectingDataSource != NULL) { LOGI("interrupting the connection process"); mConnectingDataSource->disconnect(); + } else if (mConnectingRTSPController != NULL) { + LOGI("interrupting the connection process"); + mConnectingRTSPController->disconnect(); } if (mFlags & PREPARING_CONNECTED) { @@ -448,15 +436,6 @@ void AwesomePlayer::reset_l() { mRTSPController.clear(); } - if (mLiveSession != NULL) { - mLiveSession->disconnect(); - mLiveSession.clear(); - } - - mRTPPusher.clear(); - mRTCPPusher.clear(); - mRTPSession.clear(); - if (mVideoSource != NULL) { mVideoSource->stop(); @@ -630,6 +609,9 @@ void AwesomePlayer::onBufferingUpdate() { LOGV("cachedDurationUs = %.2f secs, eos=%d", cachedDurationUs / 1E6, eos); + int64_t highWaterMarkUs = + (mRTSPController != NULL) ? kHighWaterMarkRTSPUs : kHighWaterMarkUs; + if ((mFlags & PLAYING) && !eos && (cachedDurationUs < kLowWaterMarkUs)) { LOGI("cache is running low (%.2f secs) , pausing.", @@ -638,7 +620,7 @@ void AwesomePlayer::onBufferingUpdate() { pause_l(); ensureCacheIsFetching_l(); notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START); - } else if (eos || cachedDurationUs > kHighWaterMarkUs) { + } else if (eos || cachedDurationUs > highWaterMarkUs) { if (mFlags & CACHE_UNDERRUN) { LOGI("cache has filled up (%.2f secs), resuming.", cachedDurationUs / 1E6); @@ -656,35 +638,6 @@ void AwesomePlayer::onBufferingUpdate() { postBufferingEvent_l(); } -void AwesomePlayer::partial_reset_l() { - // Only reset the video renderer and shut down the video decoder. - // Then instantiate a new video decoder and resume video playback. - - mVideoRenderer.clear(); - - if (mVideoBuffer) { - mVideoBuffer->release(); - mVideoBuffer = NULL; - } - - { - mVideoSource->stop(); - - // The following hack is necessary to ensure that the OMX - // component is completely released by the time we may try - // to instantiate it again. - wp<MediaSource> tmp = mVideoSource; - mVideoSource.clear(); - while (tmp.promote() != NULL) { - usleep(1000); - } - IPCThreadState::self()->flushCommands(); - } - - CHECK_EQ((status_t)OK, - initVideoDecoder(OMXCodec::kIgnoreCodecSpecificData)); -} - void AwesomePlayer::onStreamDone() { // Posted whenever any stream finishes playing. @@ -694,21 +647,7 @@ void AwesomePlayer::onStreamDone() { } mStreamDoneEventPending = false; - if (mStreamDoneStatus == INFO_DISCONTINUITY) { - // This special status is returned because an http live stream's - // video stream switched to a different bandwidth at this point - // and future data may have been encoded using different parameters. - // This requires us to shutdown the video decoder and reinstantiate - // a fresh one. - - LOGV("INFO_DISCONTINUITY"); - - CHECK(mVideoSource != NULL); - - partial_reset_l(); - postVideoEvent_l(); - return; - } else if (mStreamDoneStatus != ERROR_END_OF_STREAM) { + if (mStreamDoneStatus != ERROR_END_OF_STREAM) { LOGV("MEDIA_ERROR %d", mStreamDoneStatus); notifyListener_l( @@ -753,6 +692,8 @@ status_t AwesomePlayer::play() { } status_t AwesomePlayer::play_l() { + mFlags &= ~SEEK_PREVIEW; + if (mFlags & PLAYING) { return OK; } @@ -783,34 +724,33 @@ status_t AwesomePlayer::play_l() { mAudioPlayer = new AudioPlayer(mAudioSink, this); mAudioPlayer->setSource(mAudioSource); - // We've already started the MediaSource in order to enable - // the prefetcher to read its data. - status_t err = mAudioPlayer->start( - true /* sourceAlreadyStarted */); + mTimeSource = mAudioPlayer; - if (err != OK) { - delete mAudioPlayer; - mAudioPlayer = NULL; + deferredAudioSeek = true; - mFlags &= ~(PLAYING | FIRST_FRAME); + mWatchForAudioSeekComplete = false; + mWatchForAudioEOS = true; + } + } - if (mDecryptHandle != NULL) { - mDrmManagerClient->setPlaybackStatus(mDecryptHandle, - Playback::STOP, 0); - } + CHECK(!(mFlags & AUDIO_RUNNING)); - return err; - } + if (mVideoSource == NULL) { + status_t err = startAudioPlayer_l(); - mTimeSource = mAudioPlayer; + if (err != OK) { + delete mAudioPlayer; + mAudioPlayer = NULL; - deferredAudioSeek = true; + mFlags &= ~(PLAYING | FIRST_FRAME); - mWatchForAudioSeekComplete = false; - mWatchForAudioEOS = true; + if (mDecryptHandle != NULL) { + mDrmManagerClient->setPlaybackStatus( + mDecryptHandle, Playback::STOP, 0); + } + + return err; } - } else { - mAudioPlayer->resume(); } } @@ -842,6 +782,36 @@ status_t AwesomePlayer::play_l() { return OK; } +status_t AwesomePlayer::startAudioPlayer_l() { + CHECK(!(mFlags & AUDIO_RUNNING)); + + if (mAudioSource == NULL || mAudioPlayer == NULL) { + return OK; + } + + if (!(mFlags & AUDIOPLAYER_STARTED)) { + mFlags |= AUDIOPLAYER_STARTED; + + // We've already started the MediaSource in order to enable + // the prefetcher to read its data. + status_t err = mAudioPlayer->start( + true /* sourceAlreadyStarted */); + + if (err != OK) { + notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); + return err; + } + } else { + mAudioPlayer->resume(); + } + + mFlags |= AUDIO_RUNNING; + + mWatchForAudioEOS = true; + + return OK; +} + void AwesomePlayer::notifyVideoSize_l() { sp<MetaData> meta = mVideoSource->getFormat(); @@ -943,7 +913,7 @@ status_t AwesomePlayer::pause_l(bool at_eos) { cancelPlayerEvents(true /* keepBufferingGoing */); - if (mAudioPlayer != NULL) { + if (mAudioPlayer != NULL && (mFlags & AUDIO_RUNNING)) { if (at_eos) { // If we played the audio stream to completion we // want to make sure that all samples remaining in the audio @@ -952,6 +922,8 @@ status_t AwesomePlayer::pause_l(bool at_eos) { } else { mAudioPlayer->pause(); } + + mFlags &= ~AUDIO_RUNNING; } mFlags &= ~PLAYING; @@ -1066,6 +1038,11 @@ status_t AwesomePlayer::seekTo_l(int64_t timeUs) { notifyListener_l(MEDIA_SEEK_COMPLETE); mSeekNotificationSent = true; + + if ((mFlags & PREPARED) && mVideoSource != NULL) { + mFlags |= SEEK_PREVIEW; + postVideoEvent_l(); + } } return OK; @@ -1168,7 +1145,7 @@ status_t AwesomePlayer::initVideoDecoder(uint32_t flags) { } void AwesomePlayer::finishSeekIfNecessary(int64_t videoTimeUs) { - if (!mSeeking) { + if (!mSeeking || (mFlags & SEEK_PREVIEW)) { return; } @@ -1179,9 +1156,7 @@ void AwesomePlayer::finishSeekIfNecessary(int64_t videoTimeUs) { // requested seek time instead. mAudioPlayer->seekTo(videoTimeUs < 0 ? mSeekTimeUs : videoTimeUs); - mAudioPlayer->resume(); mWatchForAudioSeekComplete = true; - mWatchForAudioEOS = true; } else if (!mSeekNotificationSent) { // If we're playing video only, report seek complete now, // otherwise audio player will notify us later. @@ -1215,7 +1190,8 @@ void AwesomePlayer::onVideoEvent() { mVideoBuffer = NULL; } - if (mCachedSource != NULL && mAudioSource != NULL) { + if (mCachedSource != NULL && mAudioSource != NULL + && !(mFlags & SEEK_PREVIEW)) { // We're going to seek the video source first, followed by // the audio source. // In order to avoid jumps in the DataSource offset caused by @@ -1224,8 +1200,10 @@ void AwesomePlayer::onVideoEvent() { // locations, we'll "pause" the audio source, causing it to // stop reading input data until a subsequent seek. - if (mAudioPlayer != NULL) { + if (mAudioPlayer != NULL && (mFlags & AUDIO_RUNNING)) { mAudioPlayer->pause(); + + mFlags &= ~AUDIO_RUNNING; } mAudioSource->pause(); } @@ -1295,6 +1273,14 @@ void AwesomePlayer::onVideoEvent() { bool wasSeeking = mSeeking; finishSeekIfNecessary(timeUs); + if (mAudioPlayer != NULL && !(mFlags & (AUDIO_RUNNING | SEEK_PREVIEW))) { + status_t err = startAudioPlayer_l(); + if (err != OK) { + LOGE("Startung the audio player failed w/ err %d", err); + return; + } + } + TimeSource *ts = (mFlags & AUDIO_AT_EOS) ? &mSystemTimeSource : mTimeSource; if (mFlags & FIRST_FRAME) { @@ -1309,41 +1295,34 @@ void AwesomePlayer::onVideoEvent() { mTimeSourceDeltaUs = realTimeUs - mediaTimeUs; } - int64_t nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs; - - int64_t latenessUs = nowUs - timeUs; - - if (wasSeeking) { + if (!wasSeeking) { // Let's display the first frame after seeking right away. - latenessUs = 0; - } - if (mRTPSession != NULL) { - // We'll completely ignore timestamps for gtalk videochat - // and we'll play incoming video as fast as we get it. - latenessUs = 0; - } + int64_t nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs; - if (latenessUs > 40000) { - // We're more than 40ms late. - LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6); - if ( mSinceLastDropped > FRAME_DROP_FREQ) - { - LOGV("we're late by %lld us (%.2f secs) dropping one after %d frames", latenessUs, latenessUs / 1E6, mSinceLastDropped); - mSinceLastDropped = 0; - mVideoBuffer->release(); - mVideoBuffer = NULL; + int64_t latenessUs = nowUs - timeUs; - postVideoEvent_l(); - return; + if (latenessUs > 40000) { + // We're more than 40ms late. + LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6); + if ( mSinceLastDropped > FRAME_DROP_FREQ) + { + LOGV("we're late by %lld us (%.2f secs) dropping one after %d frames", latenessUs, latenessUs / 1E6, mSinceLastDropped); + mSinceLastDropped = 0; + mVideoBuffer->release(); + mVideoBuffer = NULL; + + postVideoEvent_l(); + return; + } } - } - if (latenessUs < -10000) { - // We're more than 10ms early. + if (latenessUs < -10000) { + // We're more than 10ms early. - postVideoEvent_l(10000); - return; + postVideoEvent_l(10000); + return; + } } if (mVideoRendererIsPreview || mVideoRenderer == NULL) { @@ -1360,6 +1339,11 @@ void AwesomePlayer::onVideoEvent() { mVideoBuffer->release(); mVideoBuffer = NULL; + if (wasSeeking && (mFlags & SEEK_PREVIEW)) { + mFlags &= ~SEEK_PREVIEW; + return; + } + postVideoEvent_l(); } @@ -1497,7 +1481,8 @@ status_t AwesomePlayer::prepareAsync_l() { status_t AwesomePlayer::finishSetDataSource_l() { sp<DataSource> dataSource; - if (!strncasecmp("http://", mUri.string(), 7)) { + if (!strncasecmp("http://", mUri.string(), 7) + || !strncasecmp("https://", mUri.string(), 8)) { mConnectingDataSource = new NuHTTPDataSource; mLock.unlock(); @@ -1549,141 +1534,6 @@ status_t AwesomePlayer::finishSetDataSource_l() { LOGI("Prepare cancelled while waiting for initial cache fill."); return UNKNOWN_ERROR; } - } else if (!strncasecmp(mUri.string(), "httplive://", 11)) { - String8 uri("http://"); - uri.append(mUri.string() + 11); - - if (mLooper == NULL) { - mLooper = new ALooper; - mLooper->setName("httplive"); - mLooper->start(); - } - - mLiveSession = new LiveSession; - mLooper->registerHandler(mLiveSession); - - mLiveSession->connect(uri.string()); - dataSource = mLiveSession->getDataSource(); - - sp<MediaExtractor> extractor = - MediaExtractor::Create(dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS); - - static_cast<MPEG2TSExtractor *>(extractor.get()) - ->setLiveSession(mLiveSession); - - return setDataSource_l(extractor); - } else if (!strncmp("rtsp://gtalk/", mUri.string(), 13)) { - if (mLooper == NULL) { - mLooper = new ALooper; - mLooper->setName("gtalk rtp"); - mLooper->start( - false /* runOnCallingThread */, - false /* canCallJava */, - PRIORITY_HIGHEST); - } - - const char *startOfCodecString = &mUri.string()[13]; - const char *startOfSlash1 = strchr(startOfCodecString, '/'); - if (startOfSlash1 == NULL) { - return BAD_VALUE; - } - const char *startOfWidthString = &startOfSlash1[1]; - const char *startOfSlash2 = strchr(startOfWidthString, '/'); - if (startOfSlash2 == NULL) { - return BAD_VALUE; - } - const char *startOfHeightString = &startOfSlash2[1]; - - String8 codecString(startOfCodecString, startOfSlash1 - startOfCodecString); - String8 widthString(startOfWidthString, startOfSlash2 - startOfWidthString); - String8 heightString(startOfHeightString); - -#if 0 - mRTPPusher = new UDPPusher("/data/misc/rtpout.bin", 5434); - mLooper->registerHandler(mRTPPusher); - - mRTCPPusher = new UDPPusher("/data/misc/rtcpout.bin", 5435); - mLooper->registerHandler(mRTCPPusher); -#endif - - mRTPSession = new ARTPSession; - mLooper->registerHandler(mRTPSession); - -#if 0 - // My AMR SDP - static const char *raw = - "v=0\r\n" - "o=- 64 233572944 IN IP4 127.0.0.0\r\n" - "s=QuickTime\r\n" - "t=0 0\r\n" - "a=range:npt=0-315\r\n" - "a=isma-compliance:2,2.0,2\r\n" - "m=audio 5434 RTP/AVP 97\r\n" - "c=IN IP4 127.0.0.1\r\n" - "b=AS:30\r\n" - "a=rtpmap:97 AMR/8000/1\r\n" - "a=fmtp:97 octet-align\r\n"; -#elif 1 - String8 sdp; - sdp.appendFormat( - "v=0\r\n" - "o=- 64 233572944 IN IP4 127.0.0.0\r\n" - "s=QuickTime\r\n" - "t=0 0\r\n" - "a=range:npt=0-315\r\n" - "a=isma-compliance:2,2.0,2\r\n" - "m=video 5434 RTP/AVP 97\r\n" - "c=IN IP4 127.0.0.1\r\n" - "b=AS:30\r\n" - "a=rtpmap:97 %s/90000\r\n" - "a=cliprect:0,0,%s,%s\r\n" - "a=framesize:97 %s-%s\r\n", - - codecString.string(), - heightString.string(), widthString.string(), - widthString.string(), heightString.string() - ); - const char *raw = sdp.string(); - -#endif - - sp<ASessionDescription> desc = new ASessionDescription; - CHECK(desc->setTo(raw, strlen(raw))); - - CHECK_EQ(mRTPSession->setup(desc), (status_t)OK); - - if (mRTPPusher != NULL) { - mRTPPusher->start(); - } - - if (mRTCPPusher != NULL) { - mRTCPPusher->start(); - } - - CHECK_EQ(mRTPSession->countTracks(), 1u); - sp<MediaSource> source = mRTPSession->trackAt(0); - -#if 0 - bool eos; - while (((APacketSource *)source.get()) - ->getQueuedDuration(&eos) < 5000000ll && !eos) { - usleep(100000ll); - } -#endif - - const char *mime; - CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime)); - - if (!strncasecmp("video/", mime, 6)) { - setVideoSource(source); - } else { - CHECK(!strncasecmp("audio/", mime, 6)); - setAudioSource(source); - } - - mExtractorFlags = MediaExtractor::CAN_PAUSE; - - return OK; } else if (!strncasecmp("rtsp://", mUri.string(), 7)) { if (mLooper == NULL) { mLooper = new ALooper; @@ -1691,7 +1541,13 @@ status_t AwesomePlayer::finishSetDataSource_l() { mLooper->start(); } mRTSPController = new ARTSPController(mLooper); + mConnectingRTSPController = mRTSPController; + + mLock.unlock(); status_t err = mRTSPController->connect(mUri.string()); + mLock.lock(); + + mConnectingRTSPController.clear(); LOGI("ARTSPController::connect returned %d", err); @@ -1718,6 +1574,7 @@ status_t AwesomePlayer::finishSetDataSource_l() { dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient); if (mDecryptHandle != NULL) { + CHECK(mDrmManagerClient); if (RightsStatus::RIGHTS_VALID == mDecryptHandle->status) { if (DecryptApiType::WV_BASED == mDecryptHandle->decryptApiType) { LOGD("Setting mCachedSource to NULL for WVM\n"); diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 66e0657b7b67..8a24bc45b62a 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -598,8 +598,7 @@ status_t CameraSource::stop() { } if (mNumGlitches > 0) { - LOGW("%d long delays between neighboring video frames during", - mNumGlitches); + LOGW("%d long delays between neighboring video frames", mNumGlitches); } CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped); @@ -696,10 +695,9 @@ void CameraSource::dataCallbackTimestamp(int64_t timestampUs, int32_t msgType, const sp<IMemory> &data) { LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs); Mutex::Autolock autoLock(mLock); - if (!mStarted) { + if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) { + LOGV("Drop frame at %lld/%lld us", timestampUs, mStartTimeUs); releaseOneRecordingFrame(data); - ++mNumFramesReceived; - ++mNumFramesDropped; return; } diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp index 3c98932cd5e6..647cf438e007 100644 --- a/media/libstagefright/DRMExtractor.cpp +++ b/media/libstagefright/DRMExtractor.cpp @@ -38,12 +38,12 @@ namespace android { -DrmManagerClient* gDrmManagerClient = NULL; - class DRMSource : public MediaSource { public: DRMSource(const sp<MediaSource> &mediaSource, - DecryptHandle* decryptHandle, int32_t trackId, DrmBuffer* ipmpBox); + DecryptHandle *decryptHandle, + DrmManagerClient *managerClient, + int32_t trackId, DrmBuffer *ipmpBox); virtual status_t start(MetaData *params = NULL); virtual status_t stop(); @@ -57,6 +57,7 @@ protected: private: sp<MediaSource> mOriginalMediaSource; DecryptHandle* mDecryptHandle; + DrmManagerClient* mDrmManagerClient; size_t mTrackId; mutable Mutex mDRMLock; size_t mNALLengthSize; @@ -69,13 +70,17 @@ private: //////////////////////////////////////////////////////////////////////////////// DRMSource::DRMSource(const sp<MediaSource> &mediaSource, - DecryptHandle* decryptHandle, int32_t trackId, DrmBuffer* ipmpBox) + DecryptHandle *decryptHandle, + DrmManagerClient *managerClient, + int32_t trackId, DrmBuffer *ipmpBox) : mOriginalMediaSource(mediaSource), mDecryptHandle(decryptHandle), + mDrmManagerClient(managerClient), mTrackId(trackId), mNALLengthSize(0), mWantsNALFragments(false) { - gDrmManagerClient->initializeDecryptUnit( + CHECK(mDrmManagerClient); + mDrmManagerClient->initializeDecryptUnit( mDecryptHandle, trackId, ipmpBox); const char *mime; @@ -100,7 +105,7 @@ DRMSource::DRMSource(const sp<MediaSource> &mediaSource, DRMSource::~DRMSource() { Mutex::Autolock autoLock(mDRMLock); - gDrmManagerClient->finalizeDecryptUnit(mDecryptHandle, mTrackId); + mDrmManagerClient->finalizeDecryptUnit(mDecryptHandle, mTrackId); } status_t DRMSource::start(MetaData *params) { @@ -140,7 +145,7 @@ status_t DRMSource::read(MediaBuffer **buffer, const ReadOptions *options) { decryptedDrmBuffer.data = new char[len]; DrmBuffer *pDecryptedDrmBuffer = &decryptedDrmBuffer; - if ((err = gDrmManagerClient->decrypt(mDecryptHandle, mTrackId, + if ((err = mDrmManagerClient->decrypt(mDecryptHandle, mTrackId, &encryptedDrmBuffer, &pDecryptedDrmBuffer)) != DRM_NO_ERROR) { if (decryptedDrmBuffer.data) { @@ -234,12 +239,12 @@ status_t DRMSource::read(MediaBuffer **buffer, const ReadOptions *options) { DRMExtractor::DRMExtractor(const sp<DataSource> &source, const char* mime) : mDataSource(source), - mDecryptHandle(NULL) { + mDecryptHandle(NULL), + mDrmManagerClient(NULL) { mOriginalExtractor = MediaExtractor::Create(source, mime); mOriginalExtractor->setDrmFlag(true); - DrmManagerClient *client; - source->getDrmInfo(&mDecryptHandle, &client); + source->getDrmInfo(&mDecryptHandle, &mDrmManagerClient); } DRMExtractor::~DRMExtractor() { @@ -260,7 +265,8 @@ sp<MediaSource> DRMExtractor::getTrack(size_t index) { ipmpBox.data = mOriginalExtractor->getDrmTrackInfo(trackID, &(ipmpBox.length)); CHECK(ipmpBox.length > 0); - return new DRMSource(originalMediaSource, mDecryptHandle, trackID, &ipmpBox); + return new DRMSource(originalMediaSource, mDecryptHandle, mDrmManagerClient, + trackID, &ipmpBox); } sp<MetaData> DRMExtractor::getTrackMetaData(size_t index, uint32_t flags) { @@ -271,22 +277,10 @@ sp<MetaData> DRMExtractor::getMetaData() { return mOriginalExtractor->getMetaData(); } -static Mutex gDRMSnifferMutex; bool SniffDRM( const sp<DataSource> &source, String8 *mimeType, float *confidence, sp<AMessage> *) { - { - Mutex::Autolock autoLock(gDRMSnifferMutex); - if (gDrmManagerClient == NULL) { - gDrmManagerClient = new DrmManagerClient(); - } - - if (gDrmManagerClient == NULL) { - return false; - } - } - - DecryptHandle *decryptHandle = source->DrmInitialization(gDrmManagerClient); + DecryptHandle *decryptHandle = source->DrmInitialization(); if (decryptHandle != NULL) { if (decryptHandle->decryptApiType == DecryptApiType::CONTAINER_BASED) { diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index ee0d792999c4..3b382085bd80 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -23,6 +23,8 @@ #include "include/NuCachedSource2.h" #include "include/NuHTTPDataSource.h" #include "include/DRMExtractor.h" +#include "include/FLACExtractor.h" +#include "include/AACExtractor.h" #include "matroska/MatroskaExtractor.h" @@ -104,9 +106,11 @@ void DataSource::RegisterDefaultSniffers() { RegisterSniffer(SniffMatroska); RegisterSniffer(SniffOgg); RegisterSniffer(SniffWAV); + RegisterSniffer(SniffFLAC); RegisterSniffer(SniffAMR); RegisterSniffer(SniffMPEG2TS); RegisterSniffer(SniffMP3); + RegisterSniffer(SniffAAC); char value[PROPERTY_VALUE_MAX]; if (property_get("drm.service.enabled", value, NULL) @@ -121,7 +125,8 @@ sp<DataSource> DataSource::CreateFromURI( sp<DataSource> source; if (!strncasecmp("file://", uri, 7)) { source = new FileSource(uri + 7); - } else if (!strncasecmp("http://", uri, 7)) { + } else if (!strncasecmp("http://", uri, 7) + || !strncasecmp("https://", uri, 8)) { sp<NuHTTPDataSource> httpSource = new NuHTTPDataSource; if (httpSource->connect(uri, headers) != OK) { return NULL; diff --git a/media/libstagefright/FLACExtractor.cpp b/media/libstagefright/FLACExtractor.cpp new file mode 100644 index 000000000000..8ba5a2d1cf5a --- /dev/null +++ b/media/libstagefright/FLACExtractor.cpp @@ -0,0 +1,813 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "FLACExtractor" +#include <utils/Log.h> + +#include "include/FLACExtractor.h" +// Vorbis comments +#include "include/OggExtractor.h" +// libFLAC parser +#include "FLAC/stream_decoder.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MediaBuffer.h> + +namespace android { + +class FLACParser; + +class FLACSource : public MediaSource { + +public: + FLACSource( + const sp<DataSource> &dataSource, + const sp<MetaData> &trackMetadata); + + virtual status_t start(MetaData *params); + virtual status_t stop(); + virtual sp<MetaData> getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options = NULL); + +protected: + virtual ~FLACSource(); + +private: + sp<DataSource> mDataSource; + sp<MetaData> mTrackMetadata; + sp<FLACParser> mParser; + bool mInitCheck; + bool mStarted; + + status_t init(); + + // no copy constructor or assignment + FLACSource(const FLACSource &); + FLACSource &operator=(const FLACSource &); + +}; + +// FLACParser wraps a C libFLAC parser aka stream decoder + +class FLACParser : public RefBase { + +public: + FLACParser( + const sp<DataSource> &dataSource, + // If metadata pointers aren't provided, we don't fill them + const sp<MetaData> &fileMetadata = 0, + const sp<MetaData> &trackMetadata = 0); + + status_t initCheck() const { + return mInitCheck; + } + + // stream properties + unsigned getMaxBlockSize() const { + return mStreamInfo.max_blocksize; + } + unsigned getSampleRate() const { + return mStreamInfo.sample_rate; + } + unsigned getChannels() const { + return mStreamInfo.channels; + } + unsigned getBitsPerSample() const { + return mStreamInfo.bits_per_sample; + } + FLAC__uint64 getTotalSamples() const { + return mStreamInfo.total_samples; + } + + // media buffers + void allocateBuffers(); + void releaseBuffers(); + MediaBuffer *readBuffer() { + return readBuffer(false, 0LL); + } + MediaBuffer *readBuffer(FLAC__uint64 sample) { + return readBuffer(true, sample); + } + +protected: + virtual ~FLACParser(); + +private: + sp<DataSource> mDataSource; + sp<MetaData> mFileMetadata; + sp<MetaData> mTrackMetadata; + bool mInitCheck; + + // media buffers + size_t mMaxBufferSize; + MediaBufferGroup *mGroup; + void (*mCopy)(short *dst, const int *const *src, unsigned nSamples); + + // handle to underlying libFLAC parser + FLAC__StreamDecoder *mDecoder; + + // current position within the data source + off64_t mCurrentPos; + bool mEOF; + + // cached when the STREAMINFO metadata is parsed by libFLAC + FLAC__StreamMetadata_StreamInfo mStreamInfo; + bool mStreamInfoValid; + + // cached when a decoded PCM block is "written" by libFLAC parser + bool mWriteRequested; + bool mWriteCompleted; + FLAC__FrameHeader mWriteHeader; + const FLAC__int32 * const *mWriteBuffer; + + // most recent error reported by libFLAC parser + FLAC__StreamDecoderErrorStatus mErrorStatus; + + status_t init(); + MediaBuffer *readBuffer(bool doSeek, FLAC__uint64 sample); + + // no copy constructor or assignment + FLACParser(const FLACParser &); + FLACParser &operator=(const FLACParser &); + + // FLAC parser callbacks as C++ instance methods + FLAC__StreamDecoderReadStatus readCallback( + FLAC__byte buffer[], size_t *bytes); + FLAC__StreamDecoderSeekStatus seekCallback( + FLAC__uint64 absolute_byte_offset); + FLAC__StreamDecoderTellStatus tellCallback( + FLAC__uint64 *absolute_byte_offset); + FLAC__StreamDecoderLengthStatus lengthCallback( + FLAC__uint64 *stream_length); + FLAC__bool eofCallback(); + FLAC__StreamDecoderWriteStatus writeCallback( + const FLAC__Frame *frame, const FLAC__int32 * const buffer[]); + void metadataCallback(const FLAC__StreamMetadata *metadata); + void errorCallback(FLAC__StreamDecoderErrorStatus status); + + // FLAC parser callbacks as C-callable functions + static FLAC__StreamDecoderReadStatus read_callback( + const FLAC__StreamDecoder *decoder, + FLAC__byte buffer[], size_t *bytes, + void *client_data); + static FLAC__StreamDecoderSeekStatus seek_callback( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 absolute_byte_offset, + void *client_data); + static FLAC__StreamDecoderTellStatus tell_callback( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *absolute_byte_offset, + void *client_data); + static FLAC__StreamDecoderLengthStatus length_callback( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *stream_length, + void *client_data); + static FLAC__bool eof_callback( + const FLAC__StreamDecoder *decoder, + void *client_data); + static FLAC__StreamDecoderWriteStatus write_callback( + const FLAC__StreamDecoder *decoder, + const FLAC__Frame *frame, const FLAC__int32 * const buffer[], + void *client_data); + static void metadata_callback( + const FLAC__StreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, + void *client_data); + static void error_callback( + const FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, + void *client_data); + +}; + +// The FLAC parser calls our C++ static callbacks using C calling conventions, +// inside FLAC__stream_decoder_process_until_end_of_metadata +// and FLAC__stream_decoder_process_single. +// We immediately then call our corresponding C++ instance methods +// with the same parameter list, but discard redundant information. + +FLAC__StreamDecoderReadStatus FLACParser::read_callback( + const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], + size_t *bytes, void *client_data) +{ + return ((FLACParser *) client_data)->readCallback(buffer, bytes); +} + +FLAC__StreamDecoderSeekStatus FLACParser::seek_callback( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 absolute_byte_offset, void *client_data) +{ + return ((FLACParser *) client_data)->seekCallback(absolute_byte_offset); +} + +FLAC__StreamDecoderTellStatus FLACParser::tell_callback( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + return ((FLACParser *) client_data)->tellCallback(absolute_byte_offset); +} + +FLAC__StreamDecoderLengthStatus FLACParser::length_callback( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *stream_length, void *client_data) +{ + return ((FLACParser *) client_data)->lengthCallback(stream_length); +} + +FLAC__bool FLACParser::eof_callback( + const FLAC__StreamDecoder *decoder, void *client_data) +{ + return ((FLACParser *) client_data)->eofCallback(); +} + +FLAC__StreamDecoderWriteStatus FLACParser::write_callback( + const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, + const FLAC__int32 * const buffer[], void *client_data) +{ + return ((FLACParser *) client_data)->writeCallback(frame, buffer); +} + +void FLACParser::metadata_callback( + const FLAC__StreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, void *client_data) +{ + ((FLACParser *) client_data)->metadataCallback(metadata); +} + +void FLACParser::error_callback( + const FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + ((FLACParser *) client_data)->errorCallback(status); +} + +// These are the corresponding callbacks with C++ calling conventions + +FLAC__StreamDecoderReadStatus FLACParser::readCallback( + FLAC__byte buffer[], size_t *bytes) +{ + size_t requested = *bytes; + ssize_t actual = mDataSource->readAt(mCurrentPos, buffer, requested); + if (0 > actual) { + *bytes = 0; + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } else if (0 == actual) { + *bytes = 0; + mEOF = true; + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + } else { + assert(actual <= requested); + *bytes = actual; + mCurrentPos += actual; + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } +} + +FLAC__StreamDecoderSeekStatus FLACParser::seekCallback( + FLAC__uint64 absolute_byte_offset) +{ + mCurrentPos = absolute_byte_offset; + mEOF = false; + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; +} + +FLAC__StreamDecoderTellStatus FLACParser::tellCallback( + FLAC__uint64 *absolute_byte_offset) +{ + *absolute_byte_offset = mCurrentPos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; +} + +FLAC__StreamDecoderLengthStatus FLACParser::lengthCallback( + FLAC__uint64 *stream_length) +{ + off64_t size; + if (OK == mDataSource->getSize(&size)) { + *stream_length = size; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } else { + return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + } +} + +FLAC__bool FLACParser::eofCallback() +{ + return mEOF; +} + +FLAC__StreamDecoderWriteStatus FLACParser::writeCallback( + const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) +{ + if (mWriteRequested) { + mWriteRequested = false; + // FLAC parser doesn't free or realloc buffer until next frame or finish + mWriteHeader = frame->header; + mWriteBuffer = buffer; + mWriteCompleted = true; + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } else { + LOGE("FLACParser::writeCallback unexpected"); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } +} + +void FLACParser::metadataCallback(const FLAC__StreamMetadata *metadata) +{ + switch (metadata->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + if (!mStreamInfoValid) { + mStreamInfo = metadata->data.stream_info; + mStreamInfoValid = true; + } else { + LOGE("FLACParser::metadataCallback unexpected STREAMINFO"); + } + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + { + const FLAC__StreamMetadata_VorbisComment *vc; + vc = &metadata->data.vorbis_comment; + for (FLAC__uint32 i = 0; i < vc->num_comments; ++i) { + FLAC__StreamMetadata_VorbisComment_Entry *vce; + vce = &vc->comments[i]; + if (mFileMetadata != 0) { + parseVorbisComment(mFileMetadata, (const char *) vce->entry, + vce->length); + } + } + } + break; + case FLAC__METADATA_TYPE_PICTURE: + if (mFileMetadata != 0) { + const FLAC__StreamMetadata_Picture *p = &metadata->data.picture; + mFileMetadata->setData(kKeyAlbumArt, + MetaData::TYPE_NONE, p->data, p->data_length); + mFileMetadata->setCString(kKeyAlbumArtMIME, p->mime_type); + } + break; + default: + LOGW("FLACParser::metadataCallback unexpected type %u", metadata->type); + break; + } +} + +void FLACParser::errorCallback(FLAC__StreamDecoderErrorStatus status) +{ + LOGE("FLACParser::errorCallback status=%d", status); + mErrorStatus = status; +} + +// Copy samples from FLAC native 32-bit non-interleaved to 16-bit interleaved. +// These are candidates for optimization if needed. + +static void copyMono8(short *dst, const int *const *src, unsigned nSamples) +{ + for (unsigned i = 0; i < nSamples; ++i) { + *dst++ = src[0][i] << 8; + } +} + +static void copyStereo8(short *dst, const int *const *src, unsigned nSamples) +{ + for (unsigned i = 0; i < nSamples; ++i) { + *dst++ = src[0][i] << 8; + *dst++ = src[1][i] << 8; + } +} + +static void copyMono16(short *dst, const int *const *src, unsigned nSamples) +{ + for (unsigned i = 0; i < nSamples; ++i) { + *dst++ = src[0][i]; + } +} + +static void copyStereo16(short *dst, const int *const *src, unsigned nSamples) +{ + for (unsigned i = 0; i < nSamples; ++i) { + *dst++ = src[0][i]; + *dst++ = src[1][i]; + } +} + +// 24-bit versions should do dithering or noise-shaping, here or in AudioFlinger + +static void copyMono24(short *dst, const int *const *src, unsigned nSamples) +{ + for (unsigned i = 0; i < nSamples; ++i) { + *dst++ = src[0][i] >> 8; + } +} + +static void copyStereo24(short *dst, const int *const *src, unsigned nSamples) +{ + for (unsigned i = 0; i < nSamples; ++i) { + *dst++ = src[0][i] >> 8; + *dst++ = src[1][i] >> 8; + } +} + +static void copyTrespass(short *dst, const int *const *src, unsigned nSamples) +{ + TRESPASS(); +} + +// FLACParser + +FLACParser::FLACParser( + const sp<DataSource> &dataSource, + const sp<MetaData> &fileMetadata, + const sp<MetaData> &trackMetadata) + : mDataSource(dataSource), + mFileMetadata(fileMetadata), + mTrackMetadata(trackMetadata), + mInitCheck(false), + mMaxBufferSize(0), + mGroup(NULL), + mCopy(copyTrespass), + mDecoder(NULL), + mCurrentPos(0LL), + mEOF(false), + mStreamInfoValid(false), + mWriteRequested(false), + mWriteCompleted(false), + mWriteBuffer(NULL), + mErrorStatus((FLAC__StreamDecoderErrorStatus) -1) +{ + LOGV("FLACParser::FLACParser"); + memset(&mStreamInfo, 0, sizeof(mStreamInfo)); + memset(&mWriteHeader, 0, sizeof(mWriteHeader)); + mInitCheck = init(); +} + +FLACParser::~FLACParser() +{ + LOGV("FLACParser::~FLACParser"); + if (mDecoder != NULL) { + FLAC__stream_decoder_delete(mDecoder); + mDecoder = NULL; + } +} + +status_t FLACParser::init() +{ + // setup libFLAC parser + mDecoder = FLAC__stream_decoder_new(); + if (mDecoder == NULL) { + // The new should succeed, since probably all it does is a malloc + // that always succeeds in Android. But to avoid dependence on the + // libFLAC internals, we check and log here. + LOGE("new failed"); + return NO_INIT; + } + FLAC__stream_decoder_set_md5_checking(mDecoder, false); + FLAC__stream_decoder_set_metadata_ignore_all(mDecoder); + FLAC__stream_decoder_set_metadata_respond( + mDecoder, FLAC__METADATA_TYPE_STREAMINFO); + FLAC__stream_decoder_set_metadata_respond( + mDecoder, FLAC__METADATA_TYPE_PICTURE); + FLAC__stream_decoder_set_metadata_respond( + mDecoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__StreamDecoderInitStatus initStatus; + initStatus = FLAC__stream_decoder_init_stream( + mDecoder, + read_callback, seek_callback, tell_callback, + length_callback, eof_callback, write_callback, + metadata_callback, error_callback, (void *) this); + if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + // A failure here probably indicates a programming error and so is + // unlikely to happen. But we check and log here similarly to above. + LOGE("init_stream failed %d", initStatus); + return NO_INIT; + } + // parse all metadata + if (!FLAC__stream_decoder_process_until_end_of_metadata(mDecoder)) { + LOGE("end_of_metadata failed"); + return NO_INIT; + } + if (mStreamInfoValid) { + // check channel count + switch (getChannels()) { + case 1: + case 2: + break; + default: + LOGE("unsupported channel count %u", getChannels()); + return NO_INIT; + } + // check bit depth + switch (getBitsPerSample()) { + case 8: + case 16: + case 24: + break; + default: + LOGE("unsupported bits per sample %u", getBitsPerSample()); + return NO_INIT; + } + // check sample rate + switch (getSampleRate()) { + case 8000: + case 11025: + case 12000: + case 16000: + case 22050: + case 24000: + case 32000: + case 44100: + case 48000: + break; + default: + // 96000 would require a proper downsampler in AudioFlinger + LOGE("unsupported sample rate %u", getSampleRate()); + return NO_INIT; + } + // configure the appropriate copy function, defaulting to trespass + static const struct { + unsigned mChannels; + unsigned mBitsPerSample; + void (*mCopy)(short *dst, const int *const *src, unsigned nSamples); + } table[] = { + { 1, 8, copyMono8 }, + { 2, 8, copyStereo8 }, + { 1, 16, copyMono16 }, + { 2, 16, copyStereo16 }, + { 1, 24, copyMono24 }, + { 2, 24, copyStereo24 }, + }; + for (unsigned i = 0; i < sizeof(table)/sizeof(table[0]); ++i) { + if (table[i].mChannels == getChannels() && + table[i].mBitsPerSample == getBitsPerSample()) { + mCopy = table[i].mCopy; + break; + } + } + // populate track metadata + if (mTrackMetadata != 0) { + mTrackMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + mTrackMetadata->setInt32(kKeyChannelCount, getChannels()); + mTrackMetadata->setInt32(kKeySampleRate, getSampleRate()); + // sample rate is non-zero, so division by zero not possible + mTrackMetadata->setInt64(kKeyDuration, + (getTotalSamples() * 1000000LL) / getSampleRate()); + } + } else { + LOGE("missing STREAMINFO"); + return NO_INIT; + } + if (mFileMetadata != 0) { + mFileMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_FLAC); + } + return OK; +} + +void FLACParser::allocateBuffers() +{ + CHECK(mGroup == NULL); + mGroup = new MediaBufferGroup; + mMaxBufferSize = getMaxBlockSize() * getChannels() * sizeof(short); + mGroup->add_buffer(new MediaBuffer(mMaxBufferSize)); +} + +void FLACParser::releaseBuffers() +{ + CHECK(mGroup != NULL); + delete mGroup; + mGroup = NULL; +} + +MediaBuffer *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample) +{ + mWriteRequested = true; + mWriteCompleted = false; + if (doSeek) { + // We implement the seek callback, so this works without explicit flush + if (!FLAC__stream_decoder_seek_absolute(mDecoder, sample)) { + LOGE("FLACParser::readBuffer seek to sample %llu failed", sample); + return NULL; + } + LOGV("FLACParser::readBuffer seek to sample %llu succeeded", sample); + } else { + if (!FLAC__stream_decoder_process_single(mDecoder)) { + LOGE("FLACParser::readBuffer process_single failed"); + return NULL; + } + } + if (!mWriteCompleted) { + LOGV("FLACParser::readBuffer write did not complete"); + return NULL; + } + // verify that block header keeps the promises made by STREAMINFO + unsigned blocksize = mWriteHeader.blocksize; + if (blocksize == 0 || blocksize > getMaxBlockSize()) { + LOGE("FLACParser::readBuffer write invalid blocksize %u", blocksize); + return NULL; + } + if (mWriteHeader.sample_rate != getSampleRate() || + mWriteHeader.channels != getChannels() || + mWriteHeader.bits_per_sample != getBitsPerSample()) { + LOGE("FLACParser::readBuffer write changed parameters mid-stream"); + } + // acquire a media buffer + CHECK(mGroup != NULL); + MediaBuffer *buffer; + status_t err = mGroup->acquire_buffer(&buffer); + if (err != OK) { + return NULL; + } + size_t bufferSize = blocksize * getChannels() * sizeof(short); + CHECK(bufferSize <= mMaxBufferSize); + short *data = (short *) buffer->data(); + buffer->set_range(0, bufferSize); + // copy PCM from FLAC write buffer to our media buffer, with interleaving + (*mCopy)(data, mWriteBuffer, blocksize); + // fill in buffer metadata + CHECK(mWriteHeader.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + FLAC__uint64 sampleNumber = mWriteHeader.number.sample_number; + int64_t timeUs = (1000000LL * sampleNumber) / getSampleRate(); + buffer->meta_data()->setInt64(kKeyTime, timeUs); + buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); + return buffer; +} + +// FLACsource + +FLACSource::FLACSource( + const sp<DataSource> &dataSource, + const sp<MetaData> &trackMetadata) + : mDataSource(dataSource), + mTrackMetadata(trackMetadata), + mParser(0), + mInitCheck(false), + mStarted(false) +{ + LOGV("FLACSource::FLACSource"); + mInitCheck = init(); +} + +FLACSource::~FLACSource() +{ + LOGV("~FLACSource::FLACSource"); + if (mStarted) { + stop(); + } +} + +status_t FLACSource::start(MetaData *params) +{ + LOGV("FLACSource::start"); + + CHECK(!mStarted); + mParser->allocateBuffers(); + mStarted = true; + + return OK; +} + +status_t FLACSource::stop() +{ + LOGV("FLACSource::stop"); + + CHECK(mStarted); + mParser->releaseBuffers(); + mStarted = false; + + return OK; +} + +sp<MetaData> FLACSource::getFormat() +{ + return mTrackMetadata; +} + +status_t FLACSource::read( + MediaBuffer **outBuffer, const ReadOptions *options) +{ + MediaBuffer *buffer; + // process an optional seek request + int64_t seekTimeUs; + ReadOptions::SeekMode mode; + if ((NULL != options) && options->getSeekTo(&seekTimeUs, &mode)) { + FLAC__uint64 sample; + if (seekTimeUs <= 0LL) { + sample = 0LL; + } else { + // sample and total samples are both zero-based, and seek to EOF ok + sample = (seekTimeUs * mParser->getSampleRate()) / 1000000LL; + if (sample >= mParser->getTotalSamples()) { + sample = mParser->getTotalSamples(); + } + } + buffer = mParser->readBuffer(sample); + // otherwise read sequentially + } else { + buffer = mParser->readBuffer(); + } + *outBuffer = buffer; + return buffer != NULL ? (status_t) OK : (status_t) ERROR_END_OF_STREAM; +} + +status_t FLACSource::init() +{ + LOGV("FLACSource::init"); + // re-use the same track metadata passed into constructor from FLACExtractor + mParser = new FLACParser(mDataSource); + return mParser->initCheck(); +} + +// FLACExtractor + +FLACExtractor::FLACExtractor( + const sp<DataSource> &dataSource) + : mDataSource(dataSource), + mInitCheck(false) +{ + LOGV("FLACExtractor::FLACExtractor"); + mInitCheck = init(); +} + +FLACExtractor::~FLACExtractor() +{ + LOGV("~FLACExtractor::FLACExtractor"); +} + +size_t FLACExtractor::countTracks() +{ + return mInitCheck == OK ? 1 : 0; +} + +sp<MediaSource> FLACExtractor::getTrack(size_t index) +{ + if (mInitCheck != OK || index > 0) { + return NULL; + } + return new FLACSource(mDataSource, mTrackMetadata); +} + +sp<MetaData> FLACExtractor::getTrackMetaData( + size_t index, uint32_t flags) +{ + if (mInitCheck != OK || index > 0) { + return NULL; + } + return mTrackMetadata; +} + +status_t FLACExtractor::init() +{ + mFileMetadata = new MetaData; + mTrackMetadata = new MetaData; + // FLACParser will fill in the metadata for us + mParser = new FLACParser(mDataSource, mFileMetadata, mTrackMetadata); + return mParser->initCheck(); +} + +sp<MetaData> FLACExtractor::getMetaData() +{ + return mFileMetadata; +} + +// Sniffer + +bool SniffFLAC( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) +{ + // first 4 is the signature word + // second 4 is the sizeof STREAMINFO + // 042 is the mandatory STREAMINFO + // no need to read rest of the header, as a premature EOF will be caught later + uint8_t header[4+4]; + if (source->readAt(0, header, sizeof(header)) != sizeof(header) + || memcmp("fLaC\0\0\0\042", header, 4+4)) + { + return false; + } + + *mimeType = MEDIA_MIMETYPE_AUDIO_FLAC; + *confidence = 0.5; + + return true; +} + +} // namespace android diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp index 98d5b509d73d..02a78c9eef60 100644 --- a/media/libstagefright/FileSource.cpp +++ b/media/libstagefright/FileSource.cpp @@ -60,6 +60,18 @@ FileSource::~FileSource() { delete[] mDrmBuf; mDrmBuf = NULL; } + + if (mDecryptHandle != NULL) { + // To release mDecryptHandle + CHECK(mDrmManagerClient); + mDrmManagerClient->closeDecryptSession(mDecryptHandle); + mDecryptHandle = NULL; + } + + if (mDrmManagerClient != NULL) { + delete mDrmManagerClient; + mDrmManagerClient = NULL; + } } status_t FileSource::initCheck() const { @@ -113,11 +125,14 @@ status_t FileSource::getSize(off64_t *size) { return OK; } -DecryptHandle* FileSource::DrmInitialization(DrmManagerClient* client) { - if (client == NULL) { +DecryptHandle* FileSource::DrmInitialization() { + if (mDrmManagerClient == NULL) { + mDrmManagerClient = new DrmManagerClient(); + } + + if (mDrmManagerClient == NULL) { return NULL; } - mDrmManagerClient = client; if (mDecryptHandle == NULL) { mDecryptHandle = mDrmManagerClient->openDecryptSession( @@ -125,6 +140,7 @@ DecryptHandle* FileSource::DrmInitialization(DrmManagerClient* client) { } if (mDecryptHandle == NULL) { + delete mDrmManagerClient; mDrmManagerClient = NULL; } diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp index 77a61a577c00..498c7b885e63 100644 --- a/media/libstagefright/HTTPStream.cpp +++ b/media/libstagefright/HTTPStream.cpp @@ -34,6 +34,8 @@ #include <media/stagefright/foundation/ADebug.h> +#include <openssl/ssl.h> + namespace android { // static @@ -41,11 +43,18 @@ const char *HTTPStream::kStatusKey = ":status:"; // MUST be lowercase. HTTPStream::HTTPStream() : mState(READY), - mSocket(-1) { + mSocket(-1), + mSSLContext(NULL), + mSSL(NULL) { } HTTPStream::~HTTPStream() { disconnect(); + + if (mSSLContext != NULL) { + SSL_CTX_free((SSL_CTX *)mSSLContext); + mSSLContext = NULL; + } } static bool MakeSocketBlocking(int s, bool blocking) { @@ -198,7 +207,11 @@ static ssize_t MyReceive(int s, void *data, size_t size, int flags) { return MySendReceive(s, data, size, flags, false /* sendData */); } -status_t HTTPStream::connect(const char *server, int port) { +status_t HTTPStream::connect(const char *server, int port, bool https) { + if (port < 0) { + port = https ? 443 : 80; + } + Mutex::Autolock autoLock(mLock); status_t err = OK; @@ -249,6 +262,47 @@ status_t HTTPStream::connect(const char *server, int port) { return res; } + if (https) { + CHECK(mSSL == NULL); + + if (mSSLContext == NULL) { + SSL_library_init(); + + mSSLContext = SSL_CTX_new(TLSv1_client_method()); + + if (mSSLContext == NULL) { + LOGE("failed to create SSL context"); + mState = READY; + return ERROR_IO; + } + } + + mSSL = SSL_new((SSL_CTX *)mSSLContext); + + if (mSSL == NULL) { + LOGE("failed to create SSL session"); + + mState = READY; + return ERROR_IO; + } + + int res = SSL_set_fd((SSL *)mSSL, mSocket); + + if (res == 1) { + res = SSL_connect((SSL *)mSSL); + } + + if (res != 1) { + SSL_free((SSL *)mSSL); + mSSL = NULL; + + LOGE("failed to connect over SSL"); + mState = READY; + + return ERROR_IO; + } + } + mState = CONNECTED; return OK; @@ -261,6 +315,13 @@ status_t HTTPStream::disconnect() { return ERROR_NOT_CONNECTED; } + if (mSSL != NULL) { + SSL_shutdown((SSL *)mSSL); + + SSL_free((SSL *)mSSL); + mSSL = NULL; + } + CHECK(mSocket >= 0); close(mSocket); mSocket = -1; @@ -276,7 +337,16 @@ status_t HTTPStream::send(const char *data, size_t size) { } while (size > 0) { - ssize_t n = MySend(mSocket, data, size, 0); + ssize_t n; + if (mSSL != NULL) { + n = SSL_write((SSL *)mSSL, data, size); + + if (n < 0) { + n = -SSL_get_error((SSL *)mSSL, n); + } + } else { + n = MySend(mSocket, data, size, 0); + } if (n < 0) { disconnect(); @@ -317,7 +387,17 @@ status_t HTTPStream::receive_line(char *line, size_t size) { for (;;) { char c; - ssize_t n = MyReceive(mSocket, &c, 1, 0); + ssize_t n; + if (mSSL != NULL) { + n = SSL_read((SSL *)mSSL, &c, 1); + + if (n < 0) { + n = -SSL_get_error((SSL *)mSSL, n); + } + } else { + n = MyReceive(mSocket, &c, 1, 0); + } + if (n < 0) { disconnect(); @@ -437,7 +517,16 @@ status_t HTTPStream::receive_header(int *http_status) { ssize_t HTTPStream::receive(void *data, size_t size) { size_t total = 0; while (total < size) { - ssize_t n = MyReceive(mSocket, (char *)data + total, size - total, 0); + ssize_t n; + if (mSSL != NULL) { + n = SSL_read((SSL *)mSSL, (char *)data + total, size - total); + + if (n < 0) { + n = -SSL_get_error((SSL *)mSSL, n); + } + } else { + n = MyReceive(mSocket, (char *)data + total, size - total, 0); + } if (n < 0) { LOGE("recv failed, errno = %d (%s)", (int)n, strerror(-n)); diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index e6e98aa3e422..a973d7e6447e 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -1074,6 +1074,20 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC('c', 't', 't', 's'): + { + status_t err = + mLastTrack->sampleTable->setCompositionTimeToSampleParams( + data_offset, chunk_data_size); + + if (err != OK) { + return err; + } + + *offset += chunk_size; + break; + } + case FOURCC('s', 't', 's', 's'): { status_t err = @@ -1150,6 +1164,30 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC('d', '2', '6', '3'): + { + // d263 contains fixed 7 bytes: + // vendor - 4 bytes + // version - 1 byte + // level - 1 byte + // profile - 1 byte + char buffer[7]; + if (chunk_data_size != (off64_t) sizeof(buffer)) { + LOGE("Incorrect D263 box size %lld", chunk_data_size); + return ERROR_MALFORMED; + } + + if (mDataSource->readAt( + data_offset, buffer, chunk_data_size) < chunk_data_size) { + return ERROR_IO; + } + + mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size); + + *offset += chunk_size; + break; + } + case FOURCC('m', 'e', 't', 'a'): { uint8_t buffer[4]; diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index d1a497f8ef94..5d6ea7cddc8a 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -1122,41 +1122,41 @@ void MPEG4Writer::bufferChunk(const Chunk& chunk) { CHECK("Received a chunk for a unknown track" == 0); } -void MPEG4Writer::writeFirstChunk(ChunkInfo* info) { - LOGV("writeFirstChunk: %p", info->mTrack); - - List<Chunk>::iterator chunkIt = info->mChunks.begin(); - for (List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin(); - it != chunkIt->mSamples.end(); ++it) { - - off64_t offset = info->mTrack->isAvc() - ? addLengthPrefixedSample_l(*it) - : addSample_l(*it); - if (it == chunkIt->mSamples.begin()) { - info->mTrack->addChunkOffset(offset); +void MPEG4Writer::writeChunkToFile(Chunk* chunk) { + LOGV("writeChunkToFile: %lld from %s track", + chunk.mTimestampUs, chunk.mTrack->isAudio()? "audio": "video"); + + int32_t isFirstSample = true; + while (!chunk->mSamples.empty()) { + List<MediaBuffer *>::iterator it = chunk->mSamples.begin(); + + off64_t offset = chunk->mTrack->isAvc() + ? addLengthPrefixedSample_l(*it) + : addSample_l(*it); + + if (isFirstSample) { + chunk->mTrack->addChunkOffset(offset); + isFirstSample = false; } - } - // Done with the current chunk. - // Release all the samples in this chunk. - while (!chunkIt->mSamples.empty()) { - List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin(); (*it)->release(); (*it) = NULL; - chunkIt->mSamples.erase(it); + chunk->mSamples.erase(it); } - chunkIt->mSamples.clear(); - info->mChunks.erase(chunkIt); + chunk->mSamples.clear(); } -void MPEG4Writer::writeChunks() { - LOGV("writeChunks"); +void MPEG4Writer::writeAllChunks() { + LOGV("writeAllChunks"); size_t outstandingChunks = 0; while (!mChunkInfos.empty()) { List<ChunkInfo>::iterator it = mChunkInfos.begin(); while (!it->mChunks.empty()) { - CHECK_EQ(OK, writeOneChunk()); - ++outstandingChunks; + Chunk chunk; + if (findChunkToWrite(&chunk)) { + writeChunkToFile(&chunk); + ++outstandingChunks; + } } it->mTrack = NULL; mChunkInfos.erase(it); @@ -1165,8 +1165,8 @@ void MPEG4Writer::writeChunks() { LOGD("%d chunks are written in the last batch", outstandingChunks); } -status_t MPEG4Writer::writeOneChunk() { - LOGV("writeOneChunk"); +bool MPEG4Writer::findChunkToWrite(Chunk *chunk) { + LOGV("findChunkToWrite"); // Find the smallest timestamp, and write that chunk out // XXX: What if some track is just too slow? @@ -1185,38 +1185,50 @@ status_t MPEG4Writer::writeOneChunk() { if (track == NULL) { LOGV("Nothing to be written after all"); - return OK; + return false; } if (mIsFirstChunk) { mIsFirstChunk = false; } + for (List<ChunkInfo>::iterator it = mChunkInfos.begin(); it != mChunkInfos.end(); ++it) { if (it->mTrack == track) { - writeFirstChunk(&(*it)); + *chunk = *(it->mChunks.begin()); + it->mChunks.erase(it->mChunks.begin()); + CHECK_EQ(chunk->mTrack, track); + return true; } } - return OK; + + return false; } void MPEG4Writer::threadFunc() { LOGV("threadFunc"); prctl(PR_SET_NAME, (unsigned long)"MPEG4Writer", 0, 0, 0); + + Mutex::Autolock autoLock(mLock); while (!mDone) { - { - Mutex::Autolock autolock(mLock); + Chunk chunk; + bool chunkFound = false; + + while (!mDone && !(chunkFound = findChunkToWrite(&chunk))) { mChunkReadyCondition.wait(mLock); - CHECK_EQ(writeOneChunk(), OK); } - } - { - // Write ALL samples - Mutex::Autolock autolock(mLock); - writeChunks(); + // Actual write without holding the lock in order to + // reduce the blocking time for media track threads. + if (chunkFound) { + mLock.unlock(); + writeChunkToFile(&chunk); + mLock.lock(); + } } + + writeAllChunks(); } status_t MPEG4Writer::startWriterThread() { @@ -1269,7 +1281,21 @@ status_t MPEG4Writer::Track::start(MetaData *params) { initTrackingProgressStatus(params); sp<MetaData> meta = new MetaData; + if (mIsRealTimeRecording && mOwner->numTracks() > 1) { + /* + * This extra delay of accepting incoming audio/video signals + * helps to align a/v start time at the beginning of a recording + * session, and it also helps eliminate the "recording" sound for + * camcorder applications. + * + * Ideally, this platform-specific value should be defined + * in media_profiles.xml file + */ + startTimeUs += 700000; + } + meta->setInt64(kKeyTime, startTimeUs); + status_t err = mSource->start(meta.get()); if (err != OK) { mDone = mReachedEOS = true; @@ -1932,7 +1958,11 @@ status_t MPEG4Writer::Track::threadEntry() { ((timestampUs * mTimeScale + 500000LL) / 1000000LL - (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL); - if (currDurationTicks != lastDurationTicks) { + // Force the first sample to have its own stts entry so that + // we can adjust its value later to maintain the A/V sync. + if (mNumSamples == 3 || currDurationTicks != lastDurationTicks) { + LOGV("%s lastDurationUs: %lld us, currDurationTicks: %lld us", + mIsAudio? "Audio": "Video", lastDurationUs, currDurationTicks); addOneSttsTableEntry(sampleCount, lastDurationUs); sampleCount = 1; } else { @@ -1945,6 +1975,8 @@ status_t MPEG4Writer::Track::threadEntry() { } previousSampleSize = sampleSize; } + LOGV("%s timestampUs/lastTimestampUs: %lld/%lld", + mIsAudio? "Audio": "Video", timestampUs, lastTimestampUs); lastDurationUs = timestampUs - lastTimestampUs; lastDurationTicks = currDurationTicks; lastTimestampUs = timestampUs; @@ -2016,7 +2048,16 @@ status_t MPEG4Writer::Track::threadEntry() { } else { ++sampleCount; // Count for the last sample } - addOneSttsTableEntry(sampleCount, lastDurationUs); + + if (mNumSamples <= 2) { + addOneSttsTableEntry(1, lastDurationUs); + if (sampleCount - 1 > 0) { + addOneSttsTableEntry(sampleCount - 1, lastDurationUs); + } + } else { + addOneSttsTableEntry(sampleCount, lastDurationUs); + } + mTrackDurationUs += lastDurationUs; mReachedEOS = true; LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s", @@ -2141,6 +2182,9 @@ void MPEG4Writer::Track::writeTrackHeader( int32_t mvhdTimeScale = mOwner->getTimeScale(); int64_t trakDurationUs = getDurationUs(); + // Compensate for small start time difference from different media tracks + int64_t trackStartTimeOffsetUs = 0; + mOwner->beginBox("trak"); mOwner->beginBox("tkhd"); @@ -2179,26 +2223,8 @@ void MPEG4Writer::Track::writeTrackHeader( int64_t moovStartTimeUs = mOwner->getStartTimestampUs(); if (mStartTimestampUs != moovStartTimeUs) { - mOwner->beginBox("edts"); - mOwner->beginBox("elst"); - mOwner->writeInt32(0); // version=0, flags=0: 32-bit time - mOwner->writeInt32(2); // never ends with an empty list - - // First elst entry: specify the starting time offset - int64_t offsetUs = mStartTimestampUs - moovStartTimeUs; - LOGV("OffsetUs: %lld", offsetUs); - int32_t seg = (offsetUs * mvhdTimeScale + 5E5) / 1E6; - mOwner->writeInt32(seg); // in mvhd timecale - mOwner->writeInt32(-1); // starting time offset - mOwner->writeInt32(1 << 16); // rate = 1.0 - - // Second elst entry: specify the track duration - seg = (trakDurationUs * mvhdTimeScale + 5E5) / 1E6; - mOwner->writeInt32(seg); // in mvhd timescale - mOwner->writeInt32(0); - mOwner->writeInt32(1 << 16); - mOwner->endBox(); - mOwner->endBox(); + CHECK(mStartTimestampUs > moovStartTimeUs); + trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs; } mOwner->beginBox("mdia"); @@ -2454,7 +2480,7 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->beginBox("stts"); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt32(mNumSttsTableEntries); - int64_t prevTimestampUs = 0; + int64_t prevTimestampUs = trackStartTimeOffsetUs; for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); it != mSttsTableEntries.end(); ++it) { mOwner->writeInt32(it->sampleCount); diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index 4599fcaa41b1..0be7261b46c0 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -35,6 +35,8 @@ const char *MEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis"; const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw"; const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw"; const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw"; +const char *MEDIA_MIMETYPE_AUDIO_FLAC = "audio/flac"; +const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS = "audio/aac-adts"; const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4"; const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav"; diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index d12ac643bc15..dbd0829e881c 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -26,6 +26,8 @@ #include "include/MPEG2TSExtractor.h" #include "include/DRMExtractor.h" #include "include/WVMExtractor.h" +#include "include/FLACExtractor.h" +#include "include/AACExtractor.h" #include "matroska/MatroskaExtractor.h" @@ -85,6 +87,8 @@ sp<MediaExtractor> MediaExtractor::Create( } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) { return new AMRExtractor(source); + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) { + return new FLACExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) { return new WAVExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) { @@ -95,6 +99,8 @@ sp<MediaExtractor> MediaExtractor::Create( return new MPEG2TSExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) { return new WVMExtractor(source); + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) { + return new AACExtractor(source); } return NULL; diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp index 741aa1ca0124..c7b99b9bf4bf 100644 --- a/media/libstagefright/NuCachedSource2.cpp +++ b/media/libstagefright/NuCachedSource2.cpp @@ -477,8 +477,8 @@ void NuCachedSource2::resumeFetchingIfNecessary() { restartPrefetcherIfNecessary_l(true /* ignore low water threshold */); } -DecryptHandle* NuCachedSource2::DrmInitialization(DrmManagerClient* client) { - return mSource->DrmInitialization(client); +DecryptHandle* NuCachedSource2::DrmInitialization() { + return mSource->DrmInitialization(); } void NuCachedSource2::getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) { diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp index 653c85e5b91c..e39fab3bcf9c 100644 --- a/media/libstagefright/NuHTTPDataSource.cpp +++ b/media/libstagefright/NuHTTPDataSource.cpp @@ -24,22 +24,30 @@ static bool ParseSingleUnsignedLong( } static bool ParseURL( - const char *url, String8 *host, unsigned *port, String8 *path) { + const char *url, String8 *host, unsigned *port, + String8 *path, bool *https) { host->setTo(""); *port = 0; path->setTo(""); - if (strncasecmp("http://", url, 7)) { + size_t hostStart; + if (!strncasecmp("http://", url, 7)) { + hostStart = 7; + *https = false; + } else if (!strncasecmp("https://", url, 8)) { + hostStart = 8; + *https = true; + } else { return false; } - const char *slashPos = strchr(&url[7], '/'); + const char *slashPos = strchr(&url[hostStart], '/'); if (slashPos == NULL) { - host->setTo(&url[7]); + host->setTo(&url[hostStart]); path->setTo("/"); } else { - host->setTo(&url[7], slashPos - &url[7]); + host->setTo(&url[hostStart], slashPos - &url[hostStart]); path->setTo(slashPos); } @@ -57,7 +65,7 @@ static bool ParseURL( String8 tmp(host->string(), colonOffset); *host = tmp; } else { - *port = 80; + *port = (*https) ? 443 : 80; } return true; @@ -66,6 +74,7 @@ static bool ParseURL( NuHTTPDataSource::NuHTTPDataSource() : mState(DISCONNECTED), mPort(0), + mHTTPS(false), mOffset(0), mContentLength(0), mContentLengthValid(false), @@ -79,6 +88,17 @@ NuHTTPDataSource::NuHTTPDataSource() } NuHTTPDataSource::~NuHTTPDataSource() { + if (mDecryptHandle != NULL) { + // To release mDecryptHandle + CHECK(mDrmManagerClient); + mDrmManagerClient->closeDecryptSession(mDecryptHandle); + mDecryptHandle = NULL; + } + + if (mDrmManagerClient != NULL) { + delete mDrmManagerClient; + mDrmManagerClient = NULL; + } } status_t NuHTTPDataSource::connect( @@ -100,11 +120,12 @@ status_t NuHTTPDataSource::connect( mUri = uri; - if (!ParseURL(uri, &host, &port, &path)) { + bool https; + if (!ParseURL(uri, &host, &port, &path, &https)) { return ERROR_MALFORMED; } - return connect(host, port, path, headers, offset); + return connect(host, port, path, https, headers, offset); } static bool IsRedirectStatusCode(int httpStatus) { @@ -114,6 +135,7 @@ static bool IsRedirectStatusCode(int httpStatus) { status_t NuHTTPDataSource::connect( const char *host, unsigned port, const char *path, + bool https, const String8 &headers, off64_t offset) { LOGI("connect to %s:%u%s @%lld", host, port, path, offset); @@ -121,7 +143,7 @@ status_t NuHTTPDataSource::connect( bool needsToReconnect = true; if (mState == CONNECTED && host == mHost && port == mPort - && offset == mOffset) { + && https == mHTTPS && offset == mOffset) { if (mContentLengthValid && mOffset == mContentLength) { LOGI("Didn't have to reconnect, old one's still good."); needsToReconnect = false; @@ -131,6 +153,7 @@ status_t NuHTTPDataSource::connect( mHost = host; mPort = port; mPath = path; + mHTTPS = https; mHeaders = headers; status_t err = OK; @@ -139,7 +162,7 @@ status_t NuHTTPDataSource::connect( if (needsToReconnect) { mHTTP.disconnect(); - err = mHTTP.connect(host, port); + err = mHTTP.connect(host, port, https); } if (err != OK) { @@ -342,7 +365,7 @@ ssize_t NuHTTPDataSource::readAt(off64_t offset, void *data, size_t size) { String8 host = mHost; String8 path = mPath; String8 headers = mHeaders; - status_t err = connect(host, mPort, path, headers, offset); + status_t err = connect(host, mPort, path, mHTTPS, headers, offset); if (err != OK) { return err; @@ -486,11 +509,14 @@ void NuHTTPDataSource::addBandwidthMeasurement_l( } } -DecryptHandle* NuHTTPDataSource::DrmInitialization(DrmManagerClient* client) { - if (client == NULL) { +DecryptHandle* NuHTTPDataSource::DrmInitialization() { + if (mDrmManagerClient == NULL) { + mDrmManagerClient = new DrmManagerClient(); + } + + if (mDrmManagerClient == NULL) { return NULL; } - mDrmManagerClient = client; if (mDecryptHandle == NULL) { /* Note if redirect occurs, mUri is the redirect uri instead of the @@ -500,6 +526,7 @@ DecryptHandle* NuHTTPDataSource::DrmInitialization(DrmManagerClient* client) { } if (mDecryptHandle == NULL) { + delete mDrmManagerClient; mDrmManagerClient = NULL; } diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 2a19b25bfa20..5d502e7effbf 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1434,6 +1434,7 @@ OMXCodec::OMXCodec( mSeekTimeUs(-1), mSeekMode(ReadOptions::SEEK_CLOSEST_SYNC), mTargetTimeUs(-1), + mOutputPortSettingsChangedPending(false), mLeftOverBuffer(NULL), mPaused(false), mNativeWindow(nativeWindow) { @@ -2344,6 +2345,14 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) { drainInputBuffers(); fillOutputBuffers(); } + + if (mOutputPortSettingsChangedPending) { + CODEC_LOGV( + "Honoring deferred output port settings change."); + + mOutputPortSettingsChangedPending = false; + onPortSettingsChanged(kPortIndexOutput); + } } break; @@ -2407,6 +2416,8 @@ void OMXCodec::onStateChange(OMX_STATETYPE newState) { CODEC_LOGV("Now Executing."); + mOutputPortSettingsChangedPending = false; + setState(EXECUTING); // Buffers will be submitted to the component in the first @@ -2520,6 +2531,14 @@ void OMXCodec::onPortSettingsChanged(OMX_U32 portIndex) { CHECK_EQ((int)mState, (int)EXECUTING); CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput); + CHECK(!mOutputPortSettingsChangedPending); + + if (mPortStatus[kPortIndexOutput] != ENABLED) { + CODEC_LOGV("Deferring output port settings change."); + mOutputPortSettingsChangedPending = true; + return; + } + setState(RECONFIGURING); if (mQuirks & kNeedsFlushBeforeDisable) { @@ -2712,6 +2731,7 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) { if (srcBuffer->meta_data()->findInt64( kKeyTargetTime, &targetTimeUs) && targetTimeUs >= 0) { + CODEC_LOGV("targetTimeUs = %lld us", targetTimeUs); mTargetTimeUs = targetTimeUs; } else { mTargetTimeUs = -1; @@ -3385,6 +3405,14 @@ status_t OMXCodec::read( } if (seeking) { + while (mState == RECONFIGURING) { + mBufferFilled.wait(mLock); + } + + if (mState != EXECUTING) { + return UNKNOWN_ERROR; + } + CODEC_LOGV("seeking to %lld us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6); mSignalledEOS = false; diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp index cf622afffae0..0e51caf00bb0 100644 --- a/media/libstagefright/OggExtractor.cpp +++ b/media/libstagefright/OggExtractor.cpp @@ -114,7 +114,6 @@ private: MediaBuffer *buffer, uint8_t type); void parseFileMetaData(); - void extractAlbumArt(const void *data, size_t size); uint64_t findPrevGranulePosition(off64_t pageOffset); @@ -122,6 +121,9 @@ private: MyVorbisExtractor &operator=(const MyVorbisExtractor &); }; +static void extractAlbumArt( + const sp<MetaData> &fileMeta, const void *data, size_t size); + //////////////////////////////////////////////////////////////////////////////// OggSource::OggSource(const sp<OggExtractor> &extractor) @@ -654,6 +656,17 @@ void MyVorbisExtractor::parseFileMetaData() { mFileMeta = new MetaData; mFileMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG); + for (int i = 0; i < mVc.comments; ++i) { + const char *comment = mVc.user_comments[i]; + size_t commentLength = mVc.comment_lengths[i]; + parseVorbisComment(mFileMeta, comment, commentLength); + //LOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]); + } +} + +void parseVorbisComment( + const sp<MetaData> &fileMeta, const char *comment, size_t commentLength) +{ struct { const char *const mTag; uint32_t mKey; @@ -675,33 +688,25 @@ void MyVorbisExtractor::parseFileMetaData() { { "ANDROID_LOOP", kKeyAutoLoop }, }; - for (int i = 0; i < mVc.comments; ++i) { - const char *comment = mVc.user_comments[i]; - for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) { size_t tagLen = strlen(kMap[j].mTag); if (!strncasecmp(kMap[j].mTag, comment, tagLen) && comment[tagLen] == '=') { if (kMap[j].mKey == kKeyAlbumArt) { extractAlbumArt( + fileMeta, &comment[tagLen + 1], - mVc.comment_lengths[i] - tagLen - 1); + commentLength - tagLen - 1); } else if (kMap[j].mKey == kKeyAutoLoop) { if (!strcasecmp(&comment[tagLen + 1], "true")) { - mFileMeta->setInt32(kKeyAutoLoop, true); + fileMeta->setInt32(kKeyAutoLoop, true); } } else { - mFileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]); + fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]); } } } - } -#if 0 - for (int i = 0; i < mVc.comments; ++i) { - LOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]); - } -#endif } // The returned buffer should be free()d. @@ -769,7 +774,8 @@ static uint8_t *DecodeBase64(const char *s, size_t size, size_t *outSize) { return (uint8_t *)buffer; } -void MyVorbisExtractor::extractAlbumArt(const void *data, size_t size) { +static void extractAlbumArt( + const sp<MetaData> &fileMeta, const void *data, size_t size) { LOGV("extractAlbumArt from '%s'", (const char *)data); size_t flacSize; @@ -833,10 +839,10 @@ void MyVorbisExtractor::extractAlbumArt(const void *data, size_t size) { LOGV("got image data, %d trailing bytes", flacSize - 32 - typeLen - descLen - dataLen); - mFileMeta->setData( + fileMeta->setData( kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen); - mFileMeta->setCString(kKeyAlbumArtMIME, type); + fileMeta->setCString(kKeyAlbumArtMIME, type); exit: free(flac); diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp index 062ab9bbfaa7..c7b00b171974 100644 --- a/media/libstagefright/SampleIterator.cpp +++ b/media/libstagefright/SampleIterator.cpp @@ -307,6 +307,8 @@ status_t SampleIterator::findSampleTime( *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex); + *time += mTable->getCompositionTimeOffset(sampleIndex); + return OK; } diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp index a9163fcc7ad2..423df705a592 100644 --- a/media/libstagefright/SampleTable.cpp +++ b/media/libstagefright/SampleTable.cpp @@ -53,6 +53,8 @@ SampleTable::SampleTable(const sp<DataSource> &source) mNumSampleSizes(0), mTimeToSampleCount(0), mTimeToSample(NULL), + mCompositionTimeDeltaEntries(NULL), + mNumCompositionTimeDeltaEntries(0), mSyncSampleOffset(-1), mNumSyncSamples(0), mSyncSamples(NULL), @@ -68,6 +70,9 @@ SampleTable::~SampleTable() { delete[] mSyncSamples; mSyncSamples = NULL; + delete[] mCompositionTimeDeltaEntries; + mCompositionTimeDeltaEntries = NULL; + delete[] mTimeToSample; mTimeToSample = NULL; @@ -260,6 +265,51 @@ status_t SampleTable::setTimeToSampleParams( return OK; } +status_t SampleTable::setCompositionTimeToSampleParams( + off64_t data_offset, size_t data_size) { + LOGI("There are reordered frames present."); + + if (mCompositionTimeDeltaEntries != NULL || data_size < 8) { + return ERROR_MALFORMED; + } + + uint8_t header[8]; + if (mDataSource->readAt( + data_offset, header, sizeof(header)) + < (ssize_t)sizeof(header)) { + return ERROR_IO; + } + + if (U32_AT(header) != 0) { + // Expected version = 0, flags = 0. + return ERROR_MALFORMED; + } + + size_t numEntries = U32_AT(&header[4]); + + if (data_size != (numEntries + 1) * 8) { + return ERROR_MALFORMED; + } + + mNumCompositionTimeDeltaEntries = numEntries; + mCompositionTimeDeltaEntries = new uint32_t[2 * numEntries]; + + if (mDataSource->readAt( + data_offset + 8, mCompositionTimeDeltaEntries, numEntries * 8) + < (ssize_t)numEntries * 8) { + delete[] mCompositionTimeDeltaEntries; + mCompositionTimeDeltaEntries = NULL; + + return ERROR_IO; + } + + for (size_t i = 0; i < 2 * numEntries; ++i) { + mCompositionTimeDeltaEntries[i] = ntohl(mCompositionTimeDeltaEntries[i]); + } + + return OK; +} + status_t SampleTable::setSyncSampleParams(off64_t data_offset, size_t data_size) { if (mSyncSampleOffset >= 0 || data_size < 8) { return ERROR_MALFORMED; @@ -333,6 +383,8 @@ uint32_t abs_difference(uint32_t time1, uint32_t time2) { status_t SampleTable::findSampleAtTime( uint32_t req_time, uint32_t *sample_index, uint32_t flags) { + // XXX this currently uses decoding time, instead of composition time. + *sample_index = 0; Mutex::Autolock autoLock(mLock); @@ -607,5 +659,26 @@ status_t SampleTable::getMetaDataForSample( return OK; } +uint32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) const { + if (mCompositionTimeDeltaEntries == NULL) { + return 0; + } + + uint32_t curSample = 0; + for (size_t i = 0; i < mNumCompositionTimeDeltaEntries; ++i) { + uint32_t sampleCount = mCompositionTimeDeltaEntries[2 * i]; + + if (sampleIndex < curSample + sampleCount) { + uint32_t sampleDelta = mCompositionTimeDeltaEntries[2 * i + 1]; + + return sampleDelta; + } + + curSample += sampleCount; + } + + return 0; +} + } // namespace android diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index be3df7c58a08..84f65ff11fdb 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -37,7 +37,7 @@ static bool FileHasAcceptableExtension(const char *extension) { ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac", ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota", - ".mkv", ".mka", ".webm", ".ts", ".fl" + ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac" }; static const size_t kNumValidExtensions = sizeof(kValidExtensions) / sizeof(kValidExtensions[0]); diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp index fa12cf0a6ed0..95cf2d39c693 100644 --- a/media/libstagefright/avc_utils.cpp +++ b/media/libstagefright/avc_utils.cpp @@ -329,5 +329,52 @@ bool IsIDR(const sp<ABuffer> &buffer) { return foundIDR; } +sp<MetaData> MakeAACCodecSpecificData( + unsigned profile, unsigned sampling_freq_index, + unsigned channel_configuration) { + sp<MetaData> meta = new MetaData; + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); + + CHECK_LE(sampling_freq_index, 11u); + static const int32_t kSamplingFreq[] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000 + }; + meta->setInt32(kKeySampleRate, kSamplingFreq[sampling_freq_index]); + meta->setInt32(kKeyChannelCount, channel_configuration); + + static const uint8_t kStaticESDS[] = { + 0x03, 22, + 0x00, 0x00, // ES_ID + 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag + + 0x04, 17, + 0x40, // Audio ISO/IEC 14496-3 + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x05, 2, + // AudioSpecificInfo follows + + // oooo offf fccc c000 + // o - audioObjectType + // f - samplingFreqIndex + // c - channelConfig + }; + sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2); + memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS)); + + csd->data()[sizeof(kStaticESDS)] = + ((profile + 1) << 3) | (sampling_freq_index >> 1); + + csd->data()[sizeof(kStaticESDS) + 1] = + ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3); + + meta->setData(kKeyESDS, 0, csd->data(), csd->size()); + + return meta; +} + } // namespace android diff --git a/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp b/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp index 9433178c0b8c..489e5ade7341 100644 --- a/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp +++ b/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp @@ -205,7 +205,9 @@ status_t VPXDecoder::read( vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); if (img == NULL) { - LOGI("on2 decoder did not return a frame."); + // The VPX format supports "internal-only" frames that are + // referenced by future content but never actually displayed, so + // this is a perfectly valid scenario. *out = new MediaBuffer(0); return OK; diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 0bed3caf793f..ee845a3cc91d 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -168,18 +168,6 @@ void LiveSession::onConnect(const sp<AMessage> &msg) { CHECK_GT(mBandwidthItems.size(), 0u); mBandwidthItems.sort(SortByBandwidth); - - char value[PROPERTY_VALUE_MAX]; - if (property_get("media.httplive.disable-nuplayer", value, NULL) - && (!strcasecmp(value, "true") || !strcmp(value, "1"))) { - // The "legacy" player cannot deal with audio format changes, - // some streams use different audio encoding parameters for - // their lowest bandwidth stream. - if (mBandwidthItems.size() > 1) { - // XXX Remove the lowest bitrate stream for now... - mBandwidthItems.removeAt(0); - } - } } postMonitorQueue(); @@ -201,7 +189,8 @@ status_t LiveSession::fetchFile(const char *url, sp<ABuffer> *out) { if (!strncasecmp(url, "file://", 7)) { source = new FileSource(url + 7); - } else if (strncasecmp(url, "http://", 7)) { + } else if (strncasecmp(url, "http://", 7) + && strncasecmp(url, "https://", 8)) { return ERROR_UNSUPPORTED; } else { { diff --git a/media/libstagefright/include/AACExtractor.h b/media/libstagefright/include/AACExtractor.h new file mode 100644 index 000000000000..8e5657b5faf8 --- /dev/null +++ b/media/libstagefright/include/AACExtractor.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAC_EXTRACTOR_H_ + +#define AAC_EXTRACTOR_H_ + +#include <media/stagefright/MediaExtractor.h> + +#include <utils/Vector.h> + +namespace android { + +struct AMessage; +class String8; + +class AACExtractor : public MediaExtractor { +public: + AACExtractor(const sp<DataSource> &source); + + virtual size_t countTracks(); + virtual sp<MediaSource> getTrack(size_t index); + virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); + + virtual sp<MetaData> getMetaData(); + +protected: + virtual ~AACExtractor(); + +private: + sp<DataSource> mDataSource; + sp<MetaData> mMeta; + status_t mInitCheck; + + Vector<uint64_t> mOffsetVector; + int64_t mFrameDurationUs; + + AACExtractor(const AACExtractor &); + AACExtractor &operator=(const AACExtractor &); +}; + +bool SniffAAC( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *); + +} // namespace android + +#endif // AAC_EXTRACTOR_H_ diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h index 1cdf36de0d16..589d8370e283 100644 --- a/media/libstagefright/include/AMRExtractor.h +++ b/media/libstagefright/include/AMRExtractor.h @@ -24,6 +24,7 @@ namespace android { struct AMessage; class String8; +#define OFFSET_TABLE_LEN 300 class AMRExtractor : public MediaExtractor { public: @@ -42,9 +43,11 @@ private: sp<DataSource> mDataSource; sp<MetaData> mMeta; status_t mInitCheck; - size_t mFrameSize; bool mIsWide; + off64_t mOffsetTable[OFFSET_TABLE_LEN]; //5 min + size_t mOffsetTableLength; + AMRExtractor(const AMRExtractor &); AMRExtractor &operator=(const AMRExtractor &); }; diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 41ef181caef9..1497732a2c7e 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -39,14 +39,10 @@ struct NuCachedSource2; struct ALooper; struct ARTSPController; -struct ARTPSession; -struct UDPPusher; class DrmManagerClinet; class DecryptHandle; -struct LiveSession; - struct AwesomeRenderer : public RefBase { AwesomeRenderer() {} @@ -119,6 +115,13 @@ private: // sufficient data to begin playback and finish the preparation phase // for good. PREPARING_CONNECTED = 2048, + + // We're triggering a single video event to display the first frame + // after the seekpoint. + SEEK_PREVIEW = 4096, + + AUDIO_RUNNING = 8192, + AUDIOPLAYER_STARTED = 16384, }; mutable Mutex mLock; @@ -200,10 +203,7 @@ private: sp<ALooper> mLooper; sp<ARTSPController> mRTSPController; - sp<ARTPSession> mRTPSession; - sp<UDPPusher> mRTPPusher, mRTCPPusher; - - sp<LiveSession> mLiveSession; + sp<ARTSPController> mConnectingRTSPController; DrmManagerClient *mDrmManagerClient; DecryptHandle *mDecryptHandle; @@ -215,7 +215,6 @@ private: status_t setDataSource_l(const sp<DataSource> &dataSource); status_t setDataSource_l(const sp<MediaExtractor> &extractor); void reset_l(); - void partial_reset_l(); status_t seekTo_l(int64_t timeUs); status_t pause_l(bool at_eos = false); void initRenderer_l(); @@ -256,6 +255,8 @@ private: void finishSeekIfNecessary(int64_t videoTimeUs); void ensureCacheIsFetching_l(); + status_t startAudioPlayer_l(); + AwesomePlayer(const AwesomePlayer &); AwesomePlayer &operator=(const AwesomePlayer &); }; diff --git a/media/libstagefright/include/DRMExtractor.h b/media/libstagefright/include/DRMExtractor.h index cafc812f0dff..9881cc19f500 100644 --- a/media/libstagefright/include/DRMExtractor.h +++ b/media/libstagefright/include/DRMExtractor.h @@ -46,6 +46,7 @@ private: sp<MediaExtractor> mOriginalExtractor; DecryptHandle* mDecryptHandle; + DrmManagerClient* mDrmManagerClient; DRMExtractor(const DRMExtractor &); DRMExtractor &operator=(const DRMExtractor &); diff --git a/media/libstagefright/include/FLACExtractor.h b/media/libstagefright/include/FLACExtractor.h new file mode 100644 index 000000000000..ded91c27e547 --- /dev/null +++ b/media/libstagefright/include/FLACExtractor.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLAC_EXTRACTOR_H_ +#define FLAC_EXTRACTOR_H_ + +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaExtractor.h> +#include <utils/String8.h> + +namespace android { + +class FLACParser; + +class FLACExtractor : public MediaExtractor { + +public: + // Extractor assumes ownership of source + FLACExtractor(const sp<DataSource> &source); + + virtual size_t countTracks(); + virtual sp<MediaSource> getTrack(size_t index); + virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); + + virtual sp<MetaData> getMetaData(); + +protected: + virtual ~FLACExtractor(); + +private: + sp<DataSource> mDataSource; + sp<FLACParser> mParser; + status_t mInitCheck; + sp<MetaData> mFileMetadata; + + // There is only one track + sp<MetaData> mTrackMetadata; + + status_t init(); + + FLACExtractor(const FLACExtractor &); + FLACExtractor &operator=(const FLACExtractor &); + +}; + +bool SniffFLAC(const sp<DataSource> &source, String8 *mimeType, + float *confidence, sp<AMessage> *); + +} // namespace android + +#endif // FLAC_EXTRACTOR_H_ diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h index 545cd0c431ed..09e6a5fbdf52 100644 --- a/media/libstagefright/include/HTTPStream.h +++ b/media/libstagefright/include/HTTPStream.h @@ -32,7 +32,7 @@ public: HTTPStream(); ~HTTPStream(); - status_t connect(const char *server, int port = 80); + status_t connect(const char *server, int port = -1, bool https = false); status_t disconnect(); status_t send(const char *data, size_t size); @@ -71,6 +71,9 @@ private: KeyedVector<AString, AString> mHeaders; + void *mSSLContext; + void *mSSL; + HTTPStream(const HTTPStream &); HTTPStream &operator=(const HTTPStream &); }; diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h index 28840be37191..022804cba5ef 100644 --- a/media/libstagefright/include/NuCachedSource2.h +++ b/media/libstagefright/include/NuCachedSource2.h @@ -37,7 +37,7 @@ struct NuCachedSource2 : public DataSource { virtual status_t getSize(off64_t *size); virtual uint32_t flags(); - virtual DecryptHandle* DrmInitialization(DrmManagerClient *client); + virtual DecryptHandle* DrmInitialization(); virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client); virtual String8 getUri(); //////////////////////////////////////////////////////////////////////////// diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h index c8e93bea6791..3918434e3709 100644 --- a/media/libstagefright/include/NuHTTPDataSource.h +++ b/media/libstagefright/include/NuHTTPDataSource.h @@ -31,7 +31,7 @@ struct NuHTTPDataSource : public DataSource { // false otherwise. bool estimateBandwidth(int32_t *bandwidth_bps); - virtual DecryptHandle* DrmInitialization(DrmManagerClient *client); + virtual DecryptHandle* DrmInitialization(); virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client); virtual String8 getUri(); @@ -57,6 +57,7 @@ private: String8 mHost; unsigned mPort; String8 mPath; + bool mHTTPS; String8 mHeaders; String8 mUri; @@ -83,6 +84,7 @@ private: status_t connect( const char *host, unsigned port, const char *path, + bool https, const String8 &headers, off64_t offset); diff --git a/media/libstagefright/include/OggExtractor.h b/media/libstagefright/include/OggExtractor.h index 1eda025d4288..a41f681e7af2 100644 --- a/media/libstagefright/include/OggExtractor.h +++ b/media/libstagefright/include/OggExtractor.h @@ -57,6 +57,9 @@ bool SniffOgg( const sp<DataSource> &source, String8 *mimeType, float *confidence, sp<AMessage> *); +void parseVorbisComment( + const sp<MetaData> &fileMeta, const char *comment, size_t commentLength); + } // namespace android #endif // OGG_EXTRACTOR_H_ diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h index c5e81365da1e..2f95de9ce5ed 100644 --- a/media/libstagefright/include/SampleTable.h +++ b/media/libstagefright/include/SampleTable.h @@ -46,6 +46,9 @@ public: status_t setTimeToSampleParams(off64_t data_offset, size_t data_size); + status_t setCompositionTimeToSampleParams( + off64_t data_offset, size_t data_size); + status_t setSyncSampleParams(off64_t data_offset, size_t data_size); //////////////////////////////////////////////////////////////////////////// @@ -104,6 +107,9 @@ private: uint32_t mTimeToSampleCount; uint32_t *mTimeToSample; + uint32_t *mCompositionTimeDeltaEntries; + size_t mNumCompositionTimeDeltaEntries; + off64_t mSyncSampleOffset; uint32_t mNumSyncSamples; uint32_t *mSyncSamples; @@ -122,6 +128,8 @@ private: status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size); + uint32_t getCompositionTimeOffset(uint32_t sampleIndex) const; + SampleTable(const SampleTable &); SampleTable &operator=(const SampleTable &); }; diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h index 3aeb07f62e07..02187552723a 100644 --- a/media/libstagefright/include/avc_utils.h +++ b/media/libstagefright/include/avc_utils.h @@ -52,6 +52,10 @@ bool IsIDR(const sp<ABuffer> &accessUnit); const char *AVCProfileToString(uint8_t profile); +sp<MetaData> MakeAACCodecSpecificData( + unsigned profile, unsigned sampling_freq_index, + unsigned channel_configuration); + } // namespace android #endif // AVC_UTILS_H_ diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 4335b999d579..60567395f07f 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -371,21 +371,6 @@ void ATSParser::Stream::signalDiscontinuity(DiscontinuityType type) { mBuffer->setRange(0, 0); switch (type) { - case DISCONTINUITY_HTTPLIVE: - { - mQueue.clear(true); - - if (mStreamType == 0x1b) { - // Don't signal discontinuities on audio streams. - if (mSource != NULL) { - mSource->queueDiscontinuity(type); - } else { - deferDiscontinuity(type); - } - } - break; - } - case DISCONTINUITY_SEEK: case DISCONTINUITY_FORMATCHANGE: { diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h index ec3be8468a8b..455f9d5c7db1 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.h +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -34,7 +34,6 @@ struct MediaSource; struct ATSParser : public RefBase { enum DiscontinuityType { DISCONTINUITY_NONE, - DISCONTINUITY_HTTPLIVE, DISCONTINUITY_SEEK, DISCONTINUITY_FORMATCHANGE }; diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index 73efdfe34ac8..dcaf9f7441bb 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -341,54 +341,6 @@ int64_t ElementaryStreamQueue::fetchTimestamp(size_t size) { return timeUs; } -// static -sp<MetaData> ElementaryStreamQueue::MakeAACCodecSpecificData( - unsigned profile, unsigned sampling_freq_index, - unsigned channel_configuration) { - sp<MetaData> meta = new MetaData; - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); - - CHECK_LE(sampling_freq_index, 11u); - static const int32_t kSamplingFreq[] = { - 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, - 16000, 12000, 11025, 8000 - }; - meta->setInt32(kKeySampleRate, kSamplingFreq[sampling_freq_index]); - meta->setInt32(kKeyChannelCount, channel_configuration); - - static const uint8_t kStaticESDS[] = { - 0x03, 22, - 0x00, 0x00, // ES_ID - 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag - - 0x04, 17, - 0x40, // Audio ISO/IEC 14496-3 - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - - 0x05, 2, - // AudioSpecificInfo follows - - // oooo offf fccc c000 - // o - audioObjectType - // f - samplingFreqIndex - // c - channelConfig - }; - sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2); - memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS)); - - csd->data()[sizeof(kStaticESDS)] = - ((profile + 1) << 3) | (sampling_freq_index >> 1); - - csd->data()[sizeof(kStaticESDS) + 1] = - ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3); - - meta->setData(kKeyESDS, 0, csd->data(), csd->size()); - - return meta; -} - struct NALPosition { size_t nalOffset; size_t nalSize; diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h index 5b7957e6f937..d08199533188 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.h +++ b/media/libstagefright/mpeg2ts/ESQueue.h @@ -61,10 +61,6 @@ private: // returns its timestamp in us (or -1 if no time information). int64_t fetchTimestamp(size_t size); - static sp<MetaData> MakeAACCodecSpecificData( - unsigned profile, unsigned sampling_freq_index, - unsigned channel_configuration); - DISALLOW_EVIL_CONSTRUCTORS(ElementaryStreamQueue); }; diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp index a1f0796c3740..dfec47f4765b 100644 --- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp +++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp @@ -202,20 +202,13 @@ void MPEG2TSExtractor::init() { LOGI("haveAudio=%d, haveVideo=%d", haveAudio, haveVideo); } -static bool isDiscontinuity(const uint8_t *data, ssize_t size) { - return size == 188 && data[0] == 0x00; -} - status_t MPEG2TSExtractor::feedMore() { Mutex::Autolock autoLock(mLock); uint8_t packet[kTSPacketSize]; ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize); - if (isDiscontinuity(packet, n)) { - LOGI("XXX discontinuity detected"); - mParser->signalDiscontinuity(ATSParser::DISCONTINUITY_HTTPLIVE); - } else if (n < (ssize_t)kTSPacketSize) { + if (n < (ssize_t)kTSPacketSize) { return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM; } else { mParser->feedTSPacket(packet, kTSPacketSize); diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp index 13988cd1d569..9f6bd29eecab 100644 --- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp @@ -104,7 +104,7 @@ AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler( mNextExpectedSeqNoValid(false), mNextExpectedSeqNo(0), mAccessUnitDamaged(false) { - mIsGeneric = desc.startsWith("mpeg4-generic/"); + mIsGeneric = !strncasecmp(desc.c_str(),"mpeg4-generic/", 14); if (mIsGeneric) { AString value; diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp index f0b858da1d25..679dcab7c057 100644 --- a/media/libstagefright/rtsp/APacketSource.cpp +++ b/media/libstagefright/rtsp/APacketSource.cpp @@ -637,7 +637,7 @@ APacketSource::APacketSource( mFormat->setInt32(kKeyWidth, width); mFormat->setInt32(kKeyHeight, height); - } else if (!strncmp(desc.c_str(), "mpeg4-generic/", 14)) { + } else if (!strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) { AString val; if (!GetAttribute(params.c_str(), "mode", &val) || (strcasecmp(val.c_str(), "AAC-lbr") diff --git a/media/libstagefright/rtsp/ARTPAssembler.cpp b/media/libstagefright/rtsp/ARTPAssembler.cpp index 9ba2b37c8899..a897c10da4a1 100644 --- a/media/libstagefright/rtsp/ARTPAssembler.cpp +++ b/media/libstagefright/rtsp/ARTPAssembler.cpp @@ -65,13 +65,9 @@ void ARTPAssembler::onPacketReceived(const sp<ARTPSource> &source) { // static void ARTPAssembler::CopyTimes(const sp<ABuffer> &to, const sp<ABuffer> &from) { - uint64_t ntpTime; - CHECK(from->meta()->findInt64("ntp-time", (int64_t *)&ntpTime)); - uint32_t rtpTime; CHECK(from->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); - to->meta()->setInt64("ntp-time", ntpTime); to->meta()->setInt32("rtp-time", rtpTime); // Copy the seq number. diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp index 5a1ea5cd46ef..47de4e0938d0 100644 --- a/media/libstagefright/rtsp/ARTPConnection.cpp +++ b/media/libstagefright/rtsp/ARTPConnection.cpp @@ -123,7 +123,7 @@ void ARTPConnection::MakePortPair( struct sockaddr_in addr; memset(addr.sin_zero, 0, sizeof(addr.sin_zero)); addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(port); if (bind(*rtpSocket, @@ -169,12 +169,6 @@ void ARTPConnection::onMessageReceived(const sp<AMessage> &msg) { break; } - case kWhatFakeTimestamps: - { - onFakeTimestamps(); - break; - } - default: { TRESPASS(); @@ -346,6 +340,8 @@ void ARTPConnection::onPollStreams() { } status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) { + LOGV("receiving %s", receiveRTP ? "RTP" : "RTCP"); + CHECK(!s->mIsInjected); sp<ABuffer> buffer = new ABuffer(65536); @@ -461,12 +457,6 @@ status_t ARTPConnection::parseRTP(StreamInfo *s, const sp<ABuffer> &buffer) { buffer->setInt32Data(u16at(&data[2])); buffer->setRange(payloadOffset, size - payloadOffset); - if ((mFlags & kFakeTimestamps) && !source->timeEstablished()) { - source->timeUpdate(rtpTime, 0); - source->timeUpdate(rtpTime + 90000, 0x100000000ll); - CHECK(source->timeEstablished()); - } - source->processRTPPacket(buffer); return OK; @@ -592,9 +582,7 @@ status_t ARTPConnection::parseSR( sp<ARTPSource> source = findSource(s, id); - if ((mFlags & kFakeTimestamps) == 0) { - source->timeUpdate(rtpTime, ntpTime); - } + source->timeUpdate(rtpTime, ntpTime); return 0; } @@ -652,27 +640,5 @@ void ARTPConnection::onInjectPacket(const sp<AMessage> &msg) { } } -void ARTPConnection::fakeTimestamps() { - (new AMessage(kWhatFakeTimestamps, id()))->post(); -} - -void ARTPConnection::onFakeTimestamps() { - List<StreamInfo>::iterator it = mStreams.begin(); - while (it != mStreams.end()) { - StreamInfo &info = *it++; - - for (size_t j = 0; j < info.mSources.size(); ++j) { - sp<ARTPSource> source = info.mSources.valueAt(j); - - if (!source->timeEstablished()) { - source->timeUpdate(0, 0); - source->timeUpdate(0 + 90000, 0x100000000ll); - - mFlags |= kFakeTimestamps; - } - } - } -} - } // namespace android diff --git a/media/libstagefright/rtsp/ARTPConnection.h b/media/libstagefright/rtsp/ARTPConnection.h index a17b382508bb..edbcc35d2840 100644 --- a/media/libstagefright/rtsp/ARTPConnection.h +++ b/media/libstagefright/rtsp/ARTPConnection.h @@ -29,7 +29,6 @@ struct ASessionDescription; struct ARTPConnection : public AHandler { enum Flags { - kFakeTimestamps = 1, kRegularlyRequestFIR = 2, }; @@ -51,8 +50,6 @@ struct ARTPConnection : public AHandler { static void MakePortPair( int *rtpSocket, int *rtcpSocket, unsigned *rtpPort); - void fakeTimestamps(); - protected: virtual ~ARTPConnection(); virtual void onMessageReceived(const sp<AMessage> &msg); @@ -63,7 +60,6 @@ private: kWhatRemoveStream, kWhatPollStreams, kWhatInjectPacket, - kWhatFakeTimestamps, }; static const int64_t kSelectTimeoutUs; @@ -81,7 +77,6 @@ private: void onPollStreams(); void onInjectPacket(const sp<AMessage> &msg); void onSendReceiverReports(); - void onFakeTimestamps(); status_t receive(StreamInfo *info, bool receiveRTP); diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp index 39c6619a7c9b..c6bcb1225a77 100644 --- a/media/libstagefright/rtsp/ARTPSession.cpp +++ b/media/libstagefright/rtsp/ARTPSession.cpp @@ -44,9 +44,7 @@ status_t ARTPSession::setup(const sp<ASessionDescription> &desc) { mDesc = desc; - mRTPConn = new ARTPConnection( - ARTPConnection::kFakeTimestamps - | ARTPConnection::kRegularlyRequestFIR); + mRTPConn = new ARTPConnection(ARTPConnection::kRegularlyRequestFIR); looper()->registerHandler(mRTPConn); diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp index 5aae4e7d779a..84c666f08f64 100644 --- a/media/libstagefright/rtsp/ARTPSource.cpp +++ b/media/libstagefright/rtsp/ARTPSource.cpp @@ -42,12 +42,12 @@ ARTPSource::ARTPSource( : mID(id), mHighestSeqNumber(0), mNumBuffersReceived(0), - mNumTimes(0), mLastNTPTime(0), mLastNTPTimeUpdateUs(0), mIssueFIRRequests(false), mLastFIRRequestUs(-1), - mNextFIRSeqNo((rand() * 256.0) / RAND_MAX) { + mNextFIRSeqNo((rand() * 256.0) / RAND_MAX), + mNotify(notify) { unsigned long PT; AString desc; AString params; @@ -67,7 +67,7 @@ ARTPSource::ARTPSource( } else if (!strncmp(desc.c_str(), "AMR-WB/", 7)) { mAssembler = new AAMRAssembler(notify, true /* isWide */, params); } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8) - || !strncmp(desc.c_str(), "mpeg4-generic/", 14)) { + || !strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) { mAssembler = new AMPEG4ElementaryAssembler(notify, desc, params); mIssueFIRRequests = true; } else { @@ -80,52 +80,25 @@ static uint32_t AbsDiff(uint32_t seq1, uint32_t seq2) { } void ARTPSource::processRTPPacket(const sp<ABuffer> &buffer) { - if (queuePacket(buffer) - && mNumTimes == 2 - && mAssembler != NULL) { + if (queuePacket(buffer) && mAssembler != NULL) { mAssembler->onPacketReceived(this); } } void ARTPSource::timeUpdate(uint32_t rtpTime, uint64_t ntpTime) { - LOGV("timeUpdate"); - mLastNTPTime = ntpTime; mLastNTPTimeUpdateUs = ALooper::GetNowUs(); - if (mNumTimes == 2) { - mNTPTime[0] = mNTPTime[1]; - mRTPTime[0] = mRTPTime[1]; - mNumTimes = 1; - } - mNTPTime[mNumTimes] = ntpTime; - mRTPTime[mNumTimes++] = rtpTime; - - if (timeEstablished()) { - for (List<sp<ABuffer> >::iterator it = mQueue.begin(); - it != mQueue.end(); ++it) { - sp<AMessage> meta = (*it)->meta(); - - uint32_t rtpTime; - CHECK(meta->findInt32("rtp-time", (int32_t *)&rtpTime)); - - meta->setInt64("ntp-time", RTP2NTP(rtpTime)); - } - } + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("time-update", true); + notify->setInt32("rtp-time", rtpTime); + notify->setInt64("ntp-time", ntpTime); + notify->post(); } bool ARTPSource::queuePacket(const sp<ABuffer> &buffer) { uint32_t seqNum = (uint32_t)buffer->int32Data(); - if (mNumTimes == 2) { - sp<AMessage> meta = buffer->meta(); - - uint32_t rtpTime; - CHECK(meta->findInt32("rtp-time", (int32_t *)&rtpTime)); - - meta->setInt64("ntp-time", RTP2NTP(rtpTime)); - } - if (mNumBuffersReceived++ == 0) { mHighestSeqNumber = seqNum; mQueue.push_back(buffer); @@ -180,14 +153,6 @@ bool ARTPSource::queuePacket(const sp<ABuffer> &buffer) { return true; } -uint64_t ARTPSource::RTP2NTP(uint32_t rtpTime) const { - CHECK_EQ(mNumTimes, 2u); - - return mNTPTime[0] + (double)(mNTPTime[1] - mNTPTime[0]) - * ((double)rtpTime - (double)mRTPTime[0]) - / (double)(mRTPTime[1] - mRTPTime[0]); -} - void ARTPSource::byeReceived() { mAssembler->onByeReceived(); } diff --git a/media/libstagefright/rtsp/ARTPSource.h b/media/libstagefright/rtsp/ARTPSource.h index e62c3f19347e..b70f94e496c8 100644 --- a/media/libstagefright/rtsp/ARTPSource.h +++ b/media/libstagefright/rtsp/ARTPSource.h @@ -46,10 +46,6 @@ struct ARTPSource : public RefBase { void addReceiverReport(const sp<ABuffer> &buffer); void addFIR(const sp<ABuffer> &buffer); - bool timeEstablished() const { - return mNumTimes == 2; - } - private: uint32_t mID; uint32_t mHighestSeqNumber; @@ -58,10 +54,6 @@ private: List<sp<ABuffer> > mQueue; sp<ARTPAssembler> mAssembler; - size_t mNumTimes; - uint64_t mNTPTime[2]; - uint32_t mRTPTime[2]; - uint64_t mLastNTPTime; int64_t mLastNTPTimeUpdateUs; @@ -69,7 +61,7 @@ private: int64_t mLastFIRRequestUs; uint8_t mNextFIRSeqNo; - uint64_t RTP2NTP(uint32_t rtpTime) const; + sp<AMessage> mNotify; bool queuePacket(const sp<ABuffer> &buffer); diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp index e93692377c97..0740515c6d43 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.cpp +++ b/media/libstagefright/rtsp/ARTSPConnection.cpp @@ -545,6 +545,10 @@ sp<ABuffer> ARTSPConnection::receiveBinaryData() { return buffer; } +static bool IsRTSPVersion(const AString &s) { + return s == "RTSP/1.0"; +} + bool ARTSPConnection::receiveRTSPReponse() { AString statusLine; @@ -584,13 +588,27 @@ bool ARTSPConnection::receiveRTSPReponse() { return false; } - AString statusCodeStr( - response->mStatusLine, space1 + 1, space2 - space1 - 1); + bool isRequest = false; - if (!ParseSingleUnsignedLong( - statusCodeStr.c_str(), &response->mStatusCode) - || response->mStatusCode < 100 || response->mStatusCode > 999) { - return false; + if (!IsRTSPVersion(AString(response->mStatusLine, 0, space1))) { + CHECK(IsRTSPVersion( + AString( + response->mStatusLine, + space2 + 1, + response->mStatusLine.size() - space2 - 1))); + + isRequest = true; + + response->mStatusCode = 0; + } else { + AString statusCodeStr( + response->mStatusLine, space1 + 1, space2 - space1 - 1); + + if (!ParseSingleUnsignedLong( + statusCodeStr.c_str(), &response->mStatusCode) + || response->mStatusCode < 100 || response->mStatusCode > 999) { + return false; + } } AString line; @@ -680,7 +698,63 @@ bool ARTSPConnection::receiveRTSPReponse() { } } - return notifyResponseListener(response); + return isRequest + ? handleServerRequest(response) + : notifyResponseListener(response); +} + +bool ARTSPConnection::handleServerRequest(const sp<ARTSPResponse> &request) { + // Implementation of server->client requests is optional for all methods + // but we do need to respond, even if it's just to say that we don't + // support the method. + + ssize_t space1 = request->mStatusLine.find(" "); + CHECK_GE(space1, 0); + + AString response; + response.append("RTSP/1.0 501 Not Implemented\r\n"); + + ssize_t i = request->mHeaders.indexOfKey("cseq"); + + if (i >= 0) { + AString value = request->mHeaders.valueAt(i); + + unsigned long cseq; + if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) { + return false; + } + + response.append("CSeq: "); + response.append(cseq); + response.append("\r\n"); + } + + response.append("\r\n"); + + size_t numBytesSent = 0; + while (numBytesSent < response.size()) { + ssize_t n = + send(mSocket, response.c_str() + numBytesSent, + response.size() - numBytesSent, 0); + + if (n == 0) { + // Server closed the connection. + LOGE("Server unexpectedly closed the connection."); + + return false; + } else if (n < 0) { + if (errno == EINTR) { + continue; + } + + LOGE("Error sending rtsp response."); + return false; + } + + numBytesSent += (size_t)n; + } + + return true; } // static diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h index 19be2a6c7791..0fecf3c6ebd5 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.h +++ b/media/libstagefright/rtsp/ARTSPConnection.h @@ -109,6 +109,8 @@ private: status_t findPendingRequest( const sp<ARTSPResponse> &response, ssize_t *index) const; + bool handleServerRequest(const sp<ARTSPResponse> &request); + static bool ParseSingleUnsignedLong( const char *from, unsigned long *x); diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp index a7563ff93b3d..1328d2ec595c 100644 --- a/media/libstagefright/rtsp/ARTSPController.cpp +++ b/media/libstagefright/rtsp/ARTSPController.cpp @@ -69,7 +69,14 @@ status_t ARTSPController::connect(const char *url) { void ARTSPController::disconnect() { Mutex::Autolock autoLock(mLock); - if (mState != CONNECTED) { + if (mState == CONNECTING) { + mState = DISCONNECTED; + mConnectionResult = ERROR_IO; + mCondition.broadcast(); + + mHandler.clear(); + return; + } else if (mState != CONNECTED) { return; } diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp index 3e710dc4a7b3..f03f7a268fb2 100644 --- a/media/libstagefright/rtsp/ASessionDescription.cpp +++ b/media/libstagefright/rtsp/ASessionDescription.cpp @@ -71,6 +71,11 @@ bool ASessionDescription::parse(const void *data, size_t size) { line.setTo(desc, i, eolPos - i); } + if (line.empty()) { + i = eolPos + 1; + continue; + } + if (line.size() < 2 || line.c_str()[1] != '=') { return false; } diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk index 0bbadc10ab72..fb42de8a01b4 100644 --- a/media/libstagefright/rtsp/Android.mk +++ b/media/libstagefright/rtsp/Android.mk @@ -11,13 +11,11 @@ LOCAL_SRC_FILES:= \ APacketSource.cpp \ ARTPAssembler.cpp \ ARTPConnection.cpp \ - ARTPSession.cpp \ ARTPSource.cpp \ ARTPWriter.cpp \ ARTSPConnection.cpp \ ARTSPController.cpp \ ASessionDescription.cpp \ - UDPPusher.cpp \ LOCAL_C_INCLUDES:= \ $(JNI_H_INCLUDE) \ @@ -57,4 +55,4 @@ LOCAL_MODULE_TAGS := debug LOCAL_MODULE:= rtp_test -include $(BUILD_EXECUTABLE) +# include $(BUILD_EXECUTABLE) diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 306a9c1c260b..d15d9c5c0966 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -38,10 +38,11 @@ #include <arpa/inet.h> #include <sys/socket.h> +#include <netdb.h> -// If no access units are received within 3 secs, assume that the rtp +// If no access units are received within 5 secs, assume that the rtp // stream has ended and signal end of stream. -static int64_t kAccessUnitTimeoutUs = 3000000ll; +static int64_t kAccessUnitTimeoutUs = 5000000ll; // If no access units arrive for the first 10 secs after starting the // stream, assume none ever will and signal EOS or switch transports. @@ -101,7 +102,9 @@ struct MyHandler : public AHandler { mSetupTracksSuccessful(false), mSeekPending(false), mFirstAccessUnit(true), - mFirstAccessUnitNTP(0), + mNTPAnchorUs(-1), + mMediaAnchorUs(-1), + mLastMediaTimeUs(0), mNumAccessUnitsReceived(0), mCheckPending(false), mCheckGeneration(0), @@ -119,9 +122,10 @@ struct MyHandler : public AHandler { // want to transmit user/pass in cleartext. AString host, path, user, pass; unsigned port; - if (ARTSPConnection::ParseURL( - mSessionURL.c_str(), &host, &port, &path, &user, &pass) - && user.size() > 0) { + CHECK(ARTSPConnection::ParseURL( + mSessionURL.c_str(), &host, &port, &path, &user, &pass)); + + if (user.size() > 0) { mSessionURL.clear(); mSessionURL.append("rtsp://"); mSessionURL.append(host); @@ -131,6 +135,8 @@ struct MyHandler : public AHandler { LOGI("rewritten session url: '%s'", mSessionURL.c_str()); } + + mSessionHost = host; } void connect(const sp<AMessage> &doneMsg) { @@ -246,34 +252,64 @@ struct MyHandler : public AHandler { // In case we're behind NAT, fire off two UDP packets to the remote // rtp/rtcp ports to poke a hole into the firewall for future incoming // packets. We're going to send an RR/SDES RTCP packet to both of them. - void pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) { + bool pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) { + struct sockaddr_in addr; + memset(addr.sin_zero, 0, sizeof(addr.sin_zero)); + addr.sin_family = AF_INET; + AString source; AString server_port; if (!GetAttribute(transport.c_str(), "source", - &source) - || !GetAttribute(transport.c_str(), + &source)) { + LOGW("Missing 'source' field in Transport response. Using " + "RTSP endpoint address."); + + struct hostent *ent = gethostbyname(mSessionHost.c_str()); + if (ent == NULL) { + LOGE("Failed to look up address of session host '%s'", + mSessionHost.c_str()); + + return false; + } + + addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; + } else { + addr.sin_addr.s_addr = inet_addr(source.c_str()); + } + + if (!GetAttribute(transport.c_str(), "server_port", &server_port)) { - return; + LOGI("Missing 'server_port' field in Transport response."); + return false; } int rtpPort, rtcpPort; if (sscanf(server_port.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2 || rtpPort <= 0 || rtpPort > 65535 || rtcpPort <=0 || rtcpPort > 65535 - || rtcpPort != rtpPort + 1 - || (rtpPort & 1) != 0) { - return; + || rtcpPort != rtpPort + 1) { + LOGE("Server picked invalid RTP/RTCP port pair %s," + " RTP port must be even, RTCP port must be one higher.", + server_port.c_str()); + + return false; } - struct sockaddr_in addr; - memset(addr.sin_zero, 0, sizeof(addr.sin_zero)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr(source.c_str()); + if (rtpPort & 1) { + LOGW("Server picked an odd RTP port, it should've picked an " + "even one, we'll let it pass for now, but this may break " + "in the future."); + } if (addr.sin_addr.s_addr == INADDR_NONE) { - return; + return true; + } + + if (IN_LOOPBACK(ntohl(addr.sin_addr.s_addr))) { + // No firewalls to traverse on the loopback interface. + return true; } // Make up an RR/SDES RTCP packet. @@ -287,16 +323,26 @@ struct MyHandler : public AHandler { ssize_t n = sendto( rtpSocket, buf->data(), buf->size(), 0, (const sockaddr *)&addr, sizeof(addr)); - CHECK_EQ(n, (ssize_t)buf->size()); + + if (n < (ssize_t)buf->size()) { + LOGE("failed to poke a hole for RTP packets"); + return false; + } addr.sin_port = htons(rtcpPort); n = sendto( rtcpSocket, buf->data(), buf->size(), 0, (const sockaddr *)&addr, sizeof(addr)); - CHECK_EQ(n, (ssize_t)buf->size()); + + if (n < (ssize_t)buf->size()) { + LOGE("failed to poke a hole for RTCP packets"); + return false; + } LOGV("successfully poked holes."); + + return true; } virtual void onMessageReceived(const sp<AMessage> &msg) { @@ -379,6 +425,7 @@ struct MyHandler : public AHandler { response->mContent->size()); if (!mSessionDesc->isValid()) { + LOGE("Failed to parse session description."); result = ERROR_MALFORMED; } else { ssize_t i = response->mHeaders.indexOfKey("content-base"); @@ -393,6 +440,25 @@ struct MyHandler : public AHandler { } } + if (!mBaseURL.startsWith("rtsp://")) { + // Some misbehaving servers specify a relative + // URL in one of the locations above, combine + // it with the absolute session URL to get + // something usable... + + LOGW("Server specified a non-absolute base URL" + ", combining it with the session URL to " + "get something usable..."); + + AString tmp; + CHECK(MakeURL( + mSessionURL.c_str(), + mBaseURL.c_str(), + &tmp)); + + mBaseURL = tmp; + } + CHECK_GT(mSessionDesc->countTracks(), 1u); setupTrack(1); } @@ -453,9 +519,12 @@ struct MyHandler : public AHandler { if (!track->mUsingInterleavedTCP) { AString transport = response->mHeaders.valueAt(i); - pokeAHole(track->mRTPSocket, - track->mRTCPSocket, - transport); + // We are going to continue even if we were + // unable to poke a hole into the firewall... + pokeAHole( + track->mRTPSocket, + track->mRTCPSocket, + transport); } mRTPConn->addStream( @@ -551,7 +620,8 @@ struct MyHandler : public AHandler { mSetupTracksSuccessful = false; mSeekPending = false; mFirstAccessUnit = true; - mFirstAccessUnitNTP = 0; + mNTPAnchorUs = -1; + mMediaAnchorUs = -1; mNumAccessUnitsReceived = 0; mReceivedFirstRTCPPacket = false; mReceivedFirstRTPPacket = false; @@ -632,6 +702,20 @@ struct MyHandler : public AHandler { case 'accu': { + int32_t timeUpdate; + if (msg->findInt32("time-update", &timeUpdate) && timeUpdate) { + size_t trackIndex; + CHECK(msg->findSize("track-index", &trackIndex)); + + uint32_t rtpTime; + uint64_t ntpTime; + CHECK(msg->findInt32("rtp-time", (int32_t *)&rtpTime)); + CHECK(msg->findInt64("ntp-time", (int64_t *)&ntpTime)); + + onTimeUpdate(trackIndex, rtpTime, ntpTime); + break; + } + int32_t first; if (msg->findInt32("first-rtcp", &first)) { mReceivedFirstRTCPPacket = true; @@ -683,51 +767,11 @@ struct MyHandler : public AHandler { break; } - uint64_t ntpTime; - CHECK(accessUnit->meta()->findInt64( - "ntp-time", (int64_t *)&ntpTime)); - - uint32_t rtpTime; - CHECK(accessUnit->meta()->findInt32( - "rtp-time", (int32_t *)&rtpTime)); - if (track->mNewSegment) { track->mNewSegment = false; - - LOGV("first segment unit ntpTime=0x%016llx rtpTime=%u seq=%d", - ntpTime, rtpTime, seqNum); - } - - if (mFirstAccessUnit) { - mDoneMsg->setInt32("result", OK); - mDoneMsg->post(); - mDoneMsg = NULL; - - mFirstAccessUnit = false; - mFirstAccessUnitNTP = ntpTime; } - if (ntpTime >= mFirstAccessUnitNTP) { - ntpTime -= mFirstAccessUnitNTP; - } else { - ntpTime = 0; - } - - int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32)); - - accessUnit->meta()->setInt64("timeUs", timeUs); - -#if 0 - int32_t damaged; - if (accessUnit->meta()->findInt32("damaged", &damaged) - && damaged != 0) { - LOGI("ignoring damaged AU"); - } else -#endif - { - TrackInfo *track = &mTracks.editItemAt(trackIndex); - track->mPacketSource->queueAccessUnit(accessUnit); - } + onAccessUnitComplete(trackIndex, accessUnit); break; } @@ -778,9 +822,15 @@ struct MyHandler : public AHandler { { // Session is paused now. for (size_t i = 0; i < mTracks.size(); ++i) { - mTracks.editItemAt(i).mPacketSource->flushQueue(); + TrackInfo *info = &mTracks.editItemAt(i); + + info->mPacketSource->flushQueue(); + info->mRTPAnchor = 0; + info->mNTPAnchorUs = -1; } + mNTPAnchorUs = -1; + int64_t timeUs; CHECK(msg->findInt64("time", &timeUs)); @@ -831,6 +881,11 @@ struct MyHandler : public AHandler { } else { parsePlayResponse(response); + ssize_t i = response->mHeaders.indexOfKey("rtp-info"); + CHECK_GE(i, 0); + + LOGV("rtp-info: %s", response->mHeaders.valueAt(i).c_str()); + LOGI("seek completed."); } } @@ -865,18 +920,16 @@ struct MyHandler : public AHandler { case 'tiou': { if (!mReceivedFirstRTCPPacket) { - if (mTryFakeRTCP) { - LOGW("Never received any data, disconnecting."); - (new AMessage('abor', id()))->post(); - } else if (mTryTCPInterleaving && mReceivedFirstRTPPacket) { + if (mReceivedFirstRTPPacket && !mTryFakeRTCP) { LOGW("We received RTP packets but no RTCP packets, " "using fake timestamps."); mTryFakeRTCP = true; mReceivedFirstRTCPPacket = true; - mRTPConn->fakeTimestamps(); - } else { + + fakeTimestamps(); + } else if (!mReceivedFirstRTPPacket && !mTryTCPInterleaving) { LOGW("Never received any data, switching transports."); mTryTCPInterleaving = true; @@ -884,6 +937,9 @@ struct MyHandler : public AHandler { sp<AMessage> msg = new AMessage('abor', id()); msg->setInt32("reconnect", true); msg->post(); + } else { + LOGW("Never received any data, disconnecting."); + (new AMessage('abor', id()))->post(); } } break; @@ -980,7 +1036,7 @@ struct MyHandler : public AHandler { uint32_t rtpTime = strtoul(val.c_str(), &end, 10); - LOGV("track #%d: rtpTime=%u <=> ntp=%.2f", n, rtpTime, npt1); + LOGV("track #%d: rtpTime=%u <=> npt=%.2f", n, rtpTime, npt1); info->mPacketSource->setNormalPlayTimeMapping( rtpTime, (int64_t)(npt1 * 1E6)); @@ -1003,6 +1059,25 @@ struct MyHandler : public AHandler { } private: + struct TrackInfo { + AString mURL; + int mRTPSocket; + int mRTCPSocket; + bool mUsingInterleavedTCP; + uint32_t mFirstSeqNumInSegment; + bool mNewSegment; + + uint32_t mRTPAnchor; + int64_t mNTPAnchorUs; + int32_t mTimeScale; + + sp<APacketSource> mPacketSource; + + // Stores packets temporarily while no notion of time + // has been established yet. + List<sp<ABuffer> > mPackets; + }; + sp<ALooper> mLooper; sp<ALooper> mNetLooper; sp<ARTSPConnection> mConn; @@ -1010,12 +1085,17 @@ private: sp<ASessionDescription> mSessionDesc; AString mOriginalSessionURL; // This one still has user:pass@ AString mSessionURL; + AString mSessionHost; AString mBaseURL; AString mSessionID; bool mSetupTracksSuccessful; bool mSeekPending; bool mFirstAccessUnit; - uint64_t mFirstAccessUnitNTP; + + int64_t mNTPAnchorUs; + int64_t mMediaAnchorUs; + int64_t mLastMediaTimeUs; + int64_t mNumAccessUnitsReceived; bool mCheckPending; int32_t mCheckGeneration; @@ -1025,16 +1105,6 @@ private: bool mReceivedFirstRTPPacket; bool mSeekable; - struct TrackInfo { - AString mURL; - int mRTPSocket; - int mRTCPSocket; - bool mUsingInterleavedTCP; - uint32_t mFirstSeqNumInSegment; - bool mNewSegment; - - sp<APacketSource> mPacketSource; - }; Vector<TrackInfo> mTracks; sp<AMessage> mDoneMsg; @@ -1066,6 +1136,20 @@ private: info->mUsingInterleavedTCP = false; info->mFirstSeqNumInSegment = 0; info->mNewSegment = true; + info->mRTPAnchor = 0; + info->mNTPAnchorUs = -1; + + unsigned long PT; + AString formatDesc; + AString formatParams; + mSessionDesc->getFormatType(index, &PT, &formatDesc, &formatParams); + + int32_t timescale; + int32_t numChannels; + ASessionDescription::ParseFormatDesc( + formatDesc.c_str(), ×cale, &numChannels); + + info->mTimeScale = timescale; LOGV("track #%d URL=%s", mTracks.size(), trackURL.c_str()); @@ -1144,6 +1228,96 @@ private: return true; } + void fakeTimestamps() { + for (size_t i = 0; i < mTracks.size(); ++i) { + onTimeUpdate(i, 0, 0ll); + } + } + + void onTimeUpdate(int32_t trackIndex, uint32_t rtpTime, uint64_t ntpTime) { + LOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = 0x%016llx", + trackIndex, rtpTime, ntpTime); + + int64_t ntpTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32)); + + TrackInfo *track = &mTracks.editItemAt(trackIndex); + + track->mRTPAnchor = rtpTime; + track->mNTPAnchorUs = ntpTimeUs; + + if (mNTPAnchorUs < 0) { + mNTPAnchorUs = ntpTimeUs; + mMediaAnchorUs = mLastMediaTimeUs; + } + } + + void onAccessUnitComplete( + int32_t trackIndex, const sp<ABuffer> &accessUnit) { + LOGV("onAccessUnitComplete track %d", trackIndex); + + if (mFirstAccessUnit) { + mDoneMsg->setInt32("result", OK); + mDoneMsg->post(); + mDoneMsg = NULL; + + mFirstAccessUnit = false; + } + + TrackInfo *track = &mTracks.editItemAt(trackIndex); + + if (mNTPAnchorUs < 0 || mMediaAnchorUs < 0 || track->mNTPAnchorUs < 0) { + LOGV("storing accessUnit, no time established yet"); + track->mPackets.push_back(accessUnit); + return; + } + + while (!track->mPackets.empty()) { + sp<ABuffer> accessUnit = *track->mPackets.begin(); + track->mPackets.erase(track->mPackets.begin()); + + if (addMediaTimestamp(trackIndex, track, accessUnit)) { + track->mPacketSource->queueAccessUnit(accessUnit); + } + } + + if (addMediaTimestamp(trackIndex, track, accessUnit)) { + track->mPacketSource->queueAccessUnit(accessUnit); + } + } + + bool addMediaTimestamp( + int32_t trackIndex, const TrackInfo *track, + const sp<ABuffer> &accessUnit) { + uint32_t rtpTime; + CHECK(accessUnit->meta()->findInt32( + "rtp-time", (int32_t *)&rtpTime)); + + int64_t relRtpTimeUs = + (((int64_t)rtpTime - (int64_t)track->mRTPAnchor) * 1000000ll) + / track->mTimeScale; + + int64_t ntpTimeUs = track->mNTPAnchorUs + relRtpTimeUs; + + int64_t mediaTimeUs = mMediaAnchorUs + ntpTimeUs - mNTPAnchorUs; + + if (mediaTimeUs > mLastMediaTimeUs) { + mLastMediaTimeUs = mediaTimeUs; + } + + if (mediaTimeUs < 0) { + LOGV("dropping early accessUnit."); + return false; + } + + LOGV("track %d rtpTime=%d mediaTimeUs = %lld us (%.2f secs)", + trackIndex, rtpTime, mediaTimeUs, mediaTimeUs / 1E6); + + accessUnit->meta()->setInt64("timeUs", mediaTimeUs); + + return true; + } + + DISALLOW_EVIL_CONSTRUCTORS(MyHandler); }; diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk index 70dc3402e29d..c25285eb2928 100644 --- a/media/mtp/Android.mk +++ b/media/mtp/Android.mk @@ -21,7 +21,6 @@ ifneq ($(TARGET_SIMULATOR),true) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - MtpClient.cpp \ MtpDataPacket.cpp \ MtpDebug.cpp \ MtpDevice.cpp \ @@ -53,7 +52,6 @@ ifeq ($(HOST_OS),linux) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - MtpClient.cpp \ MtpDataPacket.cpp \ MtpDebug.cpp \ MtpDevice.cpp \ diff --git a/media/mtp/MtpClient.cpp b/media/mtp/MtpClient.cpp deleted file mode 100644 index c83054001a0c..000000000000 --- a/media/mtp/MtpClient.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 "MtpClient" - -#include "MtpDebug.h" -#include "MtpClient.h" -#include "MtpDevice.h" - -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <errno.h> - -#include <usbhost/usbhost.h> - -struct usb_device; - -namespace android { - -static bool isMtpDevice(uint16_t vendor, uint16_t product) { - // Sandisk Sansa Fuze - if (vendor == 0x0781 && product == 0x74c2) - return true; - // Samsung YP-Z5 - if (vendor == 0x04e8 && product == 0x503c) - return true; - return false; -} - -class MtpClientThread : public Thread { -private: - MtpClient* mClient; - -public: - MtpClientThread(MtpClient* client) - : mClient(client) - { - } - - virtual bool threadLoop() { - return mClient->threadLoop(); - } -}; - - -MtpClient::MtpClient() - : mThread(NULL), - mUsbHostContext(NULL), - mDone(false) -{ -} - -MtpClient::~MtpClient() { - usb_host_cleanup(mUsbHostContext); -} - -bool MtpClient::start() { - Mutex::Autolock autoLock(mMutex); - - if (mThread) - return true; - - mUsbHostContext = usb_host_init(); - if (!mUsbHostContext) - return false; - - mThread = new MtpClientThread(this); - mThread->run("MtpClientThread"); - // wait for the thread to do initial device discovery before returning - mThreadStartCondition.wait(mMutex); - - return true; -} - -void MtpClient::stop() { - mDone = true; -} - -MtpDevice* MtpClient::getDevice(int id) { - for (int i = 0; i < mDeviceList.size(); i++) { - MtpDevice* device = mDeviceList[i]; - if (device->getID() == id) - return device; - } - return NULL; -} - -bool MtpClient::usbDeviceAdded(const char *devname) { - struct usb_descriptor_header* desc; - struct usb_descriptor_iter iter; - - struct usb_device *device = usb_device_open(devname); - if (!device) { - LOGE("usb_device_open failed\n"); - return mDone; - } - - usb_descriptor_iter_init(device, &iter); - - while ((desc = usb_descriptor_iter_next(&iter)) != NULL) { - if (desc->bDescriptorType == USB_DT_INTERFACE) { - struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc; - - if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE && - interface->bInterfaceSubClass == 1 && // Still Image Capture - interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470) - { - LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device), - usb_device_get_product_name(device)); - } else if (interface->bInterfaceClass == 0xFF && - interface->bInterfaceSubClass == 0xFF && - interface->bInterfaceProtocol == 0) { - char* interfaceName = usb_device_get_string(device, interface->iInterface); - if (!interfaceName || strcmp(interfaceName, "MTP")) - continue; - // Looks like an android style MTP device - LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device), - usb_device_get_product_name(device)); - } else { - // look for special cased devices based on vendor/product ID - // we are doing this mainly for testing purposes - uint16_t vendor = usb_device_get_vendor_id(device); - uint16_t product = usb_device_get_product_id(device); - if (!isMtpDevice(vendor, product)) { - // not an MTP or PTP device - continue; - } - // request MTP OS string and descriptor - // some music players need to see this before entering MTP mode. - char buffer[256]; - memset(buffer, 0, sizeof(buffer)); - int ret = usb_device_send_control(device, - USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD, - USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE, - 0, sizeof(buffer), buffer); - printf("usb_device_send_control returned %d errno: %d\n", ret, errno); - if (ret > 0) { - printf("got MTP string %s\n", buffer); - ret = usb_device_send_control(device, - USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1, - 0, 4, sizeof(buffer), buffer); - printf("OS descriptor got %d\n", ret); - } else { - printf("no MTP string\n"); - } - } - - // if we got here, then we have a likely MTP or PTP device - - // interface should be followed by three endpoints - struct usb_endpoint_descriptor *ep; - struct usb_endpoint_descriptor *ep_in_desc = NULL; - struct usb_endpoint_descriptor *ep_out_desc = NULL; - struct usb_endpoint_descriptor *ep_intr_desc = NULL; - for (int i = 0; i < 3; i++) { - ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter); - if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) { - LOGE("endpoints not found\n"); - return mDone; - } - if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { - if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) - ep_in_desc = ep; - else - ep_out_desc = ep; - } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT && - ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { - ep_intr_desc = ep; - } - } - if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) { - LOGE("endpoints not found\n"); - return mDone; - } - - if (usb_device_claim_interface(device, interface->bInterfaceNumber)) { - LOGE("usb_device_claim_interface failed errno: %d\n", errno); - return mDone; - } - - MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber, - ep_in_desc, ep_out_desc, ep_intr_desc); - mDeviceList.add(mtpDevice); - mtpDevice->initialize(); - deviceAdded(mtpDevice); - return mDone; - } - } - - usb_device_close(device); - return mDone; -} - -bool MtpClient::usbDeviceRemoved(const char *devname) { - for (int i = 0; i < mDeviceList.size(); i++) { - MtpDevice* device = mDeviceList[i]; - if (!strcmp(devname, device->getDeviceName())) { - deviceRemoved(device); - mDeviceList.removeAt(i); - delete device; - LOGD("Camera removed!\n"); - break; - } - } - return mDone; -} - -bool MtpClient::usbDiscoveryDone() { - Mutex::Autolock autoLock(mMutex); - mThreadStartCondition.signal(); - return mDone; -} - -bool MtpClient::threadLoop() { - usb_host_run(mUsbHostContext, usb_device_added, usb_device_removed, usb_discovery_done, this); - return false; -} - -int MtpClient::usb_device_added(const char *devname, void* client_data) { - LOGD("usb_device_added %s\n", devname); - return ((MtpClient *)client_data)->usbDeviceAdded(devname); -} - -int MtpClient::usb_device_removed(const char *devname, void* client_data) { - LOGD("usb_device_removed %s\n", devname); - return ((MtpClient *)client_data)->usbDeviceRemoved(devname); -} - -int MtpClient::usb_discovery_done(void* client_data) { - LOGD("usb_discovery_done\n"); - return ((MtpClient *)client_data)->usbDiscoveryDone(); -} - -} // namespace android diff --git a/media/mtp/MtpClient.h b/media/mtp/MtpClient.h deleted file mode 100644 index fa5c5278714b..000000000000 --- a/media/mtp/MtpClient.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 _MTP_CLIENT_H -#define _MTP_CLIENT_H - -#include "MtpTypes.h" - -#include <utils/threads.h> - -struct usb_host_context; - -namespace android { - -class MtpClientThread; - -class MtpClient { -private: - MtpDeviceList mDeviceList; - MtpClientThread* mThread; - Condition mThreadStartCondition; - Mutex mMutex; - struct usb_host_context* mUsbHostContext; - bool mDone; - -public: - MtpClient(); - virtual ~MtpClient(); - - bool start(); - void stop(); - - inline MtpDeviceList& getDeviceList() { return mDeviceList; } - MtpDevice* getDevice(int id); - - - virtual void deviceAdded(MtpDevice *device) = 0; - virtual void deviceRemoved(MtpDevice *device) = 0; - -private: - // these return true if we should stop monitoring USB and clean up - bool usbDeviceAdded(const char *devname); - bool usbDeviceRemoved(const char *devname); - bool usbDiscoveryDone(); - - friend class MtpClientThread; - bool threadLoop(); - static int usb_device_added(const char *devname, void* client_data); - static int usb_device_removed(const char *devname, void* client_data); - static int usb_discovery_done(void* client_data); -}; - -}; // namespace android - -#endif // _MTP_CLIENT_H diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp index d22c72fc4c6a..4ea884970720 100644 --- a/media/mtp/MtpDevice.cpp +++ b/media/mtp/MtpDevice.cpp @@ -38,6 +38,140 @@ namespace android { +#if 0 +static bool isMtpDevice(uint16_t vendor, uint16_t product) { + // Sandisk Sansa Fuze + if (vendor == 0x0781 && product == 0x74c2) + return true; + // Samsung YP-Z5 + if (vendor == 0x04e8 && product == 0x503c) + return true; + return false; +} +#endif + +MtpDevice* MtpDevice::open(const char* deviceName, int fd) { + struct usb_device *device = usb_device_new(deviceName, fd); + if (!device) { + LOGE("usb_device_new failed for %s", deviceName); + return NULL; + } + + struct usb_descriptor_header* desc; + struct usb_descriptor_iter iter; + + usb_descriptor_iter_init(device, &iter); + + while ((desc = usb_descriptor_iter_next(&iter)) != NULL) { + if (desc->bDescriptorType == USB_DT_INTERFACE) { + struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc; + + if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE && + interface->bInterfaceSubClass == 1 && // Still Image Capture + interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470) + { + char* manufacturerName = usb_device_get_manufacturer_name(device); + char* productName = usb_device_get_product_name(device); + LOGD("Found camera: \"%s\" \"%s\"\n", manufacturerName, productName); + free(manufacturerName); + free(productName); + } else if (interface->bInterfaceClass == 0xFF && + interface->bInterfaceSubClass == 0xFF && + interface->bInterfaceProtocol == 0) { + char* interfaceName = usb_device_get_string(device, interface->iInterface); + if (!interfaceName) { + continue; + } else if (strcmp(interfaceName, "MTP")) { + free(interfaceName); + continue; + } + free(interfaceName); + + // Looks like an android style MTP device + char* manufacturerName = usb_device_get_manufacturer_name(device); + char* productName = usb_device_get_product_name(device); + LOGD("Found MTP device: \"%s\" \"%s\"\n", manufacturerName, productName); + free(manufacturerName); + free(productName); + } +#if 0 + else { + // look for special cased devices based on vendor/product ID + // we are doing this mainly for testing purposes + uint16_t vendor = usb_device_get_vendor_id(device); + uint16_t product = usb_device_get_product_id(device); + if (!isMtpDevice(vendor, product)) { + // not an MTP or PTP device + continue; + } + // request MTP OS string and descriptor + // some music players need to see this before entering MTP mode. + char buffer[256]; + memset(buffer, 0, sizeof(buffer)); + int ret = usb_device_control_transfer(device, + USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD, + USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE, + 0, buffer, sizeof(buffer), 0); + printf("usb_device_control_transfer returned %d errno: %d\n", ret, errno); + if (ret > 0) { + printf("got MTP string %s\n", buffer); + ret = usb_device_control_transfer(device, + USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1, + 0, 4, buffer, sizeof(buffer), 0); + printf("OS descriptor got %d\n", ret); + } else { + printf("no MTP string\n"); + } + } +#endif + // if we got here, then we have a likely MTP or PTP device + + // interface should be followed by three endpoints + struct usb_endpoint_descriptor *ep; + struct usb_endpoint_descriptor *ep_in_desc = NULL; + struct usb_endpoint_descriptor *ep_out_desc = NULL; + struct usb_endpoint_descriptor *ep_intr_desc = NULL; + for (int i = 0; i < 3; i++) { + ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter); + if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) { + LOGE("endpoints not found\n"); + usb_device_close(device); + return NULL; + } + if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { + if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + ep_in_desc = ep; + else + ep_out_desc = ep; + } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT && + ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { + ep_intr_desc = ep; + } + } + if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) { + LOGE("endpoints not found\n"); + usb_device_close(device); + return NULL; + } + + if (usb_device_claim_interface(device, interface->bInterfaceNumber)) { + LOGE("usb_device_claim_interface failed errno: %d\n", errno); + usb_device_close(device); + return NULL; + } + + MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber, + ep_in_desc, ep_out_desc, ep_intr_desc); + mtpDevice->initialize(); + return mtpDevice; + } + } + + usb_device_close(device); + LOGE("device not found"); + return NULL; +} + MtpDevice::MtpDevice(struct usb_device* device, int interface, const struct usb_endpoint_descriptor *ep_in, const struct usb_endpoint_descriptor *ep_out, @@ -49,7 +183,6 @@ MtpDevice::MtpDevice(struct usb_device* device, int interface, mRequestOut(NULL), mRequestIntr(NULL), mDeviceInfo(NULL), - mID(usb_device_get_unique_id(device)), mSessionID(0), mTransactionID(0), mReceivedResponse(false) @@ -106,6 +239,7 @@ void MtpDevice::print() { MtpProperty* property = getDevicePropDesc(propCode); if (property) { property->print(); + delete property; } } } @@ -122,11 +256,13 @@ void MtpDevice::print() { for (int j = 0; j < props->size(); j++) { MtpObjectProperty prop = (*props)[j]; MtpProperty* property = getObjectPropDesc(prop, format); - if (property) + if (property) { property->print(); - else + delete property; + } else { LOGE("could not fetch property: %s", MtpDebug::getObjectPropCodeName(prop)); + } } } } @@ -362,18 +498,24 @@ bool MtpDevice::deleteObject(MtpObjectHandle handle) { MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) { MtpObjectInfo* info = getObjectInfo(handle); - if (info) - return info->mParent; - else + if (info) { + MtpObjectHandle parent = info->mParent; + delete info; + return parent; + } else { return -1; + } } MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) { MtpObjectInfo* info = getObjectInfo(handle); - if (info) - return info->mStorageID; - else + if (info) { + MtpObjectHandle storageId = info->mStorageID; + delete info; + return storageId; + } else { return -1; + } } MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) { @@ -430,6 +572,98 @@ MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectForma return NULL; } +bool MtpDevice::readObject(MtpObjectHandle handle, + bool (* callback)(void* data, int offset, int length, void* clientData), + int objectSize, void* clientData) { + Mutex::Autolock autoLock(mMutex); + bool result = false; + + mRequest.reset(); + mRequest.setParameter(1, handle); + if (sendRequest(MTP_OPERATION_GET_OBJECT) + && mData.readDataHeader(mRequestIn1)) { + uint32_t length = mData.getContainerLength(); + if (length - MTP_CONTAINER_HEADER_SIZE != objectSize) { + LOGE("readObject error objectSize: %d, length: %d", + objectSize, length); + goto fail; + } + length -= MTP_CONTAINER_HEADER_SIZE; + uint32_t remaining = length; + int offset = 0; + + int initialDataLength = 0; + void* initialData = mData.getData(initialDataLength); + if (initialData) { + if (initialDataLength > 0) { + if (!callback(initialData, 0, initialDataLength, clientData)) + goto fail; + remaining -= initialDataLength; + offset += initialDataLength; + } + free(initialData); + } + + // USB reads greater than 16K don't work + char buffer1[16384], buffer2[16384]; + mRequestIn1->buffer = buffer1; + mRequestIn2->buffer = buffer2; + struct usb_request* req = mRequestIn1; + void* writeBuffer = NULL; + int writeLength = 0; + + while (remaining > 0 || writeBuffer) { + if (remaining > 0) { + // queue up a read request + req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining); + if (mData.readDataAsync(req)) { + LOGE("readDataAsync failed"); + goto fail; + } + } else { + req = NULL; + } + + if (writeBuffer) { + // write previous buffer + if (!callback(writeBuffer, offset, writeLength, clientData)) { + LOGE("write failed"); + // wait for pending read before failing + if (req) + mData.readDataWait(mDevice); + goto fail; + } + offset += writeLength; + writeBuffer = NULL; + } + + // wait for read to complete + if (req) { + int read = mData.readDataWait(mDevice); + if (read < 0) + goto fail; + + if (read > 0) { + writeBuffer = req->buffer; + writeLength = read; + remaining -= read; + req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1); + } else { + writeBuffer = NULL; + } + } + } + + MtpResponseCode response = readResponse(); + if (response == MTP_RESPONSE_OK) + result = true; + } + +fail: + return result; +} + + // reads the object's data and writes it to the specified file path bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) { LOGD("readObject: %s", destPath); @@ -462,8 +696,10 @@ bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int gro void* initialData = mData.getData(initialDataLength); if (initialData) { if (initialDataLength > 0) { - if (write(fd, initialData, initialDataLength) != initialDataLength) + if (write(fd, initialData, initialDataLength) != initialDataLength) { + free(initialData); goto fail; + } remaining -= initialDataLength; } free(initialData); @@ -507,10 +743,14 @@ bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int gro if (read < 0) goto fail; - writeBuffer = req->buffer; - writeLength = read; - remaining -= read; - req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1); + if (read > 0) { + writeBuffer = req->buffer; + writeLength = read; + remaining -= read; + req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1); + } else { + writeBuffer = NULL; + } } } diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h index d0a0fb3016ee..b69203ec0f5a 100644 --- a/media/mtp/MtpDevice.h +++ b/media/mtp/MtpDevice.h @@ -45,9 +45,6 @@ private: MtpDeviceInfo* mDeviceInfo; MtpPropertyList mDeviceProperties; - // a unique ID for the device - int mID; - // current session ID MtpSessionID mSessionID; // current transaction ID @@ -67,9 +64,10 @@ public: const struct usb_endpoint_descriptor *ep_in, const struct usb_endpoint_descriptor *ep_out, const struct usb_endpoint_descriptor *ep_intr); - virtual ~MtpDevice(); - inline int getID() const { return mID; } + static MtpDevice* open(const char* deviceName, int fd); + + virtual ~MtpDevice(); void initialize(); void close(); @@ -97,7 +95,11 @@ public: MtpProperty* getDevicePropDesc(MtpDeviceProperty code); MtpProperty* getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format); - bool readObject(MtpObjectHandle handle, const char* destPath, int group, + bool readObject(MtpObjectHandle handle, + bool (* callback)(void* data, int offset, + int length, void* clientData), + int objectSize, void* clientData); + bool readObject(MtpObjectHandle handle, const char* destPath, int group, int perm); private: diff --git a/media/mtp/MtpPacket.cpp b/media/mtp/MtpPacket.cpp index d3f2cb4aa31e..baf99e519e2b 100644 --- a/media/mtp/MtpPacket.cpp +++ b/media/mtp/MtpPacket.cpp @@ -153,12 +153,13 @@ void MtpPacket::setParameter(int index, uint32_t value) { #ifdef MTP_HOST int MtpPacket::transfer(struct usb_request* request) { - if (usb_request_queue(request)) { - LOGE("usb_endpoint_queue failed, errno: %d", errno); - return -1; - } - request = usb_request_wait(request->dev); - return (request ? request->actual_length : -1); + int result = usb_device_bulk_transfer(request->dev, + request->endpoint, + request->buffer, + request->buffer_length, + 0); + request->actual_length = result; + return result; } #endif diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index be004d2dd786..853a5af864f7 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -343,8 +343,9 @@ MtpResponseCode MtpServer::doGetDeviceInfo() { mData.putAUInt16(deviceProperties); // Device Properties Supported mData.putAUInt16(captureFormats); // Capture Formats mData.putAUInt16(playbackFormats); // Playback Formats - // FIXME - string.set("Google, Inc."); + + property_get("ro.product.manufacturer", prop_value, "unknown manufacturer"); + string.set(prop_value); mData.putString(string); // Manufacturer property_get("ro.product.model", prop_value, "MTP Device"); diff --git a/media/tests/CameraBrowser/Android.mk b/media/tests/CameraBrowser/Android.mk new file mode 100644 index 000000000000..1d8112911d3c --- /dev/null +++ b/media/tests/CameraBrowser/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := CameraBrowser + +include $(BUILD_PACKAGE) diff --git a/media/tests/CameraBrowser/AndroidManifest.xml b/media/tests/CameraBrowser/AndroidManifest.xml new file mode 100644 index 000000000000..f167f4bec542 --- /dev/null +++ b/media/tests/CameraBrowser/AndroidManifest.xml @@ -0,0 +1,31 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.camerabrowser"> + + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.ACCESS_USB" /> + + <application android:label="@string/app_label" + android:name="com.android.camerabrowser.CameraBrowserApplication"> + + <activity android:name="CameraBrowser" android:label="Camera Browser"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity android:name="StorageBrowser" /> + <activity android:name="ObjectBrowser" /> + <activity android:name="ObjectViewer" /> + +<!-- + <receiver android:name="UsbReceiver"> + <intent-filter> + <action android:name="android.hardware.action.USB_DEVICE_ATTACHED" /> + </intent-filter> + </receiver> +--> + </application> + + +</manifest> diff --git a/media/tests/CameraBrowser/res/layout/object_info.xml b/media/tests/CameraBrowser/res/layout/object_info.xml new file mode 100644 index 000000000000..a0499f267bfe --- /dev/null +++ b/media/tests/CameraBrowser/res/layout/object_info.xml @@ -0,0 +1,169 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<TableLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/object_info" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + <TableRow> + <TextView android:id="@+id/name_label" + android:text="@string/name_label" + android:layout_gravity="right" + android:layout_marginRight="8dip" + style="@style/info_label" /> + + <TextView android:id="@+id/name" + style="@style/info_value" /> + </TableRow> + <TableRow> + <TextView android:id="@+id/size_label" + android:text="@string/size_label" + android:layout_gravity="right" + android:layout_marginRight="8dip" + style="@style/info_label" /> + + <TextView android:id="@+id/size" + style="@style/info_value" /> + </TableRow> + <TableRow> + <TextView android:id="@+id/format_label" + android:text="@string/format_label" + android:layout_gravity="right" + android:layout_marginRight="8dip" + style="@style/info_label" /> + + <TextView android:id="@+id/format" + style="@style/info_value" /> + </TableRow> + <TableRow> + <TextView android:id="@+id/thumb_width_label" + android:text="@string/thumb_width_label" + android:layout_gravity="right" + android:layout_marginRight="8dip" + style="@style/info_label" /> + + <TextView android:id="@+id/thumb_width" + style="@style/info_value" /> + </TableRow> + <TableRow> + <TextView android:id="@+id/thumb_height_label" + android:text="@string/thumb_height_label" + android:layout_gravity="right" + android:layout_marginRight="8dip" + style="@style/info_label" /> + + <TextView android:id="@+id/thumb_height" + style="@style/info_value" /> + </TableRow> + <TableRow> + <TextView android:id="@+id/thumb_size_label" + android:text="@string/thumb_size_label" + android:layout_gravity="right" + android:layout_marginRight="8dip" + style="@style/info_label" /> + + <TextView android:id="@+id/thumb_size" + style="@style/info_value" /> + </TableRow> + <TableRow> + <TextView android:id="@+id/width_label" + android:text="@string/width_label" + android:layout_gravity="right" + android:layout_marginRight="8dip" + style="@style/info_label" /> + + <TextView android:id="@+id/width" + style="@style/info_value" /> + </TableRow> + <TableRow> + <TextView android:id="@+id/height_label" + android:text="@string/height_label" + android:layout_gravity="right" + android:layout_marginRight="8dip" + style="@style/info_label" /> + + <TextView android:id="@+id/height" + style="@style/info_value" /> + </TableRow> + <TableRow> + <TextView android:id="@+id/depth_label" + android:text="@string/depth_label" + android:layout_gravity="right" + android:layout_marginRight="8dip" + style="@style/info_label" /> + + <TextView android:id="@+id/depth" + style="@style/info_value" /> + </TableRow> + <TableRow> + <TextView android:id="@+id/sequence_label" + android:text="@string/sequence_label" + android:layout_gravity="right" + android:layout_marginRight="8dip" + style="@style/info_label" /> + + <TextView android:id="@+id/sequence" + style="@style/info_value" /> + </TableRow> + <TableRow> + <TextView android:id="@+id/created_label" + android:text="@string/created_label" + android:layout_gravity="right" + android:layout_marginRight="8dip" + style="@style/info_label" /> + + <TextView android:id="@+id/created" + style="@style/info_value" /> + </TableRow> + <TableRow> + <TextView android:id="@+id/modified_label" + android:text="@string/modified_label" + android:layout_gravity="right" + android:layout_marginRight="8dip" + style="@style/info_label" /> + + <TextView android:id="@+id/modified" + style="@style/info_value" /> + </TableRow> + <TableRow> + <TextView android:id="@+id/keywords_label" + android:text="@string/keywords_label" + android:layout_gravity="right" + android:layout_marginRight="8dip" + style="@style/info_label" /> + + <TextView android:id="@+id/keywords" + style="@style/info_value" /> + </TableRow> + <TableRow> + <ImageView android:id="@+id/thumbnail" /> + </TableRow> + <TableRow> + <Button android:id="@+id/import_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/import_label"> + </Button> + <Button android:id="@+id/delete_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/delete_label"> + </Button> + </TableRow> +</TableLayout> + diff --git a/media/tests/CameraBrowser/res/layout/object_list.xml b/media/tests/CameraBrowser/res/layout/object_list.xml new file mode 100644 index 000000000000..30c18bb8a077 --- /dev/null +++ b/media/tests/CameraBrowser/res/layout/object_list.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <ImageView android:id="@+id/thumbnail" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <TextView android:id="@+id/name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceLarge" + android:gravity="center_vertical" + android:paddingLeft="6dip" + android:minHeight="?android:attr/listPreferredItemHeight" /> +</LinearLayout> diff --git a/media/tests/CameraBrowser/res/values/strings.xml b/media/tests/CameraBrowser/res/values/strings.xml new file mode 100644 index 000000000000..932aaecc626c --- /dev/null +++ b/media/tests/CameraBrowser/res/values/strings.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <string name="app_label">Camera Browser</string> + + <!-- for object info --> + <string name="name_label">Name: </string> + <string name="size_label">Size: </string> + <string name="format_label">Format: </string> + <string name="thumb_width_label">Thumb Width: </string> + <string name="thumb_height_label">Thumb Height: </string> + <string name="thumb_size_label">Thumb Size: </string> + <string name="width_label">Width: </string> + <string name="height_label">Height: </string> + <string name="depth_label">Depth: </string> + <string name="sequence_label">Sequence: </string> + <string name="created_label">Created: </string> + <string name="modified_label">Modified: </string> + <string name="keywords_label">Keywords: </string> + + <!-- button labels --> + <string name="import_label">Import</string> + <string name="delete_label">Delete</string> + + <!-- toasts --> + <string name="object_saved_message">Object saved</string> + <string name="save_failed_message">Could not save object</string> + <string name="object_deleted_message">Object deleted</string> + <string name="delete_failed_message">Could not delete object</string> + <string name="start_activity_failed_message">Import succeeded, but could not display object</string> + +</resources> diff --git a/media/tests/CameraBrowser/res/values/styles.xml b/media/tests/CameraBrowser/res/values/styles.xml new file mode 100644 index 000000000000..c869985af1d3 --- /dev/null +++ b/media/tests/CameraBrowser/res/values/styles.xml @@ -0,0 +1,34 @@ +<?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. +--> + +<resources> + <style name="info_label"> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:textSize">14sp</item> + <item name="android:textStyle">bold</item> + <item name="android:paddingRight">4dip</item> + </style> + + <style name="info_value"> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:textSize">14sp</item> + <item name="android:textStyle">normal</item> + </style> + +</resources> + diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java new file mode 100644 index 000000000000..f642d9308536 --- /dev/null +++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camerabrowser; + +import android.app.ListActivity; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.mtp.MtpClient; +import android.mtp.MtpDevice; +import android.mtp.MtpDeviceInfo; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.TwoLineListItem; + +import java.util.List; + + /** + * A list view displaying all connected cameras. + */ +public class CameraBrowser extends ListActivity implements MtpClient.Listener { + + private static final String TAG = "CameraBrowser"; + + private MtpClient mClient; + private List<MtpDevice> mDeviceList; + + private static final int MODEL_COLUMN = 0; + private static final int MANUFACTURER_COLUMN = 1; + private static final int COLUMN_COUNT = 2; + + private class CameraAdapter extends BaseAdapter { + private final Context mContext; + private final LayoutInflater mInflater; + + public CameraAdapter(Context c) { + mContext = c; + mInflater = (LayoutInflater)c.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + public int getCount() { + return mDeviceList.size(); + } + + public Object getItem(int position) { + return mDeviceList.get(position); + } + + public long getItemId(int position) { + return position; + } + + public View getView(int position, View convertView, ViewGroup parent) { + TwoLineListItem view; + if (convertView == null) { + view = (TwoLineListItem)mInflater.inflate( + android.R.layout.simple_list_item_2, parent, false); + } else { + view = (TwoLineListItem)convertView; + } + + TextView textView1 = (TextView)view.findViewById(com.android.internal.R.id.text1); + TextView textView2 = (TextView)view.findViewById(com.android.internal.R.id.text2); + MtpDevice device = mDeviceList.get(position); + MtpDeviceInfo info = device.getDeviceInfo(); + if (info != null) { + textView1.setText(info.getManufacturer()); + textView2.setText(info.getModel()); + } else { + textView1.setText("???"); + textView2.setText("???"); + } + return view; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mClient = ((CameraBrowserApplication)getApplication()).getMtpClient(); + mClient.addListener(this); + mDeviceList = mClient.getDeviceList(); + } + + @Override + protected void onResume() { + super.onResume(); + reload(); + } + + @Override + protected void onDestroy() { + mClient.removeListener(this); + super.onDestroy(); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + Intent intent = new Intent(this, StorageBrowser.class); + intent.putExtra("device", mDeviceList.get(position).getDeviceName()); + startActivity(intent); + } + + private void reload() { + setListAdapter(new CameraAdapter(this)); + } + + public void deviceAdded(MtpDevice device) { + Log.d(TAG, "deviceAdded: " + device.getDeviceName()); + mDeviceList = mClient.getDeviceList(); + reload(); + } + + public void deviceRemoved(MtpDevice device) { + Log.d(TAG, "deviceRemoved: " + device.getDeviceName()); + mDeviceList = mClient.getDeviceList(); + reload(); + } +} diff --git a/drm/libdrmframework/include/DrmIOService.h b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowserApplication.java index 244124e79124..6f1edfea2a3e 100644 --- a/drm/libdrmframework/include/DrmIOService.h +++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowserApplication.java @@ -14,33 +14,28 @@ * limitations under the License. */ -#ifndef __DRM_IO_SERVICE_H__ -#define __DRM_IO_SERVICE_H__ +package com.android.camerabrowser; -#include "IDrmIOService.h" +import android.app.Application; +import android.mtp.MtpClient; -namespace android { -/** - * This is the implementation class for DRM IO service. - * - * The instance of this class is created while starting the DRM IO service. - * - */ -class DrmIOService : public BnDrmIOService { -public: - static void instantiate(); - -private: - DrmIOService(); - virtual ~DrmIOService(); +public class CameraBrowserApplication extends Application { -public: - void writeToFile(const String8& filePath, const String8& dataBuffer); - String8 readFromFile(const String8& filePath); -}; + private MtpClient mClient; -}; + @Override + public void onCreate() { + mClient = new MtpClient(this); + } -#endif /* __DRM_IO_SERVICE_H__ */ + @Override + public void onTerminate() { + mClient.close(); + mClient = null; + } + public MtpClient getMtpClient() { + return mClient; + } +} diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/DeviceDisconnectedReceiver.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/DeviceDisconnectedReceiver.java new file mode 100644 index 000000000000..736af1f506f2 --- /dev/null +++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/DeviceDisconnectedReceiver.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camerabrowser; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.UsbManager; +import android.util.Log; + +public class DeviceDisconnectedReceiver extends BroadcastReceiver { + + private static final String TAG = "DeviceDisconnectedReceiver"; + + private final Activity mActivity; + private final String mDeviceName; + + public DeviceDisconnectedReceiver(Activity activity, String deviceName) { + mActivity = activity; + mDeviceName = deviceName; + + IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED); + activity.registerReceiver(this, filter); + } + + @Override + public void onReceive(Context context, Intent intent) { + String deviceName = intent.getStringExtra(UsbManager.EXTRA_DEVICE_NAME); + Log.d(TAG, "ACTION_USB_DEVICE_DETACHED " + deviceName); + + // close our activity if the device it is displaying is disconnected + if (deviceName.equals(mDeviceName)) { + mActivity.finish(); + } + } +}
\ No newline at end of file diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java new file mode 100644 index 000000000000..82251d9213e5 --- /dev/null +++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camerabrowser; + +import android.app.ListActivity; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.mtp.MtpClient; +import android.mtp.MtpConstants; +import android.mtp.MtpDevice; +import android.mtp.MtpObjectInfo; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.List; + + /** + * A list view displaying all objects within a container (folder or storage unit). + */ +public class ObjectBrowser extends ListActivity { + + private static final String TAG = "ObjectBrowser"; + + private MtpClient mClient; + private List<MtpObjectInfo> mObjectList; + private String mDeviceName; + private int mStorageID; + private int mObjectID; + private DeviceDisconnectedReceiver mDisconnectedReceiver; + + private class ObjectAdapter extends BaseAdapter { + private final Context mContext; + private final LayoutInflater mInflater; + + public ObjectAdapter(Context c) { + mContext = c; + mInflater = (LayoutInflater)c.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + public int getCount() { + if (mObjectList == null) { + return 0; + } else { + return mObjectList.size(); + } + } + + public Object getItem(int position) { + return mObjectList.get(position); + } + + public long getItemId(int position) { + return position; + } + + public View getView(int position, View convertView, ViewGroup parent) { + View view; + if (convertView == null) { + view = mInflater.inflate(R.layout.object_list, parent, false); + } else { + view = convertView; + } + + TextView nameView = (TextView)view.findViewById(R.id.name); + MtpObjectInfo info = mObjectList.get(position); + nameView.setText(info.getName()); + + int thumbFormat = info.getThumbFormat(); + if (thumbFormat == MtpConstants.FORMAT_EXIF_JPEG + || thumbFormat == MtpConstants.FORMAT_JFIF) { + byte[] thumbnail = mClient.getThumbnail(mDeviceName, info.getObjectHandle()); + if (thumbnail != null) { + Bitmap bitmap = BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length); + if (bitmap != null) { + ImageView thumbView = (ImageView)view.findViewById(R.id.thumbnail); + thumbView.setImageBitmap(bitmap); + } + } + } + return view; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mClient = ((CameraBrowserApplication)getApplication()).getMtpClient(); + mDeviceName = getIntent().getStringExtra("device"); + mStorageID = getIntent().getIntExtra("storage", 0); + mObjectID = getIntent().getIntExtra("object", 0); + mDisconnectedReceiver = new DeviceDisconnectedReceiver(this, mDeviceName); + } + + @Override + protected void onResume() { + super.onResume(); + + mObjectList = mClient.getObjectList(mDeviceName, mStorageID, mObjectID); + setListAdapter(new ObjectAdapter(this)); + } + + @Override + protected void onDestroy() { + unregisterReceiver(mDisconnectedReceiver); + super.onDestroy(); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + MtpObjectInfo info = mObjectList.get(position); + Intent intent; + if (info.getFormat() == MtpConstants.FORMAT_ASSOCIATION) { + intent = new Intent(this, ObjectBrowser.class); + } else { + intent = new Intent(this, ObjectViewer.class); + } + intent.putExtra("device", mDeviceName); + intent.putExtra("storage", mStorageID); + intent.putExtra("object", info.getObjectHandle()); + startActivity(intent); + } +} diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java new file mode 100644 index 000000000000..e9ea9f3a1bd8 --- /dev/null +++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camerabrowser; + +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.media.MediaScannerConnection; +import android.media.MediaScannerConnection.MediaScannerConnectionClient; +import android.mtp.MtpClient; +import android.mtp.MtpConstants; +import android.mtp.MtpObjectInfo; +import android.net.Uri; +import android.os.Bundle; +import android.os.Environment; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import java.io.File; +import java.util.Date; + +/** + * A view to display the properties of an object. + */ +public class ObjectViewer extends Activity implements View.OnClickListener { + + private static final String TAG = "ObjectViewer"; + + private MtpClient mClient; + private String mDeviceName; + private int mStorageID; + private int mObjectID; + private String mFileName; + private Button mImportButton; + private Button mDeleteButton; + private DeviceDisconnectedReceiver mDisconnectedReceiver; + + private final class ScannerClient implements MediaScannerConnectionClient { + private final Context mContext; + private String mPath; + + public ScannerClient(Context context) { + mContext = context; + } + + public void setScanPath(String path) { + mPath = path; + } + + @Override + public void onMediaScannerConnected() { + mScannerConnection.scanFile(mPath, null); + } + + @Override + public void onScanCompleted(String path, Uri uri) { + mScannerConnection.disconnect(); + + // try to start an activity to view the file + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + try { + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + Toast.makeText(mContext, R.string.start_activity_failed_message, + Toast.LENGTH_SHORT).show(); + } + } + } + + private MediaScannerConnection mScannerConnection; + private ScannerClient mScannerClient; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mClient = ((CameraBrowserApplication)getApplication()).getMtpClient(); + + setContentView(R.layout.object_info); + + mImportButton = (Button)findViewById(R.id.import_button); + mImportButton.setOnClickListener(this); + mDeleteButton = (Button)findViewById(R.id.delete_button); + mDeleteButton.setOnClickListener(this); + + mDeviceName = getIntent().getStringExtra("device"); + mStorageID = getIntent().getIntExtra("storage", 0); + mObjectID = getIntent().getIntExtra("object", 0); + mDisconnectedReceiver = new DeviceDisconnectedReceiver(this, mDeviceName); + mScannerClient = new ScannerClient(this); + mScannerConnection = new MediaScannerConnection(this, mScannerClient); + } + + @Override + protected void onResume() { + super.onResume(); + + MtpObjectInfo info = mClient.getObjectInfo(mDeviceName, mObjectID); + if (info != null) { + TextView view = (TextView)findViewById(R.id.name); + mFileName = info.getName(); + view.setText(mFileName); + view = (TextView)findViewById(R.id.format); + view.setText(Integer.toHexString(info.getFormat()).toUpperCase()); + view = (TextView)findViewById(R.id.size); + view.setText(Long.toString(info.getCompressedSize())); + view = (TextView)findViewById(R.id.thumb_width); + view.setText(Long.toString(info.getThumbPixWidth())); + view = (TextView)findViewById(R.id.thumb_height); + view.setText(Long.toString(info.getThumbPixHeight())); + view = (TextView)findViewById(R.id.thumb_size); + view.setText(Long.toString(info.getThumbCompressedSize())); + view = (TextView)findViewById(R.id.width); + view.setText(Long.toString(info.getImagePixWidth())); + view = (TextView)findViewById(R.id.height); + view.setText(Long.toString(info.getImagePixHeight())); + view = (TextView)findViewById(R.id.depth); + view.setText(Long.toString(info.getImagePixDepth())); + view = (TextView)findViewById(R.id.sequence); + view.setText(Long.toString(info.getSequenceNumber())); + view = (TextView)findViewById(R.id.created); + Date date = new Date(info.getDateCreated() * 1000); + view.setText(date.toString()); + view = (TextView)findViewById(R.id.modified); + date = new Date(info.getDateModified() * 1000); + view.setText(date.toString()); + view = (TextView)findViewById(R.id.keywords); + view.setText(info.getKeywords()); + int thumbFormat = info.getThumbFormat(); + if (thumbFormat == MtpConstants.FORMAT_EXIF_JPEG + || thumbFormat == MtpConstants.FORMAT_JFIF) { + byte[] thumbnail = mClient.getThumbnail(mDeviceName, info.getObjectHandle()); + if (thumbnail != null) { + Bitmap bitmap = BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length); + if (bitmap != null) { + ImageView thumbView = (ImageView)findViewById(R.id.thumbnail); + thumbView.setImageBitmap(bitmap); + } + } + } + } + } + + @Override + protected void onDestroy() { + unregisterReceiver(mDisconnectedReceiver); + super.onDestroy(); + } + + private void importObject() { + // copy file to /mnt/sdcard/imported/<filename> + File dest = Environment.getExternalStorageDirectory(); + dest = new File(dest, "imported"); + dest.mkdirs(); + dest = new File(dest, mFileName); + + if (mClient.importFile(mDeviceName, mObjectID, dest.getAbsolutePath())) { + Toast.makeText(this, R.string.object_saved_message, Toast.LENGTH_SHORT).show(); + + mScannerClient.setScanPath(dest.getAbsolutePath()); + mScannerConnection.connect(); + } else { + Toast.makeText(this, R.string.save_failed_message, Toast.LENGTH_SHORT).show(); + } + } + + private void deleteObject() { + if (mClient.deleteObject(mDeviceName, mObjectID)) { + Toast.makeText(this, R.string.object_deleted_message, Toast.LENGTH_SHORT).show(); + finish(); + } else { + Toast.makeText(this, R.string.delete_failed_message, Toast.LENGTH_SHORT).show(); + } + } + + public void onClick(View v) { + if (v == mImportButton) { + importObject(); + } else if (v == mDeleteButton) { + deleteObject(); + } + } +} diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java new file mode 100644 index 000000000000..7d5a5dace7ad --- /dev/null +++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camerabrowser; + +import android.app.ListActivity; +import android.content.Context; +import android.content.Intent; +import android.mtp.MtpClient; +import android.mtp.MtpDevice; +import android.mtp.MtpStorageInfo; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.List; + +/** + * A list view displaying all storage units on a device. + */ +public class StorageBrowser extends ListActivity { + + private static final String TAG = "StorageBrowser"; + + private MtpClient mClient; + private String mDeviceName; + private List<MtpStorageInfo> mStorageList; + private DeviceDisconnectedReceiver mDisconnectedReceiver; + + private class StorageAdapter extends BaseAdapter { + private final Context mContext; + private final LayoutInflater mInflater; + + public StorageAdapter(Context c) { + mContext = c; + mInflater = (LayoutInflater)c.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + public int getCount() { + if (mStorageList == null) { + return 0; + } else { + return mStorageList.size(); + } + } + + public Object getItem(int position) { + return mStorageList.get(position); + } + + public long getItemId(int position) { + return position; + } + + public View getView(int position, View convertView, ViewGroup parent) { + TextView view; + if (convertView == null) { + view = (TextView)mInflater.inflate( + android.R.layout.simple_list_item_1, parent, false); + } else { + view = (TextView)convertView; + } + + MtpStorageInfo info = mStorageList.get(position); + if (info != null) { + view.setText(info.getDescription()); + } else { + view.setText("???"); + } + return view; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mClient = ((CameraBrowserApplication)getApplication()).getMtpClient(); + mDeviceName = getIntent().getStringExtra("device"); + mDisconnectedReceiver = new DeviceDisconnectedReceiver(this, mDeviceName); + } + + @Override + protected void onResume() { + super.onResume(); + mStorageList = mClient.getStorageList(mDeviceName); + setListAdapter(new StorageAdapter(this)); + } + + @Override + protected void onDestroy() { + unregisterReceiver(mDisconnectedReceiver); + super.onDestroy(); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + Intent intent = new Intent(this, ObjectBrowser.class); + intent.putExtra("device", mDeviceName); + intent.putExtra("storage", mStorageList.get(position).getStorageId()); + startActivity(intent); + } +} diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/UsbReceiver.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/UsbReceiver.java new file mode 100644 index 000000000000..22d9443af6ee --- /dev/null +++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/UsbReceiver.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camerabrowser; + +import android.content.Context; +import android.content.Intent; +import android.content.BroadcastReceiver; +import android.hardware.UsbDevice; +import android.hardware.UsbManager; +import android.mtp.MtpClient; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.Log; + +public class UsbReceiver extends BroadcastReceiver +{ + private static final String TAG = "UsbReceiver"; + + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "onReceive " + intent); + if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { + UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + if (MtpClient.isCamera(device)) { + String deviceName = device.getDeviceName(); + Log.d(TAG, "Got camera: " + deviceName); + intent = new Intent(context, StorageBrowser.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra("device", deviceName); + context.startActivity(intent); + } + } + } +} diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml index 2253eb26e438..dd5e026b5f84 100644 --- a/media/tests/MediaFrameworkTest/AndroidManifest.xml +++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml @@ -22,6 +22,8 @@ <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> + <application> <uses-library android:name="android.test.runner" /> <activity android:label="@string/app_name" @@ -58,4 +60,9 @@ android:label="Media Power tests InstrumentationRunner"> </instrumentation> + <instrumentation android:name=".MediaPlayerStressTestRunner" + android:targetPackage="com.android.mediaframeworktest" + android:label="Media Power tests InstrumentationRunner"> + </instrumentation> + </manifest> diff --git a/media/tests/MediaFrameworkTest/res/layout/surface_view.xml b/media/tests/MediaFrameworkTest/res/layout/surface_view.xml index a72c283d8812..4999e5d4414d 100644 --- a/media/tests/MediaFrameworkTest/res/layout/surface_view.xml +++ b/media/tests/MediaFrameworkTest/res/layout/surface_view.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2007 The Android Open Source Project +<!-- Copyright (C) 2011 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,29 +14,33 @@ limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> - + <SurfaceView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" /> - - <VideoView - android:id="@+id/video_view" + + <ImageView android:id="@+id/overlay_layer" + android:layout_width="0dip" + android:layout_height="392dip"/> + + <VideoView + android:id="@+id/video_view" android:layout_width="320px" android:layout_height="240px" /> - + </FrameLayout> - + </LinearLayout> diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java index 9fb49b12a5bb..79fd2cb741e0 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import android.graphics.Color; import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; +import android.os.PowerManager; import android.provider.Downloads; import android.util.Log; import android.util.Log; @@ -40,12 +41,15 @@ import android.widget.MediaController; import android.widget.VideoView; import com.android.mediaframeworktest.MediaNames; +import android.graphics.Bitmap; +import android.widget.ImageView; + import java.io.File; import java.io.FileDescriptor; import java.net.InetAddress; -public class MediaFrameworkTest extends Activity { +public class MediaFrameworkTest extends Activity implements SurfaceHolder.Callback { //public static Surface video_sf; public static SurfaceView mSurfaceView; @@ -58,27 +62,60 @@ public class MediaFrameworkTest extends Activity { public static AssetFileDescriptor midiafd; public static AssetFileDescriptor mp3afd; - + public static Bitmap mDestBitmap; + public static ImageView mOverlayView; + private SurfaceHolder mSurfaceHolder = null; + private String TAG = "MediaFrameworkTest"; + private PowerManager.WakeLock mWakeLock = null; + public MediaFrameworkTest() { } - /** Called when the activity is first created. */ @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.surface_view); mSurfaceView = (SurfaceView)findViewById(R.id.surface_view); + mOverlayView = (ImageView)findViewById(R.id.overlay_layer); ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams(); - mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + mSurfaceHolder = mSurfaceView.getHolder(); + mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + mSurfaceHolder.addCallback(this); //Get the midi fd midiafd = this.getResources().openRawResourceFd(R.raw.testmidi); //Get the mp3 fd mp3afd = this.getResources().openRawResourceFd(R.raw.testmp3); + mOverlayView.setLayoutParams(lp); + mDestBitmap = Bitmap.createBitmap((int)640, (int)480, Bitmap.Config.ARGB_8888); + mOverlayView.setImageBitmap(mDestBitmap); + + //Acquire the full wake lock to keep the device up + PowerManager pm = (PowerManager) this.getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "MediaFrameworkTest"); + mWakeLock.acquire(); } - + + public void onStop(Bundle icicle) { + mWakeLock.release(); + } + + public void surfaceDestroyed(SurfaceHolder holder) { + //Can do nothing in here. The test case will fail if the surface destroyed. + Log.v(TAG, "Test application surface destroyed"); + mSurfaceHolder = null; + } + + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + //Do nothing in here. Just print out the log + Log.v(TAG, "Test application surface changed"); + } + + public void surfaceCreated(SurfaceHolder holder) { + } + public void startPlayback(String filename){ String mimetype = "audio/mpeg"; Uri path = Uri.parse(filename); @@ -148,4 +185,9 @@ public class MediaFrameworkTest extends Activity { InetAddress address = InetAddress.getByAddress(MediaNames.STREAM_SERVER); return address.isReachable(10000); } + + public static void testInvalidateOverlay() { + mOverlayView.invalidate(); + } + } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java index 46135ff19015..f3cf0f71c6bb 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +32,12 @@ import com.android.mediaframeworktest.functional.MediaEqualizerTest; import com.android.mediaframeworktest.functional.MediaPresetReverbTest; import com.android.mediaframeworktest.functional.MediaVirtualizerTest; import com.android.mediaframeworktest.functional.MediaVisualizerTest; +/*import for VideoEditor Test cases*/ +import com.android.mediaframeworktest.functional.MediaItemThumbnailTest; +import com.android.mediaframeworktest.functional.MediaPropertiesTest; +import com.android.mediaframeworktest.functional.VideoEditorAPITest; +import com.android.mediaframeworktest.functional.VideoEditorExportTest; +import com.android.mediaframeworktest.functional.VideoEditorPreviewTest; import junit.framework.TestSuite; import android.test.InstrumentationTestRunner; @@ -69,6 +75,12 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner { suite.addTestSuite(MediaPresetReverbTest.class); suite.addTestSuite(MediaVirtualizerTest.class); suite.addTestSuite(MediaVisualizerTest.class); + /*Test for Video Editor*/ + suite.addTestSuite(MediaItemThumbnailTest.class); + suite.addTestSuite(MediaPropertiesTest.class); + suite.addTestSuite(VideoEditorAPITest.class); + suite.addTestSuite(VideoEditorExportTest.class); + suite.addTestSuite(VideoEditorPreviewTest.class); return suite; } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaPlayerStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaPlayerStressTestRunner.java new file mode 100755 index 000000000000..543806135f26 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaPlayerStressTestRunner.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest; + +import android.test.InstrumentationTestRunner; +import android.test.InstrumentationTestSuite; +import com.android.mediaframeworktest.stress.MediaPlayerStressTest; + +import junit.framework.TestSuite; + +public class MediaPlayerStressTestRunner extends InstrumentationTestRunner { + + @Override + public TestSuite getAllTests() { + TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(MediaPlayerStressTest.class); + return suite; + } + + @Override + public ClassLoader getLoader() { + return MediaPlayerStressTestRunner.class.getClassLoader(); + } +} diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java index 3e46e27a80ab..0b0d0ce1c2fc 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java @@ -21,7 +21,6 @@ import android.os.Bundle; import android.test.InstrumentationTestRunner; import android.test.InstrumentationTestSuite; import com.android.mediaframeworktest.stress.MediaRecorderStressTest; -import com.android.mediaframeworktest.stress.MediaPlayerStressTest; import junit.framework.TestSuite; @@ -42,7 +41,6 @@ public class MediaRecorderStressTestRunner extends InstrumentationTestRunner { public TestSuite getAllTests() { TestSuite suite = new InstrumentationTestSuite(this); suite.addTestSuite(MediaRecorderStressTest.class); - suite.addTestSuite(MediaPlayerStressTest.class); return suite; } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/VideoEditorHelper.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/VideoEditorHelper.java new file mode 100644 index 000000000000..dd7c4c6b1947 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/VideoEditorHelper.java @@ -0,0 +1,479 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Random; + +import junit.framework.Assert; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.media.videoeditor.AudioTrack; +import android.media.videoeditor.EffectColor; +import android.media.videoeditor.MediaImageItem; +import android.media.videoeditor.MediaItem; +import android.media.videoeditor.MediaVideoItem; +import android.media.videoeditor.OverlayFrame; +import android.media.videoeditor.TransitionAlpha; +import android.media.videoeditor.TransitionCrossfade; +import android.media.videoeditor.TransitionFadeBlack; +import android.media.videoeditor.TransitionSliding; +import android.media.videoeditor.VideoEditor; +import android.media.videoeditor.VideoEditorFactory; +import android.util.Log; +import android.os.Environment; + +/** + * This class has the names of the all the activity name and variables in the + * instrumentation test. + */ +public class VideoEditorHelper extends Assert { + + private final String TAG = "VideoEditorMediaNames"; + + public VideoEditorHelper() { + + } + + public static final String PROJECT_LOCATION_COMMON = + Environment.getExternalStorageDirectory().toString() + "/"; + + public static final String INPUT_FILE_PATH_COMMON = PROJECT_LOCATION_COMMON + + "media_api/videoeditor/"; + + // ----------------------------------------------------------------- + // HELPER METHODS + // ----------------------------------------------------------------- + + /** + * This method creates an object of VideoEditor + * + * @param projectPath the directory where all files related to project will + * be stored + * @param className The class which implements the VideoEditor Class + * @return the object of VideoEditor + */ + public VideoEditor createVideoEditor(String projectPath) { + VideoEditor mVideoEditor = null; + try { + mVideoEditor = VideoEditorFactory.create(projectPath); + assertNotNull("VideoEditor", mVideoEditor); + } catch (Exception e) { + fail("Unable to create Video Editor"); + } + return mVideoEditor; + } + + /** + *This method deletes the VideoEditor object created using + * createVideoEditor method + * + * @param videoEditor the VideoEditor object which needs to be cleaned up + */ + public void destroyVideoEditor(VideoEditor videoEditor) { + // Release VideoEditor + if (videoEditor != null) { + try { + videoEditor.release(); + } catch (Exception e) { + fail("Unable to destory Video Editor"); + } + } + } + + /** + *This Method checks the Range in "RangePercent" (say 10) + * + * @param int Expected data + * @param actual data + * @return boolean flag which confirms the range matching + */ + public boolean checkRange(long expected, long actual, long rangePercent) { + long range = 0; + range = (100 * actual) / expected; + + Log.i("checkRange", "Range = " + range); + if ((range > (100 - rangePercent)) && (range < (100 + rangePercent))) { + return true; + } else { + return false; + } + } + + /** + *This Method Creates a Bitmap with the given input file + * + * @param file the Input whose Bitmap has top be extracted + * @return an Object of EffectColor + */ + public Bitmap getBitmap(String file, int width, int height) throws IOException { + assertNotNull("Bitmap File is Null", file); + FileInputStream inputStream = null; + Bitmap overlayBmp = null; + if (!new File(file).exists()) + throw new IOException("File not Found " + file); + try { + final BitmapFactory.Options dbo = new BitmapFactory.Options(); + dbo.inJustDecodeBounds = true; + dbo.outWidth = width; + dbo.outHeight = height; + File flPtr = new File(file); + inputStream = new FileInputStream(flPtr); + final Bitmap srcBitmap = BitmapFactory.decodeStream(inputStream); + overlayBmp = Bitmap.createBitmap(srcBitmap); + assertNotNull("Bitmap 1", srcBitmap); + assertNotNull("Bitmap 2", overlayBmp); + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return overlayBmp; + } + + /** + *This Method Create a Media Video Item with the specified params + * + * @return an Object of MediaVideoItem + */ + public MediaVideoItem createMediaItem(VideoEditor videoEditor, + String MediaId, String filename, int renderingMode) { + MediaVideoItem mvi = null; + try { + mvi = new MediaVideoItem(videoEditor, MediaId, filename, + renderingMode); + assertNotNull("Can not create an object of MediaVideoItem", mvi); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException + ("Can not create an object of Media Video Item with file name = " + + filename + " Issue = " + e.toString()); + } catch (IOException e) { + assertTrue + ("Can not create an object of Media Video Item with file name = " + + filename + " Issue = " + e.toString(), false); + } + return mvi; + } + + /** + *This Method Create a Media Image Item with the specified params + * + * @return an Object of MediaImageItem + */ + public MediaImageItem createMediaItem(VideoEditor videoEditor, + String MediaId, String filename, long duration, int renderingMode) { + MediaImageItem mii = null; + try { + mii = new MediaImageItem(videoEditor, MediaId, filename, duration, + renderingMode); + assertNotNull("Can not create an object of MediaImageItem", mii); + + } catch (IllegalArgumentException e) { + assertTrue("Can not create an object of Media Image with file name = " + + filename + " Issue = " + e.toString(), false); + } catch (IOException e) { + assertTrue("Can not create an object of Media Image with file name = " + + filename + " Issue = " + e.toString(), false); + } + return mii; + } + + /** + *This Method Create a Effect with the specified params + * + * @return an Object of EffectColor + */ + public EffectColor createEffectItem(MediaItem mediaItem, String effectId, + long startTime, long duration, int effectType, int colorType) { + EffectColor effectonMVI = null; + effectonMVI = new EffectColor(mediaItem, effectId, startTime, + duration, effectType, colorType); + return effectonMVI; + } + + /** + *This Method creates object of Type Transition Cross fade + * + * @return TransitionCrossfade object + */ + public TransitionCrossfade createTCrossFade(String transitionId, + MediaItem afterMediaItem, MediaItem beforeMediaItem, long durationMs, + int behavior) { + Log.i("TransitionCrossfade Details === ", "Transid ID = " + transitionId + + " Duration= " + durationMs + " Behaviour " + behavior); + + TransitionCrossfade transitionCF = null; + transitionCF = new TransitionCrossfade(transitionId, afterMediaItem, + beforeMediaItem, durationMs, behavior); + return transitionCF; + } + + /** + *This Method creates object of Type TransitionFadeBlack + * + * @return TransitionFadeBlack object + */ + public TransitionFadeBlack createTFadeBlack(String transitionId, + MediaItem afterMediaItem, MediaItem beforeMediaItem, long durationMs, + int behavior) { + TransitionFadeBlack transitionFB = null; + + transitionFB = new TransitionFadeBlack(transitionId, afterMediaItem, + beforeMediaItem, durationMs, behavior); + return transitionFB; + } + + /** + *This Method creates object of Type TransitionSliding + * + * @return TransitionSliding object + */ + public TransitionSliding createTSliding(String transitionId, + MediaItem afterMediaItem, MediaItem beforeMediaItem, long durationMs, + int behavior, int direction) { + TransitionSliding transSlide = null; + transSlide = new TransitionSliding(transitionId, afterMediaItem, + beforeMediaItem, durationMs, behavior, direction); + return transSlide; + } + + /** + *This Method creates object of Type TranistionAlpha + * + * @return TranistionAlpha object + */ + + public TransitionAlpha createTAlpha(String transitionId, + MediaItem afterMediaItem, MediaItem beforeMediaItem, long durationMs, + int behavior, String maskFilename, int blendingPercent, boolean invert) { + TransitionAlpha transA = null; + transA = new TransitionAlpha(transitionId, afterMediaItem, + beforeMediaItem, durationMs, behavior, maskFilename, + blendingPercent, invert); + return transA; + } + + /** + *This Method creates object of Type OverlayFrame + * + * @return OverlayFrame object + */ + + public OverlayFrame createOverlay(MediaItem mediaItem, String overlayId, + Bitmap bitmap, long startTimeMs, long durationMs) { + OverlayFrame overLayFrame = null; + overLayFrame = new OverlayFrame(mediaItem, overlayId, bitmap, + startTimeMs, durationMs); + return overLayFrame; + } + + /** + *This Method creates object of Type AudioTrack + * + * @return OverlayFrame object + */ + public AudioTrack createAudio(VideoEditor videoEditor, String audioTrackId, + String filename) { + AudioTrack audio = null; + try { + audio = new AudioTrack(videoEditor, audioTrackId, filename); + assertNotNull("Cant not create an object of an AudioTrack " + + audioTrackId, audio); + } catch (IllegalArgumentException e) { + assertTrue("Can not create object of an AudioTrack " + + audioTrackId + " Issue = " + e.toString(), false); + } catch (IOException e) { + assertTrue("Can not create object of an AudioTrack " + + audioTrackId + " Issue = " + e.toString(), false); + } + return audio; + } + + /** + *This Method validates the Exported Movie,as per the specified params + * during Export + */ + + public void validateExport(VideoEditor videoEditor, String fileName, + int export_height, int startTime, long endTime, int vCodec, int aCodec) { + File tempFile = new File(fileName); + assertEquals("Exported FileName", tempFile.exists(), true); + final MediaVideoItem mvi = createMediaItem(videoEditor, "m1", fileName, + MediaItem.RENDERING_MODE_BLACK_BORDER); + + Log.i(TAG, "VideoCodec for file = " + fileName + + "\tExpected Video Codec = " + vCodec + "\tActual Video Codec = " + + mvi.getVideoType()); + assertEquals("Export: Video Codec Mismatch for file = " + fileName + + "\t<expected> " + vCodec + "\t<actual> " + mvi.getVideoType(), + vCodec, mvi.getVideoType()); + + Log.i(TAG, "Height for file = " + fileName + "\tExpected Height = " + + export_height + "\tActual VideoHeight = " + mvi.getHeight()); + assertEquals("Export height Mismatch for file " + fileName + + "\t<expected> " + export_height + "\t<actual> " + mvi.getHeight(), + export_height, mvi.getHeight()); + if (startTime == 0) { + if (endTime != 0) { + Log.i(TAG, "TimeLine Expected = " + (startTime + endTime) + + "\t VideoTime= " + mvi.getTimelineDuration()); + assertTrue("Timeline Duration Mismatch for file " + fileName + + "<expected> " + (startTime + endTime) + "\t<actual> " + + mvi.getTimelineDuration(), checkRange((startTime + + endTime), mvi.getTimelineDuration(), 10)); + } + } else { + Log.i(TAG, "TimeLine Expected = " + (endTime - startTime) + + "\t VideoTime= " + mvi.getTimelineDuration()); + assertTrue("Timeline Duration Mismatch for file " + fileName + + "<expected> " + (endTime - startTime) + "\t<actual> " + + mvi.getTimelineDuration(), checkRange((endTime - + startTime), (int)mvi.getTimelineDuration(), 10)); + } + } + + /** + * @param videoEditor + * @param fileName + * @param export_bitrate + * @param export_height + * @param startTime + * @param endTime + * @param vCodec + * @param aCodec + */ + public void validateExport(VideoEditor videoEditor, String fileName, + int export_height, int startTime, int endTime, int vCodec, int aCodec) { + File tempFile = new File(fileName); + assertEquals("Exported FileName", tempFile.exists(), true); + final MediaVideoItem mvi = createMediaItem(videoEditor, "m1", fileName, + MediaItem.RENDERING_MODE_BLACK_BORDER); + Log.i(TAG, "VideoCodec for file = " + fileName + + "\tExpected Video Codec = " + vCodec + "\tActual Video Codec = " + + mvi.getVideoType()); + assertEquals("Export: Video Codec Mismatch for file = " + fileName + + "\t<expected> " + vCodec + "\t<actual> " + mvi.getVideoType(), + vCodec, mvi.getVideoType()); + + Log.i(TAG, "AudioCodec for file = " + fileName + + "\tExpected Audio Codec = " + aCodec + "\tActual Audio Codec = " + + mvi.getAudioType()); + assertEquals("Export: Audio Codec Mismatch for file = " + fileName + + "\t<expected> " + aCodec + "\t<actual> " + mvi.getAudioType(), + aCodec, mvi.getAudioType()); + + Log.i(TAG, "Height for file = " + fileName + "\tExpected Height = " + + export_height + "\tActual VideoHeight = " + mvi.getHeight()); + assertEquals("Export: height Mismatch for file " + fileName + + "\t<expected> " + export_height + "\t<actual> " + mvi.getHeight(), + export_height, mvi.getHeight()); + if (startTime == 0) { + if (endTime != 0) { + Log.i(TAG, "TimeLine Expected = " + (startTime + endTime) + + "\t VideoTime= " + mvi.getTimelineDuration()); + assertTrue("Export :Timeline Duration Mismatch for file " + + fileName + "<expected> " + (startTime + endTime) + + "\t<actual> " + mvi.getTimelineDuration(), + checkRange((startTime + endTime), mvi.getTimelineDuration(), 10)); + } + } else { + Log.i(TAG, "TimeLine Expected = " + (endTime-startTime) + + "\t VideoTime= " + mvi.getTimelineDuration()); + assertTrue("Timeline Duration Mismatch for file " + fileName + + "<expected> " + (endTime - startTime) + "\t<actual> " + + mvi.getTimelineDuration(), checkRange((endTime - + startTime), mvi.getTimelineDuration(), 10)); + } + } + + /** + * Check file and deletes it. + * + * @param filename + */ + public void checkDeleteExistingFile(String filename) { + Log.i(TAG, ">>>>>>>>>>>>>>>>>>checkDeleteExistingFile = " + filename); + if (filename != null) { + File temp = new File(filename); + if (temp != null && temp.exists()) { + temp.delete(); + } + } + } + + /** + * This method creates a Directory and filename + * + * @param location This is path where the file is to be created + * "/sdcard/Output/" + * @return Path in form of /sdcard/Output/200910100000 + */ + public String createRandomFile(String location) { + Random randomGenerator = new Random(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssS"); + Date date = new Date(); + final String filePath = location + dateFormat.format(date) + + randomGenerator.nextInt(10); + Log.i(TAG, ">>>>>>>>>>>>>>>>createRandomFile Location= " + location + + "\t FilePath = " + filePath); + return filePath; + } + + /** + * This method recursively deletes all the file and directory + * + * @param directory where the files are located Example = "/sdcard/Input" + * @return boolean True if deletion is successful else False + */ + public boolean deleteProject(File directory) { + Log.i(TAG, ">>>>>>>>>>>>>>>>>>>>>>>>deleteProject directory= " + + directory.toString()); + if (directory.isDirectory()) { + String[] filesInDirecory = directory.list(); + for (int i = 0; i < filesInDirecory.length; i++) { + boolean success = deleteProject(new File(directory, + filesInDirecory[i])); + if (!success) { + return false; + } + } + } + return directory.delete(); + } + + /** + * This method compares the array of Integer from 0 - 100 + * + * @param data set of integer values received as progress + * @return true if sucess else false + */ + public boolean checkProgressCBValues(int[] data) { + boolean retFlag = false; + for (int i = 0; i < 100; i++) { + if (data[i] == 100) { + retFlag = true; + break; + } else { + retFlag = false; + } + } + return retFlag; + } +} diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java index 2eea206bacd4..e84f762d9a9b 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java @@ -45,7 +45,7 @@ import java.util.Random; */ public class CodecTest { - private static String TAG = "MediaPlayerApiTest"; + private static String TAG = "CodecTest"; private static MediaPlayer mMediaPlayer; private MediaPlayer.OnPreparedListener mOnPreparedListener; @@ -58,7 +58,13 @@ public class CodecTest { private static final Object videoSizeChanged = new Object(); private static final Object onCompletion = new Object(); private static boolean onPrepareSuccess = false; - private static boolean onCompleteSuccess = false; + public static boolean onCompleteSuccess = false; + public static boolean mPlaybackError = false; + public static int mMediaInfoUnknownCount = 0; + public static int mMediaInfoVideoTrackLaggingCount = 0; + public static int mMediaInfoBadInterleavingCount = 0; + public static int mMediaInfoNotSeekableCount = 0; + public static int mMediaInfoMetdataUpdateCount = 0; public static String printCpuInfo(){ String cm = "dumpsys cpuinfo"; @@ -747,13 +753,52 @@ public class CodecTest { } }; + static MediaPlayer.OnErrorListener mOnErrorListener = new MediaPlayer.OnErrorListener() { + public boolean onError(MediaPlayer mp, int framework_err, int impl_err) { + mPlaybackError = true; + mp.reset(); + return true; + } + }; + + static MediaPlayer.OnInfoListener mInfoListener = new MediaPlayer.OnInfoListener() { + public boolean onInfo(MediaPlayer mp, int what, int extra) { + switch (what){ + case MediaPlayer.MEDIA_INFO_UNKNOWN: + mMediaInfoUnknownCount++; + break; + case MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING: + mMediaInfoVideoTrackLaggingCount++; + break; + case MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING: + mMediaInfoBadInterleavingCount++; + break; + case MediaPlayer.MEDIA_INFO_NOT_SEEKABLE: + mMediaInfoNotSeekableCount++; + break; + case MediaPlayer.MEDIA_INFO_METADATA_UPDATE: + mMediaInfoMetdataUpdateCount++; + break; + } + return true; + } + }; + // For each media file, forward twice and backward once, then play to the end public static boolean playMediaSamples(String filePath) throws Exception { int duration = 0; int curPosition = 0; int nextPosition = 0; int waittime = 0; - Random r = new Random(); + onCompleteSuccess = false; + mMediaInfoUnknownCount = 0; + mMediaInfoVideoTrackLaggingCount = 0; + mMediaInfoBadInterleavingCount = 0; + mMediaInfoNotSeekableCount = 0; + mMediaInfoMetdataUpdateCount = 0; + mPlaybackError = false; + String testResult; + initializeMessageLooper(); synchronized (lock) { try { @@ -765,37 +810,19 @@ public class CodecTest { } try { mMediaPlayer.setOnCompletionListener(mCompletionListener); + mMediaPlayer.setOnErrorListener(mOnErrorListener); + mMediaPlayer.setOnInfoListener(mInfoListener); Log.v(TAG, "playMediaSamples: sample file name " + filePath); mMediaPlayer.setDataSource(filePath); mMediaPlayer.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder()); mMediaPlayer.prepare(); duration = mMediaPlayer.getDuration(); - Log.v(TAG, "playMediaSamples: duration = " + duration); // start to play mMediaPlayer.start(); - // randomly play for time within (0, duration/3) - Thread.sleep(r.nextInt(duration/3)); - mMediaPlayer.pause(); - Log.v(TAG, "playMediaSamples: current position after pause: " - + mMediaPlayer.getCurrentPosition()); - // seek to position (0, 2/3*duration) - nextPosition = mMediaPlayer.getCurrentPosition() + r.nextInt(duration/3); - mMediaPlayer.seekTo(nextPosition); - Log.v(TAG, "playMediaSamples: current position after the first seek:" - + mMediaPlayer.getCurrentPosition()); - // play for another short time - mMediaPlayer.start(); - Thread.sleep(r.nextInt(duration/6)); - Log.v(TAG, "playMediaSamples: position after the second play:" - + mMediaPlayer.getCurrentPosition()); - // seek to a random position (0, duration) - mMediaPlayer.seekTo(r.nextInt(duration)); - Log.v(TAG, "playMediaSamples: current position after the second seek:" - + mMediaPlayer.getCurrentPosition()); waittime = duration - mMediaPlayer.getCurrentPosition(); synchronized(onCompletion){ try { - onCompletion.wait(waittime + 30000); + onCompletion.wait(waittime + 2000); }catch (Exception e) { Log.v(TAG, "playMediaSamples are interrupted"); return false; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaItemThumbnailTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaItemThumbnailTest.java new file mode 100755 index 000000000000..895ca2531a61 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaItemThumbnailTest.java @@ -0,0 +1,954 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.mediaframeworktest.functional; + +import java.io.File; +import java.io.IOException; + +import android.graphics.Bitmap; +import android.media.videoeditor.MediaImageItem; +import android.media.videoeditor.MediaItem; +import android.media.videoeditor.MediaVideoItem; +import android.media.videoeditor.VideoEditor; +import android.os.Environment; +import android.test.ActivityInstrumentationTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.VideoEditorHelper; + +public class MediaItemThumbnailTest extends + ActivityInstrumentationTestCase<MediaFrameworkTest> { + private final String TAG = "MediaItemThumbailTest"; + + private final String PROJECT_LOCATION = VideoEditorHelper.PROJECT_LOCATION_COMMON; + + private final String INPUT_FILE_PATH = VideoEditorHelper.INPUT_FILE_PATH_COMMON; + + private VideoEditor mVideoEditor; + + private VideoEditorHelper mVideoEditorHelper; + + public MediaItemThumbnailTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + // setup for each test case. + super.setUp(); + mVideoEditorHelper = new VideoEditorHelper(); + // Create a random String which will be used as project path, where all + // project related files will be stored. + final String projectPath = mVideoEditorHelper. + createRandomFile(PROJECT_LOCATION); + mVideoEditor = mVideoEditorHelper.createVideoEditor(projectPath); + } + + @Override + protected void tearDown() throws Exception { + mVideoEditorHelper.destroyVideoEditor(mVideoEditor); + // Clean the directory created as project path + mVideoEditorHelper.deleteProject(new File(mVideoEditor.getPath())); + System.gc(); + super.tearDown(); + } + + protected void validateThumbnail(Bitmap thumbNailBmp, int outWidth, + int outHeight) throws Exception { + assertNotNull("Thumbnail Retrived is Null", thumbNailBmp); + assertEquals("Thumbnail Height", outHeight, thumbNailBmp.getHeight()); + assertEquals("Thumbnail Width", outWidth, thumbNailBmp.getWidth()); + thumbNailBmp.recycle(); + } + + // ----------------------------------------------------------------- + // THUMBNAIL + // ----------------------------------------------------------------- + /** + * To test thumbnail / frame extraction on H.263 QCIF. + */ + // TODO : TC_TN_001 + @LargeTest + public void testThumbnailForH263QCIF() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final int atTime = 0; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + + final int outWidth = (mediaVideoItem.getWidth() / 2); + final int outHeight = mediaVideoItem.getHeight(); + + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + + /** + * To test thumbnail / frame extraction on MPEG4 VGA . + */ + // TODO : TC_TN_002 + @LargeTest + public void testThumbnailForMPEG4VGA() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_640x480_30fps_512Kbps_0_23.3gp"; + final int atTime = 0; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = (mediaVideoItem.getWidth() / 2); + final int outHeight = mediaVideoItem.getHeight(); + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + + /** + * To test thumbnail / frame extraction on MPEG4 NTSC. + */ + // TODO : TC_TN_003 + @LargeTest + public void testThumbnailForMPEG4NTSC() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4"; + final int atTime = 0; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = mediaVideoItem.getWidth() / 2; + final int outHeight = mediaVideoItem.getHeight() / 2; + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + + /** + * To test thumbnail / frame extraction on MPEG4 WVGA. + */ + // TODO : TC_TN_004 + @LargeTest + public void testThumbnailForMPEG4WVGA() throws Exception { + + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4"; + final int atTime = 0; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = mediaVideoItem.getWidth() * 2; + final int outHeight = mediaVideoItem.getHeight(); + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + + /** + * To test thumbnail / frame extraction on MPEG4 QCIF. + */ + // TODO : TC_TN_005 + @LargeTest + public void testThumbnailForMPEG4QCIF() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp"; + final int atTime = 0; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = mediaVideoItem.getWidth(); + final int outHeight = mediaVideoItem.getHeight() * 2; + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + + /** + * To test thumbnail / frame extraction on H264 QCIF. + */ + // TODO : TC_TN_006 + @LargeTest + public void testThumbnailForH264QCIF() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_176x144_15fps_144kbps_AMRNB_8kHz_12.2kbps_m_1_17.3gp"; + + final int atTime = 0; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = mediaVideoItem.getWidth() * 2; + final int outHeight = mediaVideoItem.getHeight() * 2; + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + + /** + * To test thumbnail / frame extraction on H264 VGA. + */ + // TODO : TC_TN_007 + @LargeTest + public void testThumbnailForH264VGA() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_192kbps_1_5.mp4"; + final int outWidth = 32; + final int outHeight = 32; + final int atTime = 0; + + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + /** + * To test thumbnail / frame extraction on H264 WVGA. + */ + // TODO : TC_TN_008 + @LargeTest + public void testThumbnailForH264WVGA() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4"; + final int outWidth = 64; + final int outHeight = 64; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final long atTime = mediaVideoItem.getDuration() / 2; + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + + /** + * To test thumbnail / frame extraction on H264 854x480. + */ + // TODO : TC_TN_009 + @LargeTest + public void testThumbnailForH264854_480() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_854x480_15fps_256kbps_AACLC_16khz_48kbps_s_0_26.mp4"; + final int outWidth = 128; + final int outHeight = 128; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + MediaVideoItem mediaVideoItem = null; + mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final long atTime = mediaVideoItem.getDuration() - 1000; + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + + /** + * To test thumbnail / frame extraction on H264 960x720. + */ + // TODO : TC_TN_010 + @LargeTest + public void testThumbnailForH264HD960() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4"; + final int outWidth = 75; + final int outHeight = 75; + + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final long atTime = mediaVideoItem.getDuration() - 1000; + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + + /** + * To test thumbnail / frame extraction on H264 1080x720 . + */ + // TODO : TC_TN_011 + @LargeTest + public void testThumbnailForH264HD1080() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_1080x720_30fps_800kbps_1_17.mp4"; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = mediaVideoItem.getWidth() / 2; + final int outHeight = mediaVideoItem.getHeight() / 2; + final long atTime = mediaVideoItem.getDuration() / 4; + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + + /** + * Check the thumbnail / frame extraction precision at 0,100 and 200 ms + */ + // TODO : TC_TN_012 + @LargeTest + public void testThumbnailForH264VGADifferentDuration() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + final int atTime = 0; + final int atTime1 = 100; + final int atTime2 = 200; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = mediaVideoItem.getWidth(); + final int outHeight = mediaVideoItem.getHeight(); + + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + + // get Thumbnail @ 100ms + final Bitmap thumbNailBmpAt100 = + mediaVideoItem.getThumbnail(outWidth, outHeight, atTime1); + validateThumbnail(thumbNailBmpAt100, outWidth, outHeight); + + // get Thumbnail @ 200ms + final Bitmap thumbNailBmpAt200 = mediaVideoItem.getThumbnail( + outWidth, outHeight, atTime2); + validateThumbnail(thumbNailBmpAt200, outWidth, outHeight); + } + + /** + *Check the thumbnail / frame extraction precision at + * FileDuration,FileDuration/2 + 100 andFileDuration/2 + 200 ms + */ + // TODO : TC_TN_013 + @LargeTest + public void testThumbnailForMP4VGA() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_640x480_15fps_256kbps_0_30.mp4"; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, MediaItem.RENDERING_MODE_BLACK_BORDER); + + final int outWidth = mediaVideoItem.getWidth(); + final int outHeight = mediaVideoItem.getHeight(); + final long atTime = mediaVideoItem.getDuration() / 2; + final long atTime1 = atTime + 100; + final long atTime2 = atTime + 200; + + // get Thumbnail @ duration/2 + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + + // get Thumbnail @ duration/2 + 100ms + final Bitmap thumbNailBmpAt100 = mediaVideoItem.getThumbnail( + outWidth, outHeight, atTime1); + validateThumbnail(thumbNailBmpAt100, outWidth, outHeight); + + // get Thumbnail @ duration/2 + 200ms + final Bitmap thumbNailBmpAt200 = mediaVideoItem.getThumbnail( + outWidth, outHeight, atTime2); + validateThumbnail(thumbNailBmpAt200, outWidth, outHeight); + } + + /** + * Check the thumbnail / frame extraction on JPEG file + */ + // TODO : TC_TN_014 + @LargeTest + public void testThumbnailForImage() throws Exception { + final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final int mediaDuration = 1000; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + int outWidth = 0; + int outHeight = 0; + + final MediaImageItem mii = mVideoEditorHelper.createMediaItem( + mVideoEditor, "m1", imageItemFilename, mediaDuration, renderingMode); + assertNotNull("Media Image Item is Null", mii); + outWidth = mii.getWidth() / 2; + outHeight = mii.getHeight() / 2; + + final Bitmap thumbNailBmp = mii.getThumbnail(outWidth, + outHeight, mediaDuration); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + /** + *To test ThumbnailList for H263 QCIF + */ + // TODO : TC_TN_015 + @LargeTest + public void testThumbnailListH263QCIF() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp"; + final int startTime = 0; + final int tnCount = 10; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + + final int outWidth = mediaVideoItem.getWidth() / 4; + final int outHeight = mediaVideoItem.getHeight() / 4; + final long endTime = mediaVideoItem.getDuration() / 2; + + final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList( + outWidth, outHeight, startTime, endTime, tnCount); + assertNotNull("Thumbnail Retrived is Null", thumbNailBmp); + assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length); + + for (int i = 0; i < thumbNailBmp.length; i++) { + validateThumbnail(thumbNailBmp[i], outWidth, outHeight); + thumbNailBmp[i] = null; + } + } + + /** + *To test ThumbnailList for MPEG4 QCIF + */ + // TODO : TC_TN_016 + @LargeTest + public void testThumbnailListMPEG4QCIF() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp"; + final int tnCount = 10; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + + final int outWidth = mediaVideoItem.getWidth() / 2; + final int outHeight = mediaVideoItem.getHeight() / 2; + final long startTime = mediaVideoItem.getDuration() / 2; + final long endTime = mediaVideoItem.getDuration(); + + final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList( + outWidth, outHeight, startTime, endTime, tnCount); + + assertNotNull("Thumbnail Retrived is Null", thumbNailBmp); + assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length); + for (int i = 0; i < thumbNailBmp.length; i++) { + validateThumbnail(thumbNailBmp[i], outWidth, outHeight); + thumbNailBmp[i] = null; + } + } + + /** + *To test ThumbnailList for H264 VGA + */ + // TODO : TC_TN_017 + @LargeTest + public void testThumbnailListH264VGA() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + final int tnCount = 10; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + + final int outWidth = mediaVideoItem.getWidth() / 2; + final int outHeight = mediaVideoItem.getHeight() / 2; + final long startTime = mediaVideoItem.getDuration() / 3; + final long endTime = mediaVideoItem.getDuration() / 2; + + final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList( + outWidth, outHeight, startTime, endTime, tnCount); + assertNotNull("Thumbnail Retrived is Null", thumbNailBmp); + assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length); + for (int i = 0; i < thumbNailBmp.length; i++) { + validateThumbnail(thumbNailBmp[i], outWidth, outHeight); + thumbNailBmp[i] = null; + } + } + + /** + *To test ThumbnailList for H264 WVGA + */ + // TODO : TC_TN_018 + @LargeTest + public void testThumbnailListH264WVGA() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4"; + final int tnCount = 10; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + + final int outWidth = mediaVideoItem.getWidth() / 2; + final int outHeight = mediaVideoItem.getHeight() / 2; + final long startTime = mediaVideoItem.getDuration() / 3; + final long endTime = mediaVideoItem.getDuration() / 2; + + final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList( + outWidth, outHeight, startTime, endTime, tnCount); + assertNotNull("Thumbnail Retrived is Null", thumbNailBmp); + assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length); + for (int i = 0; i < thumbNailBmp.length; i++) { + validateThumbnail(thumbNailBmp[i], outWidth, outHeight); + thumbNailBmp[i] = null; + } + } + + /** + *To test ThumbnailList for H264 VGA ,Time exceeding file duration + */ + // TODO : TC_TN_019 + @LargeTest + public void testThumbnailH264VGAExceedingFileDuration() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + boolean flagForException = false; + int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + try { + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = mediaVideoItem.getWidth() / 2; + final int outHeight = mediaVideoItem.getHeight() / 2; + final long atTime = mediaVideoItem.getDuration() + 2000; + mediaVideoItem.getThumbnail(outWidth, outHeight, atTime); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Exception in Extracting thumbanil with Invalid Time", + flagForException); + } + + /** + *To test ThumbnailList for VGA Image + */ + // TODO : TC_TN_020 + @LargeTest + public void testThumbnailListVGAImage() throws Exception { + final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final int imageItemDuration = 10000; + final int startTime = 0; + final int endTime = 0; + final int tnCount = 10; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaImageItem mediaImageItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + imageItemFilename, imageItemDuration, renderingMode); + final int outWidth = mediaImageItem.getWidth() / 2; + final int outHeight = mediaImageItem.getHeight() / 2; + + final Bitmap thumbNailBmp[] = mediaImageItem.getThumbnailList + (outWidth, outHeight, startTime, endTime, tnCount); + assertNotNull("Thumbnail Retrived is Null", thumbNailBmp); + assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length); + for (int i = 0; i < thumbNailBmp.length; i++) { + validateThumbnail(thumbNailBmp[i], outWidth, outHeight); + thumbNailBmp[i] = null; + } + } + + /** + *To test ThumbnailList for Invalid file path + */ + // TODO : TC_TN_021 + @LargeTest + public void testThumbnailForInvalidFilePath() throws Exception { + final String imageItemFileName = INPUT_FILE_PATH + "/sdcard/abc.jpg"; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + try{ + final MediaImageItem mii = new MediaImageItem(mVideoEditor, "m1", + imageItemFileName, 3000, renderingMode); + }catch (IllegalArgumentException e){ + flagForException = true; + } + assertTrue(" Invalid File Path", flagForException); + } + + /** + * To test thumbnail / frame extraction with setBoundaries + */ + // TODO : TC_TN_022 + @LargeTest + public void testThumbnailForMPEG4WVGAWithSetBoundaries() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4"; + final int atTime = 10000; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + + mediaVideoItem.setExtractBoundaries(1000, + (mediaVideoItem.getDuration() - 21000)); + + final int outWidth = (mediaVideoItem.getWidth() / 2); + final int outHeight = (mediaVideoItem.getHeight() / 2); + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail(outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + + /** + *To test ThumbnailList for H264 WVGA with setExtractboundaries + */ + // TODO : TC_TN_023 + @LargeTest + public void testThumbnailListForH264WVGAWithSetBoundaries() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_800x480_15fps_512kbps_1_17.mp4"; + final int thumbNailStartTime = 10000; + final int thumbNailEndTime = 12000; + final int tnCount = 10; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + + final int outWidth = (mediaVideoItem.getWidth() / 2); + final int outHeight = (mediaVideoItem.getHeight() / 2); + + mediaVideoItem.setExtractBoundaries(10000, 12000); + + final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList + (outWidth, outHeight, thumbNailStartTime, thumbNailEndTime, + tnCount); + assertNotNull("Thumbnail Retrived is Null", thumbNailBmp); + assertTrue("Thumbnail Size", (thumbNailBmp.length > 0) ? true : false); + for (int i = 0; i < thumbNailBmp.length; i++) { + validateThumbnail(thumbNailBmp[i], outWidth, outHeight); + thumbNailBmp[i] = null; + } + } + + /** + *To test ThumbnailList for H264 WVGA with count > frame available + */ + // TODO : TC_TN_024 + @LargeTest + public void testThumbnailListForH264WVGAWithCount() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4"; + final int tnCount = 100; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + + final int outWidth = (mediaVideoItem.getWidth() / 2); + final int outHeight = (mediaVideoItem.getHeight() / 2); + final long thumbNailStartTime = mediaVideoItem.getDuration() / 2; + final long thumbNailEndTime = thumbNailStartTime + 4000; + Bitmap thumbNailBmp[] = null; + boolean flagForException = false; + try{ + thumbNailBmp = mediaVideoItem.getThumbnailList(outWidth, outHeight, + thumbNailStartTime, thumbNailEndTime, tnCount); + }catch (Exception e){ + assertTrue("Unable to get Thumbnail list", flagForException); + } + if (thumbNailBmp.length <= tnCount) { + flagForException = true; + } + assertTrue("Thumbnail count more than asked", flagForException); + } + + /** + *To test ThumbnailList for H264 WVGA with startTime > End Time + */ + // TODO : TC_TN_025 + @LargeTest + public void testThumbnailListH264WVGAWithStartGreaterEnd() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4"; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final int tnCount = 10; + boolean flagForException = false; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = (mediaVideoItem.getWidth() / 2); + final int outHeight = (mediaVideoItem.getHeight() / 2); + final long thumbNailStartTime = mediaVideoItem.getDuration() / 2; + final long thumbNailEndTime = thumbNailStartTime - 1000; + try{ + mediaVideoItem.getThumbnailList(outWidth, outHeight, + thumbNailStartTime, thumbNailEndTime, tnCount); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Thumbnail Extraction where start time > end time", + flagForException); + } + + /** + *To test ThumbnailList TC_TN_026 for H264 WVGA with startTime = End Time + */ + // TODO : TC_TN_026 + @LargeTest + public void testThumbnailListH264WVGAWithStartEqualEnd() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4"; + final int tnCount = 1; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = (mediaVideoItem.getWidth() / 2); + final int outHeight = (mediaVideoItem.getHeight() / 2); + final long thumbNailStartTime = mediaVideoItem.getDuration() / 2; + final long thumbNailEndTime = thumbNailStartTime; + final Bitmap thumbNailBmp[] = mediaVideoItem.getThumbnailList(outWidth, + outHeight, thumbNailStartTime, thumbNailEndTime, tnCount); + assertNotNull("Thumbnail Retrived is Null", thumbNailBmp); + assertEquals("Thumbnail Count", tnCount, thumbNailBmp.length); + for (int i = 0; i < thumbNailBmp.length; i++) { + validateThumbnail(thumbNailBmp[i], outWidth, outHeight); + thumbNailBmp[i] = null; + } + } + + /** + *To test ThumbnailList TC_TN_027 for file where video duration is less + * than file duration. + */ + // TODO : TC_TN_027 + @LargeTest + public void testThumbnailForVideoDurationLessFileDuration() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp"; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = (mediaVideoItem.getWidth() / 2); + final int outHeight = (mediaVideoItem.getHeight() / 2); + final long atTime = mediaVideoItem.getDuration() - 2000; + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail (outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + + } + + /** + *To test ThumbnailList TC_TN_028 for file which has video part corrupted + */ + // TODO : TC_TN_028 + @LargeTest + public void testThumbnailWithCorruptedVideoPart() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "corrupted_H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4"; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + + try { + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = mediaVideoItem.getWidth(); + final int outHeight = mediaVideoItem.getHeight() * 2; + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail + (outWidth, outHeight, mediaVideoItem.getDuration()/2); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Corrupted File cannot be read", flagForException); + } + + /** + * Check the thumbnail / frame list extraction for Height as Negative Value + */ + // TODO : TC_TN_029 + @LargeTest + public void testThumbnailWithNegativeHeight() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp"; + final int tnCount = 10; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + try { + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = (mediaVideoItem.getWidth() / 2); + final int outHeight = -1; + final long thumbNailStartTime = + mediaVideoItem.getBoundaryBeginTime()/2; + final long thumbNailEndTime = mediaVideoItem.getBoundaryEndTime(); + mediaVideoItem.getThumbnailList(outWidth, outHeight, + thumbNailStartTime, thumbNailEndTime, tnCount); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Thumbnail List with negative Height", flagForException); + } + + /** + * Check the thumbnail for Height as Zero + */ + // TODO : TC_TN_030 + @LargeTest + public void testThumbnailWithHeightAsZero() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp"; + final int atTime = 100; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + try { + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = (mediaVideoItem.getWidth() / 2); + final int outHeight = -1; + mediaVideoItem.getThumbnail(outWidth, outHeight, atTime); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Thumbnail List with Zero Height", flagForException); + } + + /** + * Check the thumbnail for Height = 10 + */ + // TODO : TC_TN_031 + @LargeTest + public void testThumbnailWithHeight() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp"; + final int atTime = 1000; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = (mediaVideoItem.getWidth() / 2); + final int outHeight = 10; + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail (outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + + /** + * Check the thumbnail / frame list extraction for Width as Negative Value + */ + // TODO : TC_TN_032 + @LargeTest + public void testThumbnailWithNegativeWidth() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp"; + final int tnCount = 10; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + try { + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = -1; + final int outHeight = mediaVideoItem.getHeight(); + final long thumbNailStartTime = + mediaVideoItem.getBoundaryBeginTime()/2; + final long thumbNailEndTime = mediaVideoItem.getBoundaryEndTime(); + mediaVideoItem.getThumbnailList(outWidth, outHeight, thumbNailStartTime, + thumbNailEndTime, tnCount); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Thumbnail List with negative Height", flagForException); + } + + /** + * Check the thumbnail / frame list extraction for Width zero + */ + // TODO : TC_TN_033 + @LargeTest + public void testThumbnailWithWidthAsZero() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp"; + final int atTime = 1000; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + try { + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = 0; + final int outHeight = mediaVideoItem.getHeight() / 2; + mediaVideoItem.getThumbnail(outWidth, outHeight, atTime); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Thumbnail List with Zero Width", flagForException); + } + + /** + * Check the thumbnail for Width = 10 + */ + // TODO : TC_TN_034 + @LargeTest + public void testThumbnailWithWidth() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp"; + final int atTime = 1000; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = 10; + final int outHeight = mediaVideoItem.getHeight(); + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail (outWidth, + outHeight, atTime); + validateThumbnail(thumbNailBmp, outWidth, outHeight); + } + + /** + * To test thumbnail / frame extraction on MPEG4 (time beyond file duration). + */ + // TODO : TC_TN_035 + @LargeTest + public void testThumbnailMPEG4withMorethanFileDuration() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp"; + boolean flagForException = false; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename, renderingMode); + final int outWidth = mediaVideoItem.getWidth()/2; + final int outHeight = mediaVideoItem.getHeight()/2; + final long atTime = mediaVideoItem.getDuration() + 100; + try{ + final Bitmap thumbNailBmp = mediaVideoItem.getThumbnail (outWidth, + outHeight, atTime); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Thumbnail duration is more than file duration", + flagForException); + } +} diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java index 64ffa4ea8b9f..d22025cadee2 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java @@ -483,29 +483,4 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra CodecTest.prepareAsyncCallback(MediaNames.STREAM_H264_480_360_1411k, true); assertTrue("StreamH264PrepareAsyncCallback", onPrepareSuccess); } - - //Provide a tool to play all kinds of media files in a directory - @Suppress - @LargeTest - public void testMediaSamples() throws Exception { - // load directory files - boolean onCompleteSuccess = false; - File dir = new File(MediaNames.MEDIA_SAMPLE_POOL); - String[] children = dir.list(); - if (children == null) { - Log.v("MediaPlayerApiTest:testMediaSamples", "dir is empty"); - return; - } else { - for (int i = 0; i < children.length; i++) { - //Get filename of directory - String filename = children[i]; - Log.v("MediaPlayerApiTest", - "testMediaSamples: file to be played: " - + dir + "/" + filename); - onCompleteSuccess = - CodecTest.playMediaSamples(dir + "/" + filename); - assertTrue("testMediaSamples", onCompleteSuccess); - } - } - } } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java new file mode 100755 index 000000000000..3efa5b259d26 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java @@ -0,0 +1,734 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.functional; + +import java.io.File; +import java.io.IOException; + +import android.media.videoeditor.AudioTrack; +import android.media.videoeditor.MediaImageItem; +import android.media.videoeditor.MediaItem; +import android.media.videoeditor.MediaProperties; +import android.media.videoeditor.MediaVideoItem; +import android.media.videoeditor.VideoEditor; +import android.os.Environment; +import android.test.ActivityInstrumentationTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.VideoEditorHelper; + +public class MediaPropertiesTest extends + ActivityInstrumentationTestCase<MediaFrameworkTest> { + private final String TAG = "MediaPropertiesTest"; + + private final String PROJECT_LOCATION = VideoEditorHelper.PROJECT_LOCATION_COMMON; + + private final String INPUT_FILE_PATH = VideoEditorHelper.INPUT_FILE_PATH_COMMON; + + private VideoEditor mVideoEditor; + + private VideoEditorHelper mVideoEditorHelper; + + public MediaPropertiesTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + // setup for each test case. + super.setUp(); + mVideoEditorHelper = new VideoEditorHelper(); + // Create a random String which will be used as project path, + // where all project related files will be stored. + final String projectPath = mVideoEditorHelper. + createRandomFile(PROJECT_LOCATION); + mVideoEditor = mVideoEditorHelper.createVideoEditor(projectPath); + } + + @Override + protected void tearDown() throws Exception { + mVideoEditorHelper.destroyVideoEditor(mVideoEditor); + // Clean the directory created as project path + mVideoEditorHelper.deleteProject(new File(mVideoEditor.getPath())); + System.gc(); + super.tearDown(); + } + + protected void validateVideoProperties(int aspectRatio, int fileType, + int videoCodecType, int duration, int videoBitrate, int fps, + int videoProfile, int width, int height, int audioCodecType, + int audioSamplingFrequency, int audioChannel, int audioBitrate, + MediaVideoItem mvi) throws Exception { + assertEquals("Aspect Ratio Mismatch", aspectRatio, mvi.getAspectRatio()); + assertEquals("File Type Mismatch", fileType, mvi.getFileType()); + assertEquals("VideoCodec Mismatch", videoCodecType, mvi.getVideoType()); + + assertTrue("Video duration Mismatch", mVideoEditorHelper.checkRange ( + duration, mvi.getDuration(), 10)); + assertEquals("Video Profile " + mvi.getVideoProfile(), videoProfile, + mvi.getVideoProfile()); + assertEquals("Video height " + mvi.getHeight(), height, mvi.getHeight()); + assertEquals("Video width " + mvi.getWidth(), width, mvi.getWidth()); + /** Check FPS with 10% range */ + assertTrue("fps Mismatch" + mvi.getFps(), + mVideoEditorHelper.checkRange(fps, mvi.getFps(), 10)); + + assertEquals("AudioType Mismatch ", audioCodecType, mvi.getAudioType()); + assertEquals("Audio Sampling " + mvi.getAudioSamplingFrequency(), + audioSamplingFrequency, mvi.getAudioSamplingFrequency()); + assertEquals("Audio Channels " + mvi.getAudioChannels(), audioChannel, + mvi.getAudioChannels()); + } + + protected void validateAudioProperties(int audioCodecType, int duration, + int audioSamplingFrequency, int audioChannel, int audioBitrate, + AudioTrack aT) throws Exception { + assertEquals("AudioType Mismatch ", audioCodecType, aT.getAudioType()); + assertTrue("Video duration Mismatch", mVideoEditorHelper.checkRange ( + duration, aT.getDuration(), 10)); + assertEquals("Audio Sampling " + aT.getAudioSamplingFrequency(), + audioSamplingFrequency, aT.getAudioSamplingFrequency()); + assertEquals("Audio Channels " + aT.getAudioChannels(), audioChannel, + aT.getAudioChannels()); + } + + protected void validateImageProperties(int aspectRatio, int fileType, + int width, int height, MediaImageItem mii) + throws Exception { + assertEquals("Aspect Ratio Mismatch", aspectRatio, mii.getAspectRatio()); + assertEquals("File Type Mismatch", fileType, mii.getFileType()); + assertEquals("Image height " + mii.getHeight(), height, mii.getHeight()); + assertEquals("Image width " + mii.getWidth(), width, mii.getWidth()); + } + + + /** + *To test Media Properties for file MPEG4 854 x 480 + */ + // TODO : Remove TC_MP_001 + @LargeTest + public void testPropertiesMPEG4854_480() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_854x480_15fps_256kbps_AACLC_16khz_48kbps_s_0_26.mp4"; + final int aspectRatio = MediaProperties.ASPECT_RATIO_16_9; + final int fileType = MediaProperties.FILE_MP4; + final int videoCodecType = MediaProperties.VCODEC_MPEG4; + final int duration = 26933; + final int videoBitrate = 319000; + final int audioBitrate = 48000; + final int fps = 15; + final int audioCodecType = MediaProperties.ACODEC_AAC_LC; + final int audioSamplingFrequency = 16000; + final int audioChannel = 2; + final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1; + final int width = 854; + final int height = MediaProperties.HEIGHT_480; + + final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", videoItemFilename, + MediaItem.RENDERING_MODE_BLACK_BORDER); + + validateVideoProperties(aspectRatio, fileType, videoCodecType, duration, + videoBitrate, fps, videoProfile, width, height, audioCodecType, + audioSamplingFrequency, audioChannel, audioBitrate, mvi); + } + + + /** + *To test Media Properties for file MPEG4 WVGA + */ + // TODO : Remove TC_MP_002 + @LargeTest + public void testPropertiesMPEGWVGA() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4"; + final int aspectRatio = MediaProperties.ASPECT_RATIO_5_3; + final int fileType = MediaProperties.FILE_MP4; + final int videoCodecType = MediaProperties.VCODEC_MPEG4; + final int duration = 26933; + final int videoBitrate = 384000; + final int audioBitrate = 12800; + final int fps = 15; + final int audioCodecType = MediaProperties.ACODEC_AMRNB; + final int audioSamplingFrequency = 8000; + final int audioChannel = 1; + final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1; + final int width = 800; + final int height = MediaProperties.HEIGHT_480; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", videoItemFilename, renderingMode); + + validateVideoProperties(aspectRatio, fileType, videoCodecType, duration, + videoBitrate, fps, videoProfile, width, height, audioCodecType, + audioSamplingFrequency, audioChannel, audioBitrate, mvi); + } + + /** + *To test media properties for MPEG4 720x480 (NTSC) + AAC file. + */ + // TODO : Remove TC_MP_003 + @LargeTest + public void testPropertiesMPEGNTSC() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_161kbps_s_0_26.mp4"; + final int aspectRatio = MediaProperties.ASPECT_RATIO_3_2; + final int fileType = MediaProperties.FILE_MP4; + final int videoCodecType = MediaProperties.VCODEC_MPEG4; + final int duration = 26866; + final int videoBitrate = 403000; + final int audioBitrate = 160000; + final int fps = 30; + final int audioCodecType = MediaProperties.ACODEC_AAC_LC; + final int audioSamplingFrequency = 48000; + final int audioChannel = 2; + final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1; + final int width = 720; + final int height = MediaProperties.HEIGHT_480; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", videoItemFilename, renderingMode); + + validateVideoProperties(aspectRatio, fileType, videoCodecType, duration, + videoBitrate, fps, videoProfile, width, height, audioCodecType, + audioSamplingFrequency, audioChannel, audioBitrate, mvi); + } + + /** + *To test Media Properties for file MPEG4 VGA + */ + // TODO : Remove TC_MP_004 + @LargeTest + public void testPropertiesMPEGVGA() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_640x480_15fps_512kbps_AACLC_48khz_132kbps_s_0_26.mp4"; + final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3; + final int fileType = MediaProperties.FILE_MP4; + final int videoCodecType = MediaProperties.VCODEC_MPEG4; + final int duration = 26933; + final int videoBitrate = 533000; + final int audioBitrate = 128000; + final int fps = 15; + final int audioCodecType = MediaProperties.ACODEC_AAC_LC; + final int audioSamplingFrequency = 48000; + final int audioChannel = 2; + final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1; + final int width = 640; + final int height = MediaProperties.HEIGHT_480; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", videoItemFilename, renderingMode); + + validateVideoProperties(aspectRatio, fileType, videoCodecType, duration, + videoBitrate, fps, videoProfile, width, height, audioCodecType, + audioSamplingFrequency, audioChannel, audioBitrate, mvi); + } + + /** + *To test Media Properties for file MPEG4 QCIF + */ + // TODO : Remove TC_MP_005 + @LargeTest + public void testPropertiesMPEGQCIF() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG4_SP_176x144_12fps_92kbps_AMRNB_8KHz_12.2kbps_m_0_27.3gp"; + final int aspectRatio = MediaProperties.ASPECT_RATIO_11_9; + final int fileType = MediaProperties.FILE_3GP; + final int videoCodecType = MediaProperties.VCODEC_MPEG4; + final int duration = 27000; + final int videoBitrate = 384000; + final int audioBitrate = 12200; + final int fps = 12; + final int audioCodecType = MediaProperties.ACODEC_AMRNB; + final int audioSamplingFrequency = 8000; + final int audioChannel = 1; + final int videoProfile = MediaProperties.MPEG4_SP_LEVEL_1; + final int width = 176; + final int height = MediaProperties.HEIGHT_144; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", videoItemFilename, renderingMode); + + validateVideoProperties(aspectRatio, fileType, videoCodecType, duration, + videoBitrate, fps, videoProfile, width, height, audioCodecType, + audioSamplingFrequency, audioChannel, audioBitrate, mvi); + } + + /** + *To To test media properties for H263 176x144 (QCIF) + AAC (mono) file. + */ + // TODO : Remove TC_MP_006 + @LargeTest + public void testPropertiesH263QCIF() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_16kHz_32kbps_m_0_26.3gp"; + final int aspectRatio = MediaProperties.ASPECT_RATIO_11_9; + final int fileType = MediaProperties.FILE_3GP; + final int videoCodecType = MediaProperties.VCODEC_H263; + final int duration = 26933; + final int videoBitrate = 384000; + final int audioBitrate = 64000; + final int fps = 15; + final int audioCodecType = MediaProperties.ACODEC_AAC_LC; + final int audioSamplingFrequency = 16000; + final int audioChannel = 1; + final int videoProfile = MediaProperties.H263_PROFILE_0_LEVEL_10; + final int width = 176; + final int height = MediaProperties.HEIGHT_144; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", videoItemFilename, renderingMode); + + validateVideoProperties(aspectRatio, fileType, videoCodecType, duration, + videoBitrate, fps, videoProfile, width, height, audioCodecType, + audioSamplingFrequency, audioChannel, audioBitrate, mvi); + } + + /** + *To test Media Properties for file H264 VGA + */ + // TODO : Remove TC_MP_007 + @LargeTest + public void testPropertiesH264VGA() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp"; + final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3; + final int fileType = MediaProperties.FILE_3GP; + final int videoCodecType = MediaProperties.VCODEC_H264BP; + final int duration = 77600; + final int videoBitrate = 745000; + final int audioBitrate = 64000; + final int fps = 15; + final int audioCodecType = MediaProperties.ACODEC_AAC_LC; + final int audioSamplingFrequency = 48000; + final int audioChannel = 2; + final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3; + final int width = 640; + final int height = MediaProperties.HEIGHT_480; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", videoItemFilename, renderingMode); + + validateVideoProperties(aspectRatio, fileType, videoCodecType, duration, + videoBitrate, fps, videoProfile, width, height, audioCodecType, + audioSamplingFrequency, audioChannel, audioBitrate, mvi); + } + + /** + *To test Media Properties for file H264 NTSC + */ + // TODO : Remove TC_MP_008 + @LargeTest + public void testPropertiesH264NTSC() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_720x480_25fps_256kbps_AMRNB_8khz_12.2kbps_m_0_26.mp4"; + final int aspectRatio = MediaProperties.ASPECT_RATIO_3_2; + final int fileType = MediaProperties.FILE_MP4; + final int videoCodecType = MediaProperties.VCODEC_H264BP; + final int duration = 26880; + final int videoBitrate = 244000; + final int audioBitrate = 12200; + final int fps = 25; + final int audioCodecType = MediaProperties.ACODEC_AMRNB; + final int audioSamplingFrequency = 8000; + final int audioChannel = 1; + final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3; + final int width = 720; + final int height = MediaProperties.HEIGHT_480; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", videoItemFilename, renderingMode); + + validateVideoProperties(aspectRatio, fileType, videoCodecType, duration, + videoBitrate, fps, videoProfile, width, height, audioCodecType, + audioSamplingFrequency, audioChannel, audioBitrate, mvi); + } + + /** + *To test media properties for H264 800x480 (WVGA) + AAC file. + */ + // TODO : Remove TC_MP_009 + @LargeTest + public void testPropertiesH264WVGA() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4"; + final int aspectRatio = MediaProperties.ASPECT_RATIO_5_3; + final int fileType = MediaProperties.FILE_MP4; + final int videoCodecType = MediaProperties.VCODEC_H264BP; + final int duration = 77466; + final int videoBitrate = 528000; + final int audioBitrate = 38000; + final int fps = 15; + final int audioCodecType = MediaProperties.ACODEC_AAC_LC; + final int audioSamplingFrequency = 24000; + final int audioChannel = 2; + final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3; + final int width = 800; + final int height = MediaProperties.HEIGHT_480; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", videoItemFilename, renderingMode); + + validateVideoProperties(aspectRatio, fileType, videoCodecType, duration, + videoBitrate, fps, videoProfile, width, height, audioCodecType, + audioSamplingFrequency, audioChannel, audioBitrate, mvi); + } + + /** + *To test Media Properties for file H264 HD1280 + */ + // TODO : Remove TC_MP_010 + @LargeTest + public void testPropertiesH264HD1280() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_1280x720_15fps_512kbps_AACLC_16khz_48kbps_s_1_17.mp4"; + final int aspectRatio = MediaProperties.ASPECT_RATIO_16_9; + final int fileType = MediaProperties.FILE_MP4; + final int videoCodecType = MediaProperties.VCODEC_H264BP; + final int duration = 77600; + final int videoBitrate = 606000; + final int audioBitrate = 48000; + final int fps = 15; + final int audioCodecType = MediaProperties.ACODEC_AAC_LC; + final int audioSamplingFrequency = 16000; + final int audioChannel = 2; + final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3; + final int width = 1280; + final int height = MediaProperties.HEIGHT_720; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", videoItemFilename, renderingMode); + + validateVideoProperties(aspectRatio, fileType, videoCodecType, duration, + videoBitrate, fps, videoProfile, width, height, audioCodecType, + audioSamplingFrequency, audioChannel, audioBitrate, mvi); + } + + /** + *To test media properties for H264 1080x720 + AAC file + */ + // TODO : Remove TC_MP_011 + @LargeTest + public void testPropertiesH264HD1080WithAudio() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_1080x720_30fps_12Mbps_AACLC_44.1khz_64kbps_s_1_17.mp4"; + final int aspectRatio = MediaProperties.ASPECT_RATIO_3_2; + final int fileType = MediaProperties.FILE_MP4; + final int videoCodecType = MediaProperties.VCODEC_H264BP; + final int duration = 77500; + final int videoBitrate = 1190000; + final int audioBitrate = 64000; + final int fps = 10; + final int audioCodecType = MediaProperties.ACODEC_AAC_LC; + final int audioSamplingFrequency = 44100; + final int audioChannel = 2; + final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3; + final int width = 1080; + final int height = MediaProperties.HEIGHT_720; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", videoItemFilename, renderingMode); + + validateVideoProperties(aspectRatio, fileType, videoCodecType, duration, + videoBitrate, fps, videoProfile, width, height, audioCodecType, + audioSamplingFrequency, audioChannel, audioBitrate, mvi); + } + + /** + *To test Media Properties for file WMV - Unsupported type + */ + // TODO : Remove TC_MP_012 + @LargeTest + public void testPropertiesWMVFile() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "WMV_V7_640x480_15fps_512Kbps_wma_V9_44khz_48Kbps_s_1_30.wmv"; + boolean flagForException = false; + try { + new MediaVideoItem(mVideoEditor, "m1", videoItemFilename, + MediaItem.RENDERING_MODE_BLACK_BORDER); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Media Properties for a WMV File -- Unsupported file type", + flagForException); + } + + /** + *To test media properties for H.264 Main/Advanced profile. (unsupported profile input) + */ + // TODO : Remove TC_MP_013 + @LargeTest + public void testPropertiesH264MainLineProfile() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_MP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4"; + final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3; + //final int videoCodecType = MediaProperties.VCODEC_H264BP; + final int videoCodecType = MediaProperties.VCODEC_H264MP; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + + try { + final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", videoItemFilename, renderingMode); + assertEquals("VideoCodec Mismatch", videoCodecType, mvi.getVideoType()); + }catch (IllegalArgumentException e){ + flagForException = true; + } + assertTrue("Unsupported Main Profile", flagForException); + } + + /** + *To test Media Properties for non existing file. + */ + // TODO : Remove TC_MP_014 + @LargeTest + public void testPropertiesForNonExsitingFile() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + "abc.3gp"; + boolean flagForException = false; + + try { + new MediaVideoItem(mVideoEditor, "m1", videoItemFilename, + MediaItem.RENDERING_MODE_BLACK_BORDER); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Media Properties for non exsisting file", flagForException); + } + + /** + *To test Media Properties for file H264 HD1080 + */ + // TODO : Remove TC_MP_015 + @LargeTest + public void testPropertiesH264HD1080WithoutAudio() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "H264_BP_1080x720_30fps_800kbps_1_17.mp4"; + final int aspectRatio = MediaProperties.ASPECT_RATIO_3_2; + final int fileType = MediaProperties.FILE_MP4; + final int videoCodecType = MediaProperties.VCODEC_H264BP; + final int duration = 77366; + final int videoBitrate = 859000; + final int audioBitrate = 0; + final int fps = 30; + final int audioCodecType = -1; + final int audioSamplingFrequency = 0; + final int audioChannel = 0; + final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3; + final int width = 1080; + final int height = MediaProperties.HEIGHT_720; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mvi = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", videoItemFilename, renderingMode); + + validateVideoProperties(aspectRatio, fileType, videoCodecType, duration, + videoBitrate, fps, videoProfile, width, height, audioCodecType, + audioSamplingFrequency, audioChannel, audioBitrate, mvi); + } + + /** + *To test Media Properties for Image file of JPEG Type + */ + // TODO : Remove TC_MP_016 + @LargeTest + public void testPropertiesVGAImage() throws Exception { + final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final int imageItemDuration = 10000; + final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3; + final int fileType = MediaProperties.FILE_JPEG; + final int width = 640; + final int height = MediaProperties.HEIGHT_480; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaImageItem mii = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", imageItemFilename, imageItemDuration, + renderingMode); + validateImageProperties(aspectRatio, fileType, width, height, mii); + } + + /** + *To test Media Properties for Image file of PNG Type + */ + // TODO : Remove TC_MP_017 + @LargeTest + public void testPropertiesPNG() throws Exception { + final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.png"; + final int imageItemDuration = 10000; + final int aspectRatio = MediaProperties.ASPECT_RATIO_4_3; + final int fileType = MediaProperties.FILE_PNG; + final int width = 640; + final int height = 480; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaImageItem mii = mVideoEditorHelper.createMediaItem + (mVideoEditor, "m1", imageItemFilename, imageItemDuration, + renderingMode); + validateImageProperties(aspectRatio, fileType, width, height, mii); + } + + /** + *To test Media Properties for file GIF - Unsupported type + */ + // TODO : Remove TC_MP_018 + @LargeTest + public void testPropertiesGIFFile() throws Exception { + + final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.gif"; + final int imageItemDuration = 10000; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + try { + new MediaImageItem(mVideoEditor, "m1", imageItemFilename, + imageItemDuration, renderingMode); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Media Properties for a GIF File -- Unsupported file type", + flagForException); + } + + /** + *To test Media Properties for file Text file named as 3GP + */ + // TODO : Remove TC_MP_019 + @LargeTest + public void testPropertiesofDirtyFile() throws Exception { + + final String videoItemFilename = INPUT_FILE_PATH + + "Text_FileRenamedTo3gp.3gp"; + boolean flagForException = false; + + try { + new MediaVideoItem(mVideoEditor, "m1", videoItemFilename, + MediaItem.RENDERING_MODE_BLACK_BORDER); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Media Properties for a Dirty File ", + flagForException); + } + + /** + *To test Media Properties for file name as NULL + */ + // TODO : Remove TC_MP_020 + @LargeTest + public void testPropertieNULLFile() throws Exception { + final String videoItemFilename = null; + boolean flagForException = false; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + try { + new MediaVideoItem(mVideoEditor, "m1", videoItemFilename, + renderingMode); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Media Properties for NULL File ", + flagForException); + } + + /** + *To test Media Properties for file which is of type MPEG2 + */ + // TODO : Remove TC_MP_021 + @LargeTest + public void testPropertiesMPEG2File() throws Exception { + final String videoItemFilename = INPUT_FILE_PATH + + "MPEG2_640x480_30fps_192kbps_1_5.mp4"; + boolean flagForException = false; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + try { + new MediaVideoItem(mVideoEditor, "m1", videoItemFilename, + renderingMode); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Media Properties for a MPEG2 File --Unsupported file type", + flagForException); + } + + /** + *To test Media Properties TC_MP_023 for file without Video only Audio + */ + // TODO : Remove TC_MP_023 + @LargeTest + public void testProperties3GPWithoutVideoMediaItem() throws Exception { + final String audioFilename = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + boolean flagForException = false; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + try { + new MediaVideoItem(mVideoEditor, "m1", audioFilename, + renderingMode); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Exception in Creaing Media Video item object without video", + flagForException); + } + + /** + *To test media properties for Audio Track file. (No Video, AAC Audio) + */ + // TODO : Remove TC_MP_024 + @LargeTest + public void testProperties3GPWithoutVideoAudioTrack() throws Exception { + + final String audioFilename = INPUT_FILE_PATH + + "AACLC_44.1kHz_256kbps_s_1_17.mp4"; + final int duration = 77554; + final int audioBitrate = 384000; + final int audioCodecType = MediaProperties.ACODEC_AAC_LC; + final int audioSamplingFrequency = 44100; + final int audioChannel = 2; + + final AudioTrack audioTrack = mVideoEditorHelper.createAudio + (mVideoEditor, "a1", audioFilename); + + validateAudioProperties(audioCodecType, duration, audioSamplingFrequency, + audioChannel, audioBitrate, audioTrack); + } + + /** + *To test media properties for Audio Track file. MP3 file + */ + // TODO : Remove TC_MP_025 + @LargeTest + public void testPropertiesMP3AudioTrack() throws Exception { + + final String audioFilename = INPUT_FILE_PATH + + "MP3_48KHz_128kbps_s_1_17.mp3"; + final int duration = 77640; + final int audioBitrate = 128000; + final int audioCodecType = MediaProperties.ACODEC_MP3; + final int audioSamplingFrequency = 48000; + final int audioChannel = 2; + + final AudioTrack audioTrack = mVideoEditorHelper.createAudio + (mVideoEditor, "a1", audioFilename); + + validateAudioProperties(audioCodecType, duration, audioSamplingFrequency, + audioChannel, audioBitrate, audioTrack); + } +} diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorAPITest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorAPITest.java new file mode 100644 index 000000000000..0dadaa56ca8a --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorAPITest.java @@ -0,0 +1,2786 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.functional; + +import java.io.File; +import java.util.List; + +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.media.videoeditor.AudioTrack; +import android.media.videoeditor.EffectColor; +import android.media.videoeditor.EffectKenBurns; +import android.media.videoeditor.ExtractAudioWaveformProgressListener; +import android.media.videoeditor.MediaImageItem; +import android.media.videoeditor.MediaItem; +import android.media.videoeditor.MediaProperties; +import android.media.videoeditor.MediaVideoItem; +import android.media.videoeditor.OverlayFrame; +import android.media.videoeditor.Transition; +import android.media.videoeditor.TransitionAlpha; +import android.media.videoeditor.TransitionCrossfade; +import android.media.videoeditor.TransitionFadeBlack; +import android.media.videoeditor.TransitionSliding; +import android.media.videoeditor.VideoEditor; +import android.os.Environment; +import android.test.ActivityInstrumentationTestCase; +import android.media.videoeditor.VideoEditor.MediaProcessingProgressListener; + +import android.util.Log; +import java.lang.annotation.Annotation; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import android.test.suitebuilder.annotation.LargeTest; +import com.android.mediaframeworktest.VideoEditorHelper; + +public class VideoEditorAPITest extends + ActivityInstrumentationTestCase<MediaFrameworkTest> { + private final String TAG = "VideoEditorTest"; + + private final String PROJECT_LOCATION = VideoEditorHelper.PROJECT_LOCATION_COMMON; + + private final String INPUT_FILE_PATH = VideoEditorHelper.INPUT_FILE_PATH_COMMON; + + private final String PROJECT_CLASS_NAME = + "android.media.videoeditor.VideoEditorImpl"; + private VideoEditor mVideoEditor; + private VideoEditorHelper mVideoEditorHelper; + + public VideoEditorAPITest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + // setup for each test case. + super.setUp(); + mVideoEditorHelper = new VideoEditorHelper(); + // Create a random String which will be used as project path, where all + // project related files will be stored. + final String projectPath = mVideoEditorHelper. + createRandomFile(PROJECT_LOCATION); + mVideoEditor = mVideoEditorHelper.createVideoEditor(projectPath); + } + + @Override + protected void tearDown() throws Exception { + mVideoEditorHelper.destroyVideoEditor(mVideoEditor); + // Clean the directory created as project path + mVideoEditorHelper.deleteProject(new File(mVideoEditor.getPath())); + System.gc(); + super.tearDown(); + } + + /** + * To Test Creation of Media Video Item. + */ + // TODO : remove TC_API_001 + @LargeTest + public void testMediaVideoItem() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final int videoItemRenderingMode = + MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName, videoItemRenderingMode); + + assertTrue("Media Video ID", + mediaVideoItem1.getId().equals("mediaVideoItem1")); + assertTrue("Media Video Filename", + mediaVideoItem1.getFilename().equals(videoItemFileName)); + assertEquals("Media Video Rendering Mode", + videoItemRenderingMode, mediaVideoItem1.getRenderingMode()); + assertEquals("Media Video Item Duration", mediaVideoItem1.getDuration(), + mediaVideoItem1.getTimelineDuration()); + assertEquals("Media Video Overlay", 0, + mediaVideoItem1.getAllOverlays().size()); + assertEquals("Media Video Effect", 0, + mediaVideoItem1.getAllEffects().size()); + assertNull("Media Video Begin transition", + mediaVideoItem1.getBeginTransition()); + assertNull("Media Video End transition", + mediaVideoItem1.getEndTransition()); + mediaVideoItem1.setExtractBoundaries(1000,11000); + boolean flagForException = false; + if (mediaVideoItem1.getDuration() != + mediaVideoItem1.getTimelineDuration()) { + flagForException = true; + } + assertTrue("Media Video Item Duration & Timeline are same", + flagForException ); + } + + /** + * To test creation of Media Video Item with Set Extract Boundaries With Get + * the Begin and End Time. + */ + // TODO : remove TC_API_002 + @LargeTest + public void testMediaVideoItemExtractBoundaries() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final int videoItemRenderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName, videoItemRenderingMode); + mVideoEditor.addMediaItem(mediaVideoItem1); + + mediaVideoItem1.setExtractBoundaries(1000, 11000); + assertEquals("Media Item Duration = StoryBoard Duration", + mediaVideoItem1.getTimelineDuration(), mVideoEditor.getDuration()); + try { + mediaVideoItem1.setExtractBoundaries(0, 100000000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Set Extract with Invalid Values endTime > FileDuration", + flagForException); + + flagForException = false; + try { + mediaVideoItem1.setExtractBoundaries(100000000, 11000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Set Extract with Invalid Values startTime > endTime", + flagForException); + + flagForException = false; + try { + mediaVideoItem1.setExtractBoundaries(0, 0); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Set Extract with Invalid Values startTime = endTime", + flagForException); + + mediaVideoItem1.setExtractBoundaries(1000, 10000); + assertTrue("Media Item Duration is still the same", + (mediaVideoItem1.getTimelineDuration() == + (mediaVideoItem1.getBoundaryEndTime()- + mediaVideoItem1.getBoundaryBeginTime())) ? true : false); + + mediaVideoItem1.setExtractBoundaries(1,mediaVideoItem1.getDuration()-1); + assertEquals("Media Item Start Time", 1, + mediaVideoItem1.getBoundaryBeginTime()); + assertEquals("Media Item End Time", (mediaVideoItem1.getDuration() - 1), + mediaVideoItem1.getBoundaryEndTime()); + + mediaVideoItem1.setExtractBoundaries(1, mediaVideoItem1.getDuration()); + assertEquals("Media Item Duration = StoryBoard Duration", + mediaVideoItem1.getTimelineDuration(), mVideoEditor.getDuration()); + + mediaVideoItem1.setExtractBoundaries(0,mediaVideoItem1.getDuration()/2); + assertEquals("Media Item Duration = StoryBoard Duration", + mediaVideoItem1.getTimelineDuration(), mVideoEditor.getDuration()); + + mediaVideoItem1.setExtractBoundaries(0, -1); + assertEquals("Media Item Duration = StoryBoard Duration", + mediaVideoItem1.getTimelineDuration(), mVideoEditor.getDuration()); + } + + /** + * To test creation of Media Video Item with Set and Get rendering Mode + */ + // TODO : remove TC_API_003 + @LargeTest + public void testMediaVideoItemRenderingModes() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final int videoItemRenderingMode= MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName, videoItemRenderingMode); + mVideoEditor.addMediaItem(mediaVideoItem1); + mediaVideoItem1.setRenderingMode(MediaItem.RENDERING_MODE_CROPPING); + assertEquals("MediaVideo Item rendering Mode", + MediaItem.RENDERING_MODE_CROPPING, + mediaVideoItem1.getRenderingMode()); + try { + mediaVideoItem1.setRenderingMode( + MediaItem.RENDERING_MODE_CROPPING + 911); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Media Item Invalid rendering Mode", flagForException); + flagForException = false; + try { + mediaVideoItem1.setRenderingMode( + MediaItem.RENDERING_MODE_BLACK_BORDER - 11); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Media Item Invalid rendering Mode", flagForException); + assertEquals("MediaVideo Item rendering Mode", + MediaItem.RENDERING_MODE_CROPPING, + mediaVideoItem1.getRenderingMode()); + mediaVideoItem1.setRenderingMode(MediaItem.RENDERING_MODE_STRETCH); + assertEquals("MediaVideo Item rendering Mode", + MediaItem.RENDERING_MODE_STRETCH, + mediaVideoItem1.getRenderingMode()); + } + + /** Test Case TC_API_004 is removed */ + + /** + * To Test the Media Video API : Set Audio Volume, Get Audio Volume and Mute + */ + // TODO : remove TC_API_005 + @LargeTest + public void testMediaVideoItemAudioFeatures() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final int videoItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName, videoItemRenderingMode); + mVideoEditor.addMediaItem(mediaVideoItem1); + mediaVideoItem1.setVolume(77); + assertEquals("Updated Volume is 77", 77, mediaVideoItem1.getVolume()); + + mediaVideoItem1.setMute(true); + assertTrue("Audio must be Muted", mediaVideoItem1.isMuted()); + + mediaVideoItem1.setVolume(78); + assertEquals("Updated Volume is 78", 78, mediaVideoItem1.getVolume()); + assertTrue("Audio must be Muted", mediaVideoItem1.isMuted()); + + try { + mediaVideoItem1.setVolume(1000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Invalid Set Volume", flagForException); + + mediaVideoItem1.setMute(false); + assertFalse("Audio must be Un-Muted", mediaVideoItem1.isMuted()); + + mediaVideoItem1.setVolume(0); + assertFalse("Audio must be Un-Muted", mediaVideoItem1.isMuted()); + + flagForException = false; + try { + mediaVideoItem1.setVolume(-1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Invalid Set Volume", flagForException); + + mediaVideoItem1.setVolume(100); + assertEquals("MediaItem Volume", 100, mediaVideoItem1.getVolume()); + try { + mediaVideoItem1.setVolume(101); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Invalid Set Volume", flagForException); + assertEquals("MediaItem Volume", 100, mediaVideoItem1.getVolume()); + } + + /** + * To Test the Media Video API : GetWaveFormData and + * extractAudioWaveFormData + */ + + // TODO : remove TC_API_006 + @LargeTest + public void testMediaVideoItemGetWaveformData() throws Exception { + + final String videoItemFileName = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final int videoItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName, videoItemRenderingMode); + mVideoEditor.addMediaItem(mediaVideoItem1); + + assertNull("WaveForm data", mediaVideoItem1.getWaveformData()); + final int[] progressWaveform = new int[105]; + + mediaVideoItem1.extractAudioWaveform(new + ExtractAudioWaveformProgressListener() { + int i = 0; + public void onProgress(int progress) { + Log.i("WaveformData","progress=" +progress); + progressWaveform[i++] = progress; + } + }); + assertTrue("Progress of WaveForm data", mVideoEditorHelper + .checkProgressCBValues(progressWaveform)); + assertNotNull("WaveForm data", mediaVideoItem1.getWaveformData()); + assertTrue("WaveForm Frame Duration", + (mediaVideoItem1.getWaveformData().getFrameDuration() > 0? + true : false)); + assertTrue("WaveForm Frame Count", + (mediaVideoItem1.getWaveformData().getFramesCount() > 0 ? + true : false)); + assertTrue("WaveForm Gain", + (mediaVideoItem1.getWaveformData().getFrameGains().length > 0 ? + true : false)); + + } + + /** + * To Test the Media Video API : Get Effect, GetAllEffects, remove Effect + */ + + // TODO : remove TC_API_007 + @LargeTest + public void testMediaVideoItemEffect() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final int videoItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem1 = mVideoEditorHelper. + createMediaItem(mVideoEditor, "mediaVideoItem1", videoItemFileName, + videoItemRenderingMode); + mVideoEditor.addMediaItem(mediaVideoItem1); + + assertTrue("Effect List Size", + (mediaVideoItem1.getAllEffects().size() == 0) ? true : false); + assertNull("Effect Item by ID", mediaVideoItem1.getEffect("xyx")); + + final EffectColor effectColor = mVideoEditorHelper.createEffectItem( + mediaVideoItem1, "Effecton MVi1", 0, 4000, EffectColor.TYPE_GRADIENT, + EffectColor.GRAY); + mediaVideoItem1.addEffect(effectColor); + + assertTrue("Effect List Size", (mediaVideoItem1. + getAllEffects().size() == 1) ? true : false); + assertEquals("Effect Item by Valid ID", effectColor, + mediaVideoItem1.getEffect(effectColor.getId())); + assertNull("Effect Item by Invalid ID", + mediaVideoItem1.getEffect("xyz")); + assertNull("Effect Item by Invalid ID", + mediaVideoItem1.removeEffect("effectId")); + assertTrue("Effect List Size", + (mediaVideoItem1.getAllEffects().size() == 1) ? true : false); + assertEquals("Effect Removed", effectColor, + mediaVideoItem1.removeEffect(effectColor.getId())); + assertTrue("Effect List Size", + (mediaVideoItem1.getAllEffects().size() == 0) ? true : false); + assertNull("Effect Item by ID", mediaVideoItem1.getEffect("effectId")); + } + + /** + * To Test the Media Video API : Get Before and after transition + */ + + // TODO : remove TC_API_008 + @LargeTest + public void testMediaVideoItemTransitions() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final int videoItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName, videoItemRenderingMode); + mVideoEditor.addMediaItem(mediaVideoItem1); + assertNull("Begin Transition", mediaVideoItem1.getBeginTransition()); + assertNull("End Transition", mediaVideoItem1.getEndTransition()); + + TransitionFadeBlack transition1 = + mVideoEditorHelper.createTFadeBlack("transition1", mediaVideoItem1, + null, 0, Transition.BEHAVIOR_SPEED_UP); + mVideoEditor.addTransition(transition1); + assertEquals("Begin transition", transition1, + mediaVideoItem1.getEndTransition()); + + assertNotNull("End Transition", mediaVideoItem1.getEndTransition()); + assertTrue(mediaVideoItem1. + getEndTransition().getId().equals(transition1.getId())); + assertTrue(mediaVideoItem1.getEndTransition().getDuration() == + transition1.getDuration() ? true : false); + assertTrue(mediaVideoItem1.getEndTransition().getBehavior() == + transition1.getBehavior() ? true : false); + + TransitionFadeBlack transition2 = mVideoEditorHelper.createTFadeBlack( + "transition2", null,mediaVideoItem1, 0, Transition.BEHAVIOR_LINEAR); + mVideoEditor.addTransition(transition2); + assertNotNull("Begin transition", mediaVideoItem1.getBeginTransition()); + assertEquals("End Transition", transition2, + mediaVideoItem1.getBeginTransition()); + assertTrue(mediaVideoItem1. + getBeginTransition().getId().equals(transition2.getId())); + assertTrue(mediaVideoItem1. getBeginTransition().getDuration() == + transition2.getDuration() ? true : false); + assertTrue(mediaVideoItem1.getBeginTransition().getBehavior() == + transition2.getBehavior() ? true : false); + } + + /** + * To Test the Media Video API : Get All Overlay, Get Overlay and remove Overlay + * + */ + + // TODO : remove TC_API_009 + @LargeTest + public void testMediaVideoItemOverlays() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final String overlayItemFileName = INPUT_FILE_PATH + + "IMG_176x144_Overlay1.png"; + final int videoItemRenderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName, videoItemRenderingMode); + mVideoEditor.addMediaItem(mediaVideoItem1); + + assertTrue("Overlay List Size", + (mediaVideoItem1.getAllOverlays().size() == 0) ? true : false); + assertNull("Overlay Item by ID", mediaVideoItem1.getOverlay("xyz")); + + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayItemFileName, + 176, 144); + final OverlayFrame overlayFrame = mVideoEditorHelper.createOverlay( + mediaVideoItem1, "overlayId", mBitmap, 5000, 5000); + mediaVideoItem1.addOverlay(overlayFrame); + + assertTrue("Overlay List Size", + (mediaVideoItem1.getAllOverlays().size() == 1) ? true : false); + assertEquals("Overlay Item by Valid ID", overlayFrame, mediaVideoItem1 + .getOverlay(overlayFrame.getId())); + assertNull("Overlay Item by Invalid ID", + mediaVideoItem1.getOverlay("xyz")); + assertNull("Overlay Item by Invalid ID", + mediaVideoItem1.removeOverlay("xyz")); + assertTrue("Overlay List Size", + (mediaVideoItem1.getAllOverlays().size() == 1) ? true : false); + assertEquals("Overlay Removed", overlayFrame, + mediaVideoItem1.removeOverlay(overlayFrame.getId())); + assertTrue("Overlay List Size", + (mediaVideoItem1.getAllOverlays().size() == 0) ? true : false); + assertNull("Overlay Item by ID",mediaVideoItem1.getOverlay("effectId")); + } + + /** + * To Test Creation of Media Image Item. + */ + // TODO : remove TC_API_010 + @LargeTest + public void testMediaImageItem() throws Exception { + final String imageItemFileName = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final int imageItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1", + imageItemFileName, 5000, imageItemRenderingMode); + assertTrue("Media Image ID", + mediaImageItem1.getId().equals("mediaImageItem1")); + assertTrue("Media IMage Filename", + mediaImageItem1.getFilename().equals(imageItemFileName)); + assertEquals("Media Image Rendering Mode", + imageItemRenderingMode, mediaImageItem1.getRenderingMode()); + assertEquals("Media Image Item Duration", mediaImageItem1.getDuration(), + mediaImageItem1.getTimelineDuration()); + assertEquals("Media Image Overlay", 0, + mediaImageItem1.getAllOverlays().size()); + assertEquals("Media Image Effect", 0, + mediaImageItem1.getAllEffects().size()); + assertNull("Media Image Begin transition", + mediaImageItem1.getBeginTransition()); + assertNull("Media Image End transition", + mediaImageItem1.getEndTransition()); + assertEquals("Media Image Scaled Height", MediaProperties.HEIGHT_720, + mediaImageItem1.getScaledHeight()); + assertEquals("Media Image Scaled Width", 960, + mediaImageItem1.getScaledWidth()); + assertEquals("Media Image Aspect Ratio", MediaProperties.ASPECT_RATIO_4_3, + mediaImageItem1.getAspectRatio()); + assertNotNull("Media Image Thumbnail", + mediaImageItem1.getThumbnail(960, MediaProperties.HEIGHT_720, 2000)); + } + + /** + * To Test the Media Image API : Get and Set rendering Mode + */ + // TODO : remove TC_API_011 + @LargeTest + public void testMediaImageItemRenderingModes() throws Exception { + final String imageItemFileName = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final int imageItemRenderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1", + imageItemFileName, imageItemRenderingMode, 5000); + mVideoEditor.addMediaItem(mediaImageItem1); + + mediaImageItem1.setRenderingMode(MediaItem.RENDERING_MODE_CROPPING); + assertEquals("MediaVideo Item rendering Mode", + MediaItem.RENDERING_MODE_CROPPING, mediaImageItem1.getRenderingMode()); + try { + mediaImageItem1.setRenderingMode( + MediaItem.RENDERING_MODE_CROPPING + 911); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Media Item Invalid rendering Mode", flagForException); + + flagForException = false; + try { + mediaImageItem1.setRenderingMode( + MediaItem.RENDERING_MODE_BLACK_BORDER - 11); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Media Item Invalid rendering Mode", flagForException); + + assertEquals("MediaVideo Item rendering Mode", + MediaItem.RENDERING_MODE_CROPPING, + mediaImageItem1.getRenderingMode()); + mediaImageItem1.setRenderingMode(MediaItem.RENDERING_MODE_STRETCH); + assertEquals("MediaVideo Item rendering Mode", + MediaItem.RENDERING_MODE_STRETCH, + mediaImageItem1.getRenderingMode()); + } + + /** + * To Test the Media Image API : GetHeight and GetWidth + */ + // TODO : remove TC_API_012 + @LargeTest + public void testMediaImageItemHeightWidth() throws Exception { + final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final int imageItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1", + imageItemFileName, imageItemRenderingMode, 5000); + mVideoEditor.addMediaItem(mediaImageItem1); + + assertEquals("Image Height = Image Scaled Height", + mediaImageItem1.getScaledHeight(), mediaImageItem1.getHeight()); + assertEquals("Image Width = Image Scaled Width", + mediaImageItem1.getScaledWidth(), mediaImageItem1.getWidth()); + } + + + +/** This Test Case can be removed as this is already checked in TC 010 */ + /** + * To Test the Media Image API : Scaled Height and Scaled GetWidth + */ + // TODO : remove TC_API_013 + @LargeTest + public void testMediaImageItemScaledHeightWidth() throws Exception { + final String imageItemFileName = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final int imageItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1", + imageItemFileName, imageItemRenderingMode, 5000); + mVideoEditor.addMediaItem(mediaImageItem1); + + assertNotSame("Image Height = Image Scaled Height", + mediaImageItem1.getScaledHeight(), mediaImageItem1.getHeight()); + assertNotSame("Image Width = Image Scaled Width", + mediaImageItem1.getScaledWidth(), mediaImageItem1.getWidth()); + } + + /** + * To Test the Media Image API : Get Effect, GetAllEffects, remove Effect + */ + + // TODO : remove TC_API_014 + @LargeTest + public void testMediaImageItemEffect() throws Exception { + final String imageItemFileName = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final int imageItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1", + imageItemFileName, 5000, imageItemRenderingMode); + mVideoEditor.addMediaItem(mediaImageItem1); + + assertTrue("Effect List Size", + (mediaImageItem1.getAllEffects().size() == 0) ? true : false); + assertNull("Effect Item by ID", mediaImageItem1.getEffect("xyx")); + + final EffectColor effectColor = + mVideoEditorHelper.createEffectItem(mediaImageItem1, + "Effecton MVi1", 0, 4000, EffectColor.TYPE_GRADIENT, EffectColor.GRAY); + mediaImageItem1.addEffect(effectColor); + + assertTrue("Effect List Size", + (mediaImageItem1.getAllEffects().size() == 1) ? true : false); + assertEquals("Effect Item by Valid ID", + effectColor, mediaImageItem1.getEffect(effectColor.getId())); + assertNull("Effect Item by Invalid ID", + mediaImageItem1.getEffect("xyz")); + assertNull("Effect Item by Invalid ID", + mediaImageItem1.removeEffect("effectId")); + assertTrue("Effect List Size", + (mediaImageItem1.getAllEffects().size() == 1) ? true : false); + assertEquals("Effect Removed", effectColor, + mediaImageItem1.removeEffect(effectColor.getId())); + assertTrue("Effect List Size", + (mediaImageItem1.getAllEffects().size() == 0) ? true : false); + assertNull("Effect Item by ID", mediaImageItem1.getEffect("effectId")); + } + + /** + * To Test the Media Image API : Get Before and after transition + */ + + // TODO : remove TC_API_015 + @LargeTest + public void testMediaImageItemTransitions() throws Exception { + final String imageItemFileName = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final int imageItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1", + imageItemFileName, 5000, imageItemRenderingMode); + mVideoEditor.addMediaItem(mediaImageItem1); + + assertNull("Begin Transition", mediaImageItem1.getBeginTransition()); + assertNull("End Transition", mediaImageItem1.getEndTransition()); + + TransitionFadeBlack transition1 = + mVideoEditorHelper.createTFadeBlack("transition1", mediaImageItem1, + null, 0, Transition.BEHAVIOR_SPEED_UP); + mVideoEditor.addTransition(transition1); + + assertEquals("Begin transition", transition1, + mediaImageItem1.getEndTransition()); + assertNotNull("End Transition", mediaImageItem1.getEndTransition()); + assertTrue(mediaImageItem1.getEndTransition().getId().equals + (transition1.getId())); + assertTrue(mediaImageItem1.getEndTransition().getDuration() == + transition1.getDuration() ? true : false); + assertTrue(mediaImageItem1.getEndTransition().getBehavior() == + transition1.getBehavior() ? true : false); + + TransitionFadeBlack transition2 = mVideoEditorHelper.createTFadeBlack( + "transition2",null, mediaImageItem1, 0, Transition.BEHAVIOR_SPEED_UP); + mVideoEditor.addTransition(transition2); + + assertNotNull("Begin transition", mediaImageItem1.getBeginTransition()); + assertEquals("End Transition", transition2, + mediaImageItem1.getBeginTransition()); + assertTrue(mediaImageItem1.getBeginTransition().getId().equals( + transition2.getId())); + assertTrue(mediaImageItem1.getBeginTransition().getDuration() == + transition2.getDuration() ? true : false); + assertTrue(mediaImageItem1.getBeginTransition().getBehavior() == + transition2.getBehavior() ? true : false); + } + + /** + * To Test the Media Image API : Get All Overlay, Get Overlay and remove + * Overlay + */ + + // TODO : remove TC_API_016 + @LargeTest + public void testMediaImageItemOverlays() throws Exception { + final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final String overlayItemFileName = INPUT_FILE_PATH + + "IMG_640x480_Overlay1.png"; + final int imageItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1", + imageItemFileName, 12000, imageItemRenderingMode); + mVideoEditor.addMediaItem(mediaImageItem1); + + assertTrue("Overlay List Size", + (mediaImageItem1.getAllOverlays().size() == 0) ? true : false); + assertNull("Overlay Item by ID", mediaImageItem1.getOverlay("xyz")); + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayItemFileName, + 640, 480); + final OverlayFrame overlayFrame = + mVideoEditorHelper.createOverlay(mediaImageItem1, "overlayId", + mBitmap, 5000, 5000); + mediaImageItem1.addOverlay(overlayFrame); + + assertTrue("Overlay List Size", + (mediaImageItem1.getAllOverlays().size() == 1) ? true : false); + assertEquals("Overlay Item by Valid ID", overlayFrame, mediaImageItem1 + .getOverlay(overlayFrame.getId())); + assertNull("Overlay Item by Invalid ID", + mediaImageItem1.getOverlay("xyz")); + assertNull("Remove Overlay Item by Invalid ID", + mediaImageItem1.removeOverlay("xyz")); + assertTrue("Overlay List Size", + (mediaImageItem1.getAllOverlays().size() == 1) ? true : false); + assertEquals("Overlay Removed", + overlayFrame, mediaImageItem1.removeOverlay(overlayFrame.getId())); + assertTrue("Overlay List Size", + (mediaImageItem1.getAllOverlays().size() == 0) ? true : false); + assertNull("Overlay Item by ID", + mediaImageItem1.getOverlay("effectId")); + } + + /** + * To test creation of Audio Track + */ + + // TODO : remove TC_API_017 + @LargeTest + public void testAudioTrack() throws Exception { + final String audioFileName = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrack", audioFileName); + mVideoEditor.addAudioTrack(audioTrack); + + assertEquals("Audio Track Item Duration", audioTrack.getDuration(), + audioTrack.getTimelineDuration()); + assertEquals("Audio Track Start Time", 0, audioTrack.getStartTime()); + assertFalse("Audio Track is Looping", audioTrack.isLooping()); + audioTrack.getVolume(); + assertFalse("Audio Track Ducking is Disabled", + audioTrack.isDuckingEnabled()); + assertTrue("Audio Track Filename", + audioTrack.getFilename().equals(audioFileName)); + assertEquals("Audio Ducking Threshold", 0, + audioTrack.getDuckingThreshhold()); + assertFalse("Audio Track Mute", audioTrack.isMuted()); + audioTrack.getDuckedTrackVolume(); + } + + /** + * To test creation of Audio Track with set extract boundaries + */ + // TODO : remove TC_API_018 + @LargeTest + public void testAudioTrackExtractBoundaries() throws Exception { + final String audioFileName = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + boolean flagForException = false; + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrack", audioFileName); + mVideoEditor.addAudioTrack(audioTrack); + + audioTrack.setExtractBoundaries(1000, 5000); + assertEquals("Audio Track Start time", 1000, + audioTrack.getBoundaryBeginTime()); + assertEquals("Audio Track End time", 5000, + audioTrack.getBoundaryEndTime()); + try { + audioTrack.setExtractBoundaries(0, 100000000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Audio Track With endTime > FileDuration", flagForException); + flagForException = false; + try { + audioTrack.setExtractBoundaries(100000000, 5000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Audio Track With startTime > FileDuration", + flagForException); + flagForException = false; + try { + audioTrack.setExtractBoundaries(0, 0); + } catch (IllegalArgumentException e) { + flagForException = true; + } + /* This is under discussion. Hence, checked for False */ + assertFalse("Audio Track With startTime = endTime", flagForException); + assertEquals("Audio Track Start time", 0, + audioTrack.getBoundaryBeginTime()); + assertEquals("Audio Track End time", 0, + audioTrack.getBoundaryEndTime()); + assertEquals("Audio Track Start time",0, + audioTrack.getBoundaryBeginTime()); + assertEquals("Audio Track End time", (audioTrack.getTimelineDuration()), + audioTrack.getBoundaryEndTime()); + audioTrack.setExtractBoundaries(0, audioTrack.getDuration() / 2); + assertEquals("Audio Track Start time",0, + audioTrack.getBoundaryBeginTime()); + assertEquals("Audio Track End time", (audioTrack.getDuration() / 2), + audioTrack.getBoundaryEndTime()); + audioTrack.setExtractBoundaries(1, audioTrack.getDuration() - 1); + assertEquals("Audio Track Start time", 1, + audioTrack.getBoundaryBeginTime()); + assertEquals("Audio Track End time", (audioTrack.getDuration() - 1), + audioTrack.getBoundaryEndTime()); + + flagForException = false; + try { + audioTrack.setExtractBoundaries(0, -1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue ("Audio Track end time < 0",flagForException); + } + + /** + * To test creation of Audio Track with set Start Time and Get Time + */ + // TODO : remove TC_API_019 + @LargeTest + public void testAudioTrackSetGetTime() throws Exception { + final String audioFileName = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + boolean flagForException = false; + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrack", audioFileName); + mVideoEditor.addAudioTrack(audioTrack); + /** set StartTime API is removed and start time is always 0 */ + assertEquals("Audio Track Start Time", 0, audioTrack.getStartTime()); + } + + /** + * To Test the Audio Track API: Enable Ducking + */ + // TODO : remove TC_API_020 + @LargeTest + public void testAudioTrackEnableDucking() throws Exception { + final String audioFileName = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + boolean flagForException = false; + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrack", audioFileName); + mVideoEditor.addAudioTrack(audioTrack); + + assertFalse("Audio Ducking Disabled by default", + audioTrack.isDuckingEnabled()); + audioTrack.enableDucking(45, 70); + assertTrue("Audio Ducking Enabled", audioTrack.isDuckingEnabled()); + assertEquals("Audio Ducking Threshold", 45, + audioTrack.getDuckingThreshhold()); + assertEquals("Audio Ducking Volume", 70, + audioTrack.getDuckedTrackVolume()); + audioTrack.enableDucking(85, 70); + assertEquals("Audio Ducking Threshold", 85, + audioTrack.getDuckingThreshhold()); + assertEquals("Audio Ducking Volume", 70, + audioTrack.getDuckedTrackVolume()); + try { + audioTrack.enableDucking(91, 70); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Enable ducking threshold > 90", flagForException); + flagForException = false; + try { + audioTrack.enableDucking(90, 101); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Enable ducking volume > 100", flagForException); + flagForException = false; + try { + audioTrack.enableDucking(91, 101); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Enable ducking volume > 100 and threshold > 91", + flagForException); + flagForException = false; + try { + audioTrack.enableDucking(-1, 100); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Enable ducking threshold < 0", flagForException); + flagForException = false; + try { + audioTrack.enableDucking(1, -1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Enable ducking lowVolume < 0", flagForException); + flagForException = false; + try { + audioTrack.enableDucking(0, 50); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertFalse("Enable ducking threshold = 0", flagForException); + } + + /** + * To Test the Audio Track API: Looping + */ + // TODO : remove TC_API_021 + @LargeTest + public void testAudioTrackLooping() throws Exception { + final String audioFileName = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrack", audioFileName); + mVideoEditor.addAudioTrack(audioTrack); + assertFalse("Audio Looping", audioTrack.isLooping()); + audioTrack.enableLoop(); + assertTrue("Audio Looping", audioTrack.isLooping()); + audioTrack.disableLoop(); + assertFalse("Audio Looping", audioTrack.isLooping()); + } + + /** + * To Test the Audio Track API:Extract waveform data + */ + // TODO : remove TC_API_022 + + @LargeTest + public void testAudioTrackWaveFormData() throws Exception { + /** Image item is added as dummy as Audio track cannot be added without + * a media item in the story board + */ + final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final int imageItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaImageItem mediaImageItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1", + imageItemFileName, 5000, imageItemRenderingMode); + mVideoEditor.addMediaItem(mediaImageItem); + + final String audioFileName = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrack", audioFileName); + + mVideoEditor.addAudioTrack(audioTrack); + assertNull("WaveForm data", audioTrack.getWaveformData()); + + final int[] progressUpdate = new int[105]; + mVideoEditor.generatePreview(new MediaProcessingProgressListener() { + int i = 0; + public void onProgress(Object item, int action, int progress) { + progressUpdate[i++] = progress; + } + }); + + final int[] progressWaveform = new int[105]; + + audioTrack.extractAudioWaveform( + new ExtractAudioWaveformProgressListener() { + int i = 0; + public void onProgress(int progress) { + Log.i("AudioWaveformData","progress=" +progress); + progressWaveform[i++] = progress; + } + }); + assertTrue("Progress of WaveForm data", mVideoEditorHelper + .checkProgressCBValues(progressWaveform)); + assertNotNull("WaveForm data", audioTrack.getWaveformData()); + assertTrue("WaveForm Frame Duration", + (audioTrack.getWaveformData().getFrameDuration() > 0 ? + true : false)); + assertTrue("WaveForm Frame Count", + (audioTrack.getWaveformData().getFramesCount() > 0 ? true : false)); + assertTrue("WaveForm Gain", + (audioTrack.getWaveformData().getFrameGains().length > 0 ? + true : false)); + } + + /** + * To Test the Audio Track API: Mute + */ + // TODO : remove TC_API_023 + @LargeTest + public void testAudioTrackMute() throws Exception { + final String audioFileName = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrack", audioFileName); + assertFalse("Audio Track UnMute", audioTrack.isMuted()); + audioTrack.setMute(true); + assertTrue("Audio Track Mute", audioTrack.isMuted()); + audioTrack.setMute(false); + assertFalse("Audio Track UnMute", audioTrack.isMuted()); + } + + /** + * To Test the Audio Track API: Get Volume and Set Volume + */ + // TODO : remove TC_API_024 + @LargeTest + public void testAudioTrackGetSetVolume() throws Exception { + final String audioFileName = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + boolean flagForException = false; + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrack", audioFileName); + audioTrack.setVolume(0); + assertEquals("Audio Volume", 0, audioTrack.getVolume()); + assertFalse("Audio Track UnMute", audioTrack.isMuted()); + audioTrack.setVolume(45); + assertEquals("Audio Volume", 45, audioTrack.getVolume()); + assertFalse("Audio Track UnMute", audioTrack.isMuted()); + try { + audioTrack.setVolume(-1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Volume = -1", flagForException); + assertEquals("Audio Volume", 45, audioTrack.getVolume()); + flagForException = false; + try { + audioTrack.setVolume(101); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Volume = 101", flagForException); + flagForException = false; + try { + audioTrack.setVolume(1000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Volume = 10000", flagForException); + assertEquals("Audio Volume", 45, audioTrack.getVolume()); + } + + /** + * To test Effect Color. + */ + // TODO : remove TC_API_025 + @LargeTest + public void testAllEffects() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "MPEG4_SP_640x480_30fps_512Kbps_0_27.mp4"; + boolean flagForException = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final EffectColor effectColor1 = mVideoEditorHelper.createEffectItem( + mediaVideoItem1, "effect1", 1000, 1000, EffectColor.TYPE_COLOR, + EffectColor.PINK); + mediaVideoItem1.addEffect(effectColor1); + + assertEquals("Associated Media Item", mediaVideoItem1, + effectColor1.getMediaItem()); + assertTrue("Effect Id", effectColor1.getId().equals("effect1")); + assertEquals("Effect StartTime", 1000, effectColor1.getStartTime()); + assertEquals("Effect EndTime", 1000, effectColor1.getDuration()); + assertEquals("Effect Type", EffectColor.TYPE_COLOR, + effectColor1.getType()); + assertEquals("Effect Color", EffectColor.PINK, effectColor1.getColor()); + + final EffectColor effectColor2 = mVideoEditorHelper.createEffectItem( + mediaVideoItem1, "effect2", 2000, 1000, EffectColor.TYPE_COLOR, + EffectColor.GRAY); + mediaVideoItem1.addEffect(effectColor2); + + assertEquals("Associated Media Item", mediaVideoItem1, + effectColor2.getMediaItem()); + assertTrue("Effect Id", effectColor2.getId().equals("effect2")); + assertEquals("Effect StartTime", 2000, effectColor2.getStartTime()); + assertEquals("Effect EndTime", 1000, effectColor2.getDuration()); + assertEquals("Effect Type", EffectColor.TYPE_COLOR, + effectColor2.getType()); + assertEquals("Effect Color", EffectColor.GRAY, effectColor2.getColor()); + + final EffectColor effectColor3 = mVideoEditorHelper.createEffectItem( + mediaVideoItem1, "effect3", 3000, 1000, EffectColor.TYPE_COLOR, + EffectColor.GREEN); + mediaVideoItem1.addEffect(effectColor3); + + assertEquals("Associated Media Item", mediaVideoItem1, + effectColor3.getMediaItem()); + assertTrue("Effect Id", effectColor3.getId().equals("effect3")); + assertEquals("Effect StartTime", 3000, effectColor3.getStartTime()); + assertEquals("Effect EndTime", 1000, effectColor3.getDuration()); + assertEquals("Effect Type", EffectColor.TYPE_COLOR, + effectColor3.getType()); + assertEquals("Effect Color", EffectColor.GREEN, effectColor3.getColor()); + + final EffectColor effectColor4 = mVideoEditorHelper.createEffectItem( + mediaVideoItem1, "effect4", 4000, 1000, EffectColor.TYPE_GRADIENT, + EffectColor.PINK); + mediaVideoItem1.addEffect(effectColor4); + + assertEquals("Associated Media Item", mediaVideoItem1, + effectColor4.getMediaItem()); + assertTrue("Effect Id", effectColor4.getId().equals("effect4")); + assertEquals("Effect StartTime", 4000, effectColor4.getStartTime()); + assertEquals("Effect EndTime", 1000, effectColor4.getDuration()); + assertEquals("Effect Type", EffectColor.TYPE_GRADIENT, + effectColor4.getType()); + assertEquals("Effect Color", EffectColor.PINK, effectColor4.getColor()); + + final EffectColor effectColor5 = mVideoEditorHelper.createEffectItem( + mediaVideoItem1, "effect5", 5000, 1000, + EffectColor.TYPE_GRADIENT, EffectColor.GRAY); + mediaVideoItem1.addEffect(effectColor5); + + assertEquals("Associated Media Item", mediaVideoItem1, + effectColor5.getMediaItem()); + assertTrue("Effect Id", effectColor5.getId().equals("effect5")); + assertEquals("Effect StartTime", 5000, effectColor5.getStartTime()); + assertEquals("Effect EndTime", 1000, effectColor5.getDuration()); + assertEquals("Effect Type", EffectColor.TYPE_GRADIENT, + effectColor5.getType()); + assertEquals("Effect Color", EffectColor.GRAY, effectColor5.getColor()); + + final EffectColor effectColor6 = mVideoEditorHelper.createEffectItem( + mediaVideoItem1, "effect6", 6000, 1000, + EffectColor.TYPE_GRADIENT, EffectColor.GREEN); + mediaVideoItem1.addEffect(effectColor6); + + assertEquals("Associated Media Item", mediaVideoItem1, + effectColor6.getMediaItem()); + assertTrue("Effect Id", effectColor6.getId().equals("effect6")); + assertEquals("Effect StartTime", 6000, effectColor6.getStartTime()); + assertEquals("Effect EndTime", 1000, effectColor6.getDuration()); + assertEquals("Effect Type", + EffectColor.TYPE_GRADIENT, effectColor6.getType()); + assertEquals("Effect Color", + EffectColor.GREEN, effectColor6.getColor()); + + final EffectColor effectColor7 = mVideoEditorHelper.createEffectItem( + mediaVideoItem1, "effect7", 7000, 1000, + EffectColor.TYPE_FIFTIES, 0); + mediaVideoItem1.addEffect(effectColor7); + + assertEquals("Associated Media Item", mediaVideoItem1, + effectColor7.getMediaItem()); + assertTrue("Effect Id", effectColor7.getId().equals("effect7")); + assertEquals("Effect StartTime", 7000, effectColor7.getStartTime()); + assertEquals("Effect EndTime", 1000, effectColor7.getDuration()); + assertEquals("Effect Type", EffectColor.TYPE_FIFTIES, + effectColor7.getType()); + assertEquals("Effect Color", -1, effectColor7.getColor()); + + final EffectColor effectColor8 = mVideoEditorHelper.createEffectItem( + mediaVideoItem1, "effect8", 8000, 1000, EffectColor.TYPE_SEPIA, 0); + mediaVideoItem1.addEffect(effectColor8); + + assertEquals("Associated Media Item", mediaVideoItem1, + effectColor8.getMediaItem()); + assertTrue("Effect Id", effectColor8.getId().equals("effect8")); + assertEquals("Effect StartTime", 8000, effectColor8.getStartTime()); + assertEquals("Effect EndTime", 1000, effectColor8.getDuration()); + assertEquals("Effect Type", EffectColor.TYPE_SEPIA, + effectColor8.getType()); + assertEquals("Effect Color", -1, effectColor8.getColor()); + + final EffectColor effectColor9 = mVideoEditorHelper.createEffectItem( + mediaVideoItem1, "effect9", 9000, 1000, + EffectColor.TYPE_NEGATIVE, 0); + mediaVideoItem1.addEffect(effectColor9); + + assertEquals("Associated Media Item", mediaVideoItem1, + effectColor9.getMediaItem()); + assertTrue("Effect Id", effectColor9.getId().equals("effect9")); + assertEquals("Effect StartTime", 9000, effectColor9.getStartTime()); + assertEquals("Effect EndTime", 1000, effectColor9.getDuration()); + assertEquals("Effect Type", EffectColor.TYPE_NEGATIVE, + effectColor9.getType()); + assertEquals("Effect Color", -1, effectColor9.getColor()); + try { + mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effect9", + 9000, 1000, EffectColor.TYPE_COLOR - 1, 0); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Effect type Invalid", flagForException); + flagForException = false; + try { + mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effect9", + 9000, 1000, EffectColor.TYPE_FIFTIES + 1, 0); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Effect type Invalid", flagForException); + try { + mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effect10", + 10000, 1000, EffectColor.TYPE_FIFTIES + + EffectColor.TYPE_GRADIENT, 0); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Effect type Invalid", flagForException); + } + + /** + * To test Effect Color : Set duration and Get Duration + */ + // TODO : remove TC_API_026 + @LargeTest + public void testEffectSetgetDuration() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "MPEG4_SP_640x480_30fps_512Kbps_0_27.mp4"; + final int videoItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName, videoItemRenderingMode); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final EffectColor effectColor1 = mVideoEditorHelper.createEffectItem( + mediaVideoItem1, "effect1", 1000, 2000, + EffectColor.TYPE_COLOR, EffectColor.PINK); + mediaVideoItem1.addEffect(effectColor1); + + effectColor1.setDuration(5000); + assertEquals("Updated Effect Duration", 5000, + effectColor1.getDuration()); + try { + effectColor1.setDuration(mediaVideoItem1.getDuration() + 1000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Effect Color duration > mediaVideoItemDuration", + flagForException); + assertEquals("Effect Duration", 5000, effectColor1.getDuration()); + flagForException = false; + try { + effectColor1.setDuration(-1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Effect Color duration = -1", flagForException); + } + + /** + * To test Effect Color : UNDEFINED color param value + */ + // TODO : remove TC_API_027 + @LargeTest + public void testEffectUndefinedColorParam() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "MPEG4_SP_640x480_30fps_512Kbps_0_27.mp4"; + final int videoItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName, videoItemRenderingMode); + mVideoEditor.addMediaItem(mediaVideoItem1); + try{ + mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effect1", 1000, + 2000, EffectColor.TYPE_COLOR, 0xabcdabcd); + }catch (IllegalArgumentException e){ + flagForException = true; + } + assertTrue("Invalid Effect added",flagForException); + } + + /** + * To test Effect Color : with Invalid StartTime and Duration + */ + // TODO : remove TC_API_028 + @LargeTest + public void testEffectInvalidStartTimeAndDuration() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_32kbps_m_1_17.3gp"; + final int videoItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName, videoItemRenderingMode); + mVideoEditor.addMediaItem(mediaVideoItem1); + + try { + mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effect1", + 400000000, 2000, EffectColor.TYPE_COLOR, EffectColor.GREEN); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Effect with invalid StartTime", flagForException); + + flagForException = false; + try { + mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effect1", -1, + 2000, EffectColor.TYPE_COLOR, EffectColor.GREEN); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Effect with invalid StartTime", flagForException); + + flagForException = false; + try { + mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effect1", + 2000, -1, EffectColor.TYPE_COLOR, EffectColor.GREEN); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Effect with invalid Duration", flagForException); + } + + + /** Test cases 29, 30, 31, 32 and 33 are removed */ + + + /** + * To test Effect : with NULL Media Item + */ + // TODO : remove TC_API_034 + @LargeTest + public void testEffectNullMediaItem() throws Exception { + boolean flagForException = false; + try { + mVideoEditorHelper.createEffectItem(null, "effect1", 1000, 4000, + EffectColor.TYPE_COLOR, EffectColor.GREEN); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Effect with null MediaItem", flagForException); + } + + /** + * To test Effect : KenBurn Effect + */ + // TODO : remove TC_API_035 + @LargeTest + public void testEffectKenBurn() throws Exception { + final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final int imageItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaImageItem mediaImageItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1", + imageItemFileName, 5000, imageItemRenderingMode); + mVideoEditor.addMediaItem(mediaImageItem); + + final Rect startRect = new Rect((mediaImageItem.getHeight() / 3), + (mediaImageItem.getWidth() / 3), (mediaImageItem.getHeight() / 2), + (mediaImageItem.getWidth() / 2)); + final Rect endRect = new Rect(0, 0, mediaImageItem.getWidth(), + mediaImageItem.getHeight()); + + final EffectKenBurns kbEffectOnMediaItem = new EffectKenBurns( + mediaImageItem, "KBOnM2", startRect, endRect, 500, 3000); + + assertNotNull("EffectKenBurns", kbEffectOnMediaItem); + mediaImageItem.addEffect(kbEffectOnMediaItem); + assertEquals("KenBurn Start Rect", startRect, + kbEffectOnMediaItem.getStartRect()); + assertEquals("KenBurn End Rect", endRect, + kbEffectOnMediaItem.getEndRect()); + } + + /** + * To test KenBurnEffect : Set StartRect and EndRect + */ + + // TODO : remove TC_API_036 + @LargeTest + public void testEffectKenBurnSet() throws Exception { + final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final int imageItemRenderingMode =MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + final MediaImageItem mediaImageItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1", + imageItemFileName, 5000, imageItemRenderingMode); + mVideoEditor.addMediaItem(mediaImageItem); + + final Rect startRect = new Rect((mediaImageItem.getHeight() / 3), + (mediaImageItem.getWidth() / 3), (mediaImageItem.getHeight() / 2), + (mediaImageItem.getWidth() / 2)); + final Rect endRect = new Rect(0, 0, mediaImageItem.getWidth(), + mediaImageItem.getHeight()); + + EffectKenBurns kbEffectOnMediaItem=null; + kbEffectOnMediaItem = new EffectKenBurns(mediaImageItem, "KBOnM2", + startRect, endRect, 500, 3000); + + assertNotNull("EffectKenBurns", kbEffectOnMediaItem); + mediaImageItem.addEffect(kbEffectOnMediaItem); + assertEquals("KenBurn Start Rect", startRect, + kbEffectOnMediaItem.getStartRect()); + assertEquals("KenBurn End Rect", endRect, + kbEffectOnMediaItem.getEndRect()); + + final Rect startRect1 = new Rect((mediaImageItem.getHeight() / 5), + (mediaImageItem.getWidth() / 5), (mediaImageItem.getHeight() / 4), + (mediaImageItem.getWidth() / 4)); + final Rect endRect1 = new Rect(10, 10, mediaImageItem.getWidth() / 4, + mediaImageItem.getHeight() / 4); + + /* Added newly to take care of removal set APIs */ + kbEffectOnMediaItem = new EffectKenBurns(mediaImageItem, "KBOnM2_changed", + startRect1, endRect1, 500, 3000); + + assertEquals("KenBurn Start Rect", startRect1, + kbEffectOnMediaItem.getStartRect()); + assertEquals("KenBurn End Rect", endRect1, + kbEffectOnMediaItem.getEndRect()); + + final Rect zeroRect = new Rect(0, 0, 0, 0); + try { + kbEffectOnMediaItem = new EffectKenBurns(mediaImageItem, "KBOnM2_zeroStart", + zeroRect, endRect, 500, 3000); + + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Invalid Start Rect", flagForException); + + flagForException = false; + try { + kbEffectOnMediaItem = new EffectKenBurns(mediaImageItem, "KBOnM2_zeroEnd", + startRect, zeroRect, 500, 3000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Invalid End Rect", flagForException); + } + + /** + * To test Transition : Fade To Black with all behavior + * SPEED_UP/SPEED_DOWN/LINEAR/MIDDLE_SLOW/MIDDLE_FAST + */ + + // TODO : remove TC_API_037 + @LargeTest + public void testTransitionFadeBlack() throws Exception { + + final String videoItemFilename1 = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + final String videoItemFilename2 = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_128kbps_1_35.3gp"; + final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final String videoItemFilename3 = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_192kbps_1_5.mp4"; + final String videoItemFilename4 = INPUT_FILE_PATH + + "MPEG4_SP_640x480_15fps_256kbps_0_30.mp4"; + final String videoItemFilename5 = INPUT_FILE_PATH + + "H263_profile0_176x144_10fps_96kbps_0_25.3gp"; + boolean flagForException = false; + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(0, 15000); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final MediaVideoItem mediaVideoItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem2.setExtractBoundaries(0, 15000); + mVideoEditor.addMediaItem(mediaVideoItem2); + + final TransitionFadeBlack transition1And2 = mVideoEditorHelper + .createTFadeBlack("transition1And2", mediaVideoItem1, + mediaVideoItem2, 3000, Transition.BEHAVIOR_SPEED_UP); + mVideoEditor.addTransition(transition1And2); + + assertTrue("Transition ID", + transition1And2.getId().equals("transition1And2")); + assertEquals("Transtion After Media item", + mediaVideoItem1, transition1And2.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem2, + transition1And2.getBeforeMediaItem()); + assertEquals("Transtion Duration", 3000, transition1And2.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_SPEED_UP, + transition1And2.getBehavior()); + + final MediaImageItem mediaImageItem3 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m3", + imageItemFilename1, 15000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem3); + + final TransitionFadeBlack transition2And3 = + mVideoEditorHelper.createTFadeBlack("transition2And3", mediaVideoItem2, + mediaImageItem3, 1000, Transition.BEHAVIOR_SPEED_DOWN); + mVideoEditor.addTransition(transition2And3); + + assertTrue("Transition ID", + transition2And3.getId().equals("transition2And3")); + assertEquals("Transtion After Media item", mediaVideoItem2, + transition2And3.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaImageItem3, + transition2And3.getBeforeMediaItem()); + assertEquals("Transtion Duration", 1000, transition2And3.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_SPEED_DOWN, + transition2And3.getBehavior()); + + final MediaVideoItem mediaVideoItem4 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m4", + videoItemFilename3, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem4.setExtractBoundaries(0, 20000); + mVideoEditor.addMediaItem(mediaVideoItem4); + + final TransitionFadeBlack transition3And4 = + mVideoEditorHelper.createTFadeBlack("transition3And4", mediaImageItem3, + mediaVideoItem4, 5000, Transition.BEHAVIOR_LINEAR); + mVideoEditor.addTransition(transition3And4); + + assertTrue("Transition ID", + transition3And4.getId().equals("transition3And4")); + assertEquals("Transtion After Media item", mediaImageItem3, + transition3And4.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem4, + transition3And4.getBeforeMediaItem()); + assertEquals("Transtion Duration", 5000, transition3And4.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_LINEAR, + transition3And4.getBehavior()); + + final MediaVideoItem mediaVideoItem5 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m5", + videoItemFilename4, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem5); + + final TransitionFadeBlack transition4And5 = + mVideoEditorHelper.createTFadeBlack("transition4And5", mediaVideoItem4, + mediaVideoItem5, 8000, Transition.BEHAVIOR_MIDDLE_FAST); + mVideoEditor.addTransition(transition4And5); + + assertTrue("Transition ID", + transition4And5.getId().equals("transition4And5")); + assertEquals("Transtion After Media item", mediaVideoItem4, + transition4And5.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem5, + transition4And5.getBeforeMediaItem()); + assertEquals("Transtion Duration", 8000, transition4And5.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_MIDDLE_FAST, + transition4And5.getBehavior()); + + final MediaVideoItem mediaVideoItem6 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m6", + videoItemFilename5, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem6.setExtractBoundaries(0, 20000); + mVideoEditor.addMediaItem(mediaVideoItem6); + + final TransitionFadeBlack transition5And6 = + mVideoEditorHelper.createTFadeBlack("transition5And6", mediaVideoItem5, + mediaVideoItem6, 2000, Transition.BEHAVIOR_MIDDLE_SLOW); + mVideoEditor.addTransition(transition5And6); + + assertTrue("Transition ID", + transition5And6.getId().equals("transition5And6")); + assertEquals("Transtion After Media item", mediaVideoItem5, + transition5And6.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem6, + transition5And6.getBeforeMediaItem()); + assertEquals("Transtion Duration", 2000, transition5And6.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_MIDDLE_SLOW, + transition5And6.getBehavior()); + flagForException = false; + try { + mVideoEditorHelper.createTFadeBlack("transitiond6", mediaVideoItem5, + mediaVideoItem6, 2000, Transition.BEHAVIOR_SPEED_UP - 1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Transition FadeBlack with Invalid behavior", flagForException); + flagForException = false; + try { + mVideoEditorHelper.createTFadeBlack("transitiond6", mediaVideoItem5, + mediaVideoItem6, 2000, Transition.BEHAVIOR_MIDDLE_FAST + 1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Transition FadeBlack with Invalid behavior", flagForException); + } + + /** + * To test Transition : CrossFade with all behavior + * SPEED_UP/SPEED_DOWN/LINEAR/MIDDLE_SLOW/MIDDLE_FAST + */ + + // TODO : remove TC_API_038 + @LargeTest + public void testTransitionCrossFade() throws Exception { + + final String videoItemFilename1 = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + final String videoItemFilename2 = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_128kbps_1_35.3gp"; + final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final String videoItemFilename3 = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_192kbps_1_5.mp4"; + final String videoItemFilename4 = INPUT_FILE_PATH + + "MPEG4_SP_640x480_15fps_256kbps_0_30.mp4"; + final String videoItemFilename5 = INPUT_FILE_PATH + + "H263_profile0_176x144_10fps_96kbps_0_25.3gp"; + boolean flagForException = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(0, 15000); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final MediaVideoItem mediaVideoItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem2.setExtractBoundaries(0, 15000); + mVideoEditor.addMediaItem(mediaVideoItem2); + + final TransitionCrossfade transition1And2 = + mVideoEditorHelper.createTCrossFade("transition1And2", mediaVideoItem1, + mediaVideoItem2, 3000, Transition.BEHAVIOR_SPEED_UP); + mVideoEditor.addTransition(transition1And2); + + assertTrue("Transition ID", + transition1And2.getId().equals("transition1And2")); + assertEquals("Transtion After Media item", mediaVideoItem1, + transition1And2.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem2, + transition1And2.getBeforeMediaItem()); + assertEquals("Transtion Duration", 3000, transition1And2.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_SPEED_UP, + transition1And2.getBehavior()); + + final MediaImageItem mediaImageItem3 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m3", + imageItemFilename1, 15000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem3); + + final TransitionCrossfade transition2And3 = + mVideoEditorHelper.createTCrossFade("transition2And3", mediaVideoItem2, + mediaImageItem3, 1000, Transition.BEHAVIOR_SPEED_DOWN); + mVideoEditor.addTransition(transition2And3); + + assertTrue("Transition ID", + transition2And3.getId().equals("transition2And3")); + assertEquals("Transtion After Media item", mediaVideoItem2, + transition2And3.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaImageItem3, + transition2And3.getBeforeMediaItem()); + assertEquals("Transtion Duration", 1000, transition2And3.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_SPEED_DOWN, + transition2And3.getBehavior()); + + final MediaVideoItem mediaVideoItem4 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m4", + videoItemFilename3, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem4.setExtractBoundaries(0, 18000); + mVideoEditor.addMediaItem(mediaVideoItem4); + + final TransitionCrossfade transition3And4 = + mVideoEditorHelper.createTCrossFade("transition3And4", mediaImageItem3, + mediaVideoItem4, 5000, Transition.BEHAVIOR_LINEAR); + mVideoEditor.addTransition(transition3And4); + + assertTrue("Transition ID", + transition3And4.getId().equals("transition3And4")); + assertEquals("Transtion After Media item", mediaImageItem3, + transition3And4.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem4, + transition3And4.getBeforeMediaItem()); + assertEquals("Transtion Duration", 5000, transition3And4.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_LINEAR, + transition3And4.getBehavior()); + + final MediaVideoItem mediaVideoItem5 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m5", + videoItemFilename4, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem5); + + final TransitionCrossfade transition4And5 = + mVideoEditorHelper.createTCrossFade("transition4And5", mediaVideoItem4, + mediaVideoItem5, 8000, Transition.BEHAVIOR_MIDDLE_FAST); + mVideoEditor.addTransition(transition4And5); + + assertTrue("Transition ID", + transition4And5.getId().equals("transition4And5")); + assertEquals("Transtion After Media item", mediaVideoItem4, + transition4And5.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem5, + transition4And5.getBeforeMediaItem()); + assertEquals("Transtion Duration", 8000, transition4And5.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_MIDDLE_FAST, + transition4And5.getBehavior()); + + final MediaVideoItem mediaVideoItem6 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m6", + videoItemFilename5, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem6.setExtractBoundaries(0, 20000); + mVideoEditor.addMediaItem(mediaVideoItem6); + + final TransitionCrossfade transition5And6 = + mVideoEditorHelper.createTCrossFade("transition5And6", mediaVideoItem5, + mediaVideoItem6, 2000, Transition.BEHAVIOR_MIDDLE_SLOW); + mVideoEditor.addTransition(transition5And6); + + assertTrue("Transition ID", + transition5And6.getId().equals("transition5And6")); + assertEquals("Transtion After Media item", mediaVideoItem5, + transition5And6.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem6, + transition5And6.getBeforeMediaItem()); + assertEquals("Transtion Duration", 2000, transition5And6.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_MIDDLE_SLOW, + transition5And6.getBehavior()); + + flagForException = false; + try { + mVideoEditorHelper.createTCrossFade("transitiond6", mediaVideoItem5, + mediaVideoItem6, 2000, Transition.BEHAVIOR_SPEED_UP - 1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Transition FadeBlack with Invalid behavior", flagForException); + flagForException = false; + try { + mVideoEditorHelper.createTCrossFade("transitiond6", mediaVideoItem5, + mediaVideoItem6, 2000, Transition.BEHAVIOR_MIDDLE_FAST + 1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Transition FadeBlack with Invalid behavior", flagForException); + } + + /** + * To test Transition : Sliding with all behavior + * SPEED_UP/SPEED_DOWN/LINEAR/MIDDLE_SLOW/MIDDLE_FAST and Direction = + * DIRECTION_RIGHT_OUT_LEFT_IN + * ,DIRECTION_LEFT_OUT_RIGHT_IN,DIRECTION_TOP_OUT_BOTTOM_IN + * ,DIRECTION_BOTTOM_OUT_TOP_IN + */ + + // TODO : remove TC_API_039 + @LargeTest + public void testTransitionSliding() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + final String videoItemFilename2 = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_128kbps_1_35.3gp"; + final String imageItemFilename1 = INPUT_FILE_PATH + + "IMG_1600x1200.jpg"; + final String videoItemFilename3 = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_192kbps_1_5.mp4"; + final String videoItemFilename4 = INPUT_FILE_PATH + + "MPEG4_SP_640x480_15fps_256kbps_0_30.mp4"; + final String videoItemFilename5 = INPUT_FILE_PATH + + "H263_profile0_176x144_10fps_96kbps_0_25.3gp"; + boolean flagForException = false; + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(0, 15000); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final MediaVideoItem mediaVideoItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem2.setExtractBoundaries(0, 15000); + mVideoEditor.addMediaItem(mediaVideoItem2); + + final TransitionSliding transition1And2 = + mVideoEditorHelper.createTSliding("transition1And2", mediaVideoItem1, + mediaVideoItem2, 3000, Transition.BEHAVIOR_SPEED_UP, + TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN); + mVideoEditor.addTransition(transition1And2); + + assertTrue("Transition ID", + transition1And2.getId().equals("transition1And2")); + assertEquals("Transtion After Media item", mediaVideoItem1, + transition1And2.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem2, + transition1And2.getBeforeMediaItem()); + assertEquals("Transtion Duration", 3000, transition1And2.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_SPEED_UP, + transition1And2.getBehavior()); + assertEquals("Transition Sliding", + TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN, + transition1And2.getDirection()); + + final MediaImageItem mediaImageItem3 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m3", + imageItemFilename1, 15000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem3); + + final TransitionSliding transition2And3 = + mVideoEditorHelper.createTSliding("transition2And3", + mediaVideoItem2, mediaImageItem3, 1000, + Transition.BEHAVIOR_SPEED_DOWN, + TransitionSliding.DIRECTION_LEFT_OUT_RIGHT_IN); + mVideoEditor.addTransition(transition2And3); + + assertTrue("Transition ID", + transition2And3.getId().equals("transition2And3")); + assertEquals("Transtion After Media item", mediaVideoItem2, + transition2And3.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaImageItem3, + transition2And3.getBeforeMediaItem()); + assertEquals("Transtion Duration", 1000, transition2And3.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_SPEED_DOWN, + transition2And3.getBehavior()); + assertEquals("Transition Sliding", + TransitionSliding.DIRECTION_LEFT_OUT_RIGHT_IN, + transition2And3.getDirection()); + + final MediaVideoItem mediaVideoItem4 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m4", + videoItemFilename3, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem4.setExtractBoundaries(0, 18000); + mVideoEditor.addMediaItem(mediaVideoItem4); + + final TransitionSliding transition3And4 = + mVideoEditorHelper.createTSliding("transition3And4", mediaImageItem3, + mediaVideoItem4, 5000, Transition.BEHAVIOR_LINEAR, + TransitionSliding.DIRECTION_TOP_OUT_BOTTOM_IN); + mVideoEditor.addTransition(transition3And4); + + assertTrue("Transition ID", + transition3And4.getId().equals("transition3And4")); + assertEquals("Transtion After Media item", mediaImageItem3, + transition3And4.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem4, + transition3And4.getBeforeMediaItem()); + assertEquals("Transtion Duration", 5000, transition3And4.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_LINEAR, + transition3And4.getBehavior()); + assertEquals("Transition Sliding", + TransitionSliding.DIRECTION_TOP_OUT_BOTTOM_IN, + transition3And4.getDirection()); + + final MediaVideoItem mediaVideoItem5 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m5", + videoItemFilename4, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem5); + + final TransitionSliding transition4And5 = + mVideoEditorHelper.createTSliding("transition4And5", mediaVideoItem4, + mediaVideoItem5, 8000, Transition.BEHAVIOR_MIDDLE_FAST, + TransitionSliding.DIRECTION_BOTTOM_OUT_TOP_IN); + mVideoEditor.addTransition(transition4And5); + + assertTrue("Transition ID", + transition4And5.getId().equals("transition4And5")); + assertEquals("Transtion After Media item", mediaVideoItem4, + transition4And5.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem5, + transition4And5.getBeforeMediaItem()); + assertEquals("Transtion Duration", 8000, transition4And5.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_MIDDLE_FAST, + transition4And5.getBehavior()); + assertEquals("Transition Sliding", + TransitionSliding.DIRECTION_BOTTOM_OUT_TOP_IN, + transition4And5.getDirection()); + + final MediaVideoItem mediaVideoItem6 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m6", + videoItemFilename5, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem6.setExtractBoundaries(0, 20000); + mVideoEditor.addMediaItem(mediaVideoItem6); + + final TransitionSliding transition5And6 = + mVideoEditorHelper.createTSliding("transition5And6", mediaVideoItem5, + mediaVideoItem6, 2000, Transition.BEHAVIOR_MIDDLE_SLOW, + TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN); + mVideoEditor.addTransition(transition5And6); + + assertTrue("Transition ID", + transition5And6.getId().equals("transition5And6")); + assertEquals("Transtion After Media item", mediaVideoItem5, + transition5And6.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem6, + transition5And6.getBeforeMediaItem()); + assertEquals("Transtion Duration", 2000, transition5And6.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_MIDDLE_SLOW, + transition5And6.getBehavior()); + assertEquals("Transition Sliding", + TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN, + transition5And6.getDirection()); + + flagForException = false; + try { + mVideoEditorHelper.createTSliding("transitiond6", mediaVideoItem5, + mediaVideoItem6, 2000, Transition.BEHAVIOR_MIDDLE_SLOW, + TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN - 1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Transition Sliding with Invalid Direction", flagForException); + flagForException = false; + try { + mVideoEditorHelper.createTSliding("transitiond6", mediaVideoItem5, + mediaVideoItem6, 2000, Transition.BEHAVIOR_MIDDLE_FAST + 1, + TransitionSliding.DIRECTION_BOTTOM_OUT_TOP_IN + 1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Transition Sliding with Invalid behavior", flagForException); + flagForException = false; + try { + mVideoEditorHelper.createTSliding("transitiond6", mediaVideoItem5, + mediaVideoItem6, 2000, Transition.BEHAVIOR_SPEED_UP - 1, + TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Transition Sliding with Invalid behavior", flagForException); + flagForException = false; + try { + mVideoEditorHelper.createTSliding("transitiond6", mediaVideoItem5, + mediaVideoItem6, 2000, Transition.BEHAVIOR_MIDDLE_FAST + 1, + TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Transition Sliding with Invalid behavior", flagForException); + } + + /** + * To test Transition : Alpha with all behavior + * SPEED_UP/SPEED_DOWN/LINEAR/MIDDLE_SLOW/MIDDLE_FAST + */ + + // TODO : remove TC_API_040 + @LargeTest + public void testTransitionAlpha() throws Exception { + + final String videoItemFilename1 = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + final String videoItemFilename2 = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_128kbps_1_35.3gp"; + final String imageItemFilename1 = INPUT_FILE_PATH + + "IMG_640x480.jpg"; + final String videoItemFilename3 = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_192kbps_1_5.mp4"; + final String videoItemFilename4 = INPUT_FILE_PATH + + "MPEG4_SP_640x480_15fps_256kbps_0_30.mp4"; + final String videoItemFilename5 = INPUT_FILE_PATH + + "H263_profile0_176x144_10fps_96kbps_0_25.3gp"; + final String maskFilename = INPUT_FILE_PATH + + "TransitionSpiral_QVGA.jpg"; + boolean flagForException = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(0, 15000); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final MediaVideoItem mediaVideoItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem2.setExtractBoundaries(0, 15000); + mVideoEditor.addMediaItem(mediaVideoItem2); + + final TransitionAlpha transition1And2 = + mVideoEditorHelper.createTAlpha("transition1And2", mediaVideoItem1, + mediaVideoItem2, 3000, Transition.BEHAVIOR_SPEED_UP, maskFilename, + 10, false); + mVideoEditor.addTransition(transition1And2); + + assertTrue("Transition ID", + transition1And2.getId().equals("transition1And2")); + assertEquals("Transtion After Media item", mediaVideoItem1, + transition1And2.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem2, + transition1And2.getBeforeMediaItem()); + assertEquals("Transtion Duration", 3000, transition1And2.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_SPEED_UP, + transition1And2.getBehavior()); + assertTrue("Transition maskFile", + transition1And2.getMaskFilename().equals(maskFilename)); + assertEquals("Transition BlendingPercent", 10, + transition1And2.getBlendingPercent()); + assertFalse("Transition Invert", transition1And2.isInvert()); + + final MediaImageItem mediaImageItem3 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m3", + imageItemFilename1, 15000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem3); + + final TransitionAlpha transition2And3 = + mVideoEditorHelper.createTAlpha("transition2And3", mediaVideoItem2, + mediaImageItem3, 1000, Transition.BEHAVIOR_SPEED_DOWN, + maskFilename, 30, false); + mVideoEditor.addTransition(transition2And3); + + assertTrue("Transition ID", + transition2And3.getId().equals("transition2And3")); + assertEquals("Transtion After Media item", mediaVideoItem2, + transition2And3.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaImageItem3, + transition2And3.getBeforeMediaItem()); + assertEquals("Transtion Duration", 1000, transition2And3.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_SPEED_DOWN, + transition2And3.getBehavior()); + assertTrue("Transition maskFile", + transition2And3.getMaskFilename().equals(maskFilename)); + assertEquals("Transition BlendingPercent", 30, + transition2And3.getBlendingPercent()); + assertFalse("Transition Invert", transition2And3.isInvert()); + + final MediaVideoItem mediaVideoItem4 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m4", + videoItemFilename3, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem4.setExtractBoundaries(0, 18000); + mVideoEditor.addMediaItem(mediaVideoItem4); + + final TransitionAlpha transition3And4 = + mVideoEditorHelper.createTAlpha("transition3And4", mediaImageItem3, + mediaVideoItem4, 5000, Transition.BEHAVIOR_LINEAR, maskFilename, + 50, false); + mVideoEditor.addTransition(transition3And4); + + assertTrue("Transition ID", + transition3And4.getId().equals("transition3And4")); + assertEquals("Transtion After Media item", mediaImageItem3, + transition3And4.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem4, + transition3And4.getBeforeMediaItem()); + assertEquals("Transtion Duration", 5000, transition3And4.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_LINEAR, + transition3And4.getBehavior()); + assertTrue("Transition maskFile", + transition3And4.getMaskFilename().equals(maskFilename)); + assertEquals("Transition BlendingPercent", 50, + transition3And4.getBlendingPercent()); + assertFalse("Transition Invert", transition3And4.isInvert()); + + final MediaVideoItem mediaVideoItem5 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m5", + videoItemFilename4, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem5); + + final TransitionAlpha transition4And5 = + mVideoEditorHelper.createTAlpha("transition4And5", mediaVideoItem4, + mediaVideoItem5, 8000, Transition.BEHAVIOR_MIDDLE_FAST, + maskFilename, 70, true); + mVideoEditor.addTransition(transition4And5); + + assertTrue("Transition ID", + transition4And5.getId().equals("transition4And5")); + assertEquals("Transtion After Media item", mediaVideoItem4, + transition4And5.getAfterMediaItem()); + assertEquals("Transtion Before Media item", mediaVideoItem5, + transition4And5.getBeforeMediaItem()); + assertEquals("Transtion Duration", 8000, transition4And5.getDuration()); + assertEquals("Transtion Behavior", Transition.BEHAVIOR_MIDDLE_FAST, + transition4And5.getBehavior()); + assertTrue("Transition maskFile", + transition4And5.getMaskFilename().equals(maskFilename)); + assertEquals("Transition BlendingPercent", 70, + transition4And5.getBlendingPercent()); + assertTrue("Transition Invert", transition4And5.isInvert()); + + final MediaVideoItem mediaVideoItem6 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m6", + videoItemFilename5, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem6.setExtractBoundaries(0, 20000); + mVideoEditor.addMediaItem(mediaVideoItem6); + + try { + mVideoEditorHelper.createTAlpha("transition5And6", mediaVideoItem5, + mediaVideoItem6, 2000, Transition.BEHAVIOR_MIDDLE_SLOW, + INPUT_FILE_PATH + "imDummyFile.jpg", 70, + true); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("MaskFile is not exsisting", flagForException); + flagForException = false; + try { + mVideoEditorHelper.createTAlpha("transition5And6", null, null, 2000, + Transition.BEHAVIOR_MIDDLE_SLOW, maskFilename, 101, true); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Invalid Blending Percent", flagForException); + + flagForException = false; + try { + mVideoEditorHelper.createTAlpha("transitiond6", mediaVideoItem4, + mediaVideoItem5, 2000, Transition.BEHAVIOR_SPEED_UP - 1, + maskFilename, 30, false); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Transition FadeBlack with Invalid behavior", flagForException); + flagForException = false; + try { + mVideoEditorHelper.createTAlpha("transitiond6", mediaVideoItem4, + mediaVideoItem5, 2000, Transition.BEHAVIOR_MIDDLE_FAST + 1, + maskFilename, 30, false); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Transition FadeBlack with Invalid behavior", flagForException); + } + + /** + * To test Frame Overlay for Media Video Item + */ + + // TODO : remove TC_API_041 + @LargeTest + public void testFrameOverlayVideoItem() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + + "H263_profile0_176x144_10fps_256kbps_0_25.3gp"; + final String overlayFile1 = INPUT_FILE_PATH + "IMG_176x144_Overlay1.png"; + final String overlayFile2 = INPUT_FILE_PATH + "IMG_176x144_Overlay2.png"; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final Bitmap mBitmap1 = mVideoEditorHelper.getBitmap(overlayFile1, + 176, 144); + final OverlayFrame overlayFrame1 = mVideoEditorHelper.createOverlay( + mediaVideoItem1, "overlayId1", mBitmap1, 5000, 5000); + mediaVideoItem1.addOverlay(overlayFrame1); + + assertEquals("Overlay : Media Item", mediaVideoItem1, + overlayFrame1.getMediaItem()); + assertTrue("Overlay Id", overlayFrame1.getId().equals("overlayId1")); + assertEquals("Overlay Bitmap", mBitmap1, overlayFrame1.getBitmap()); + assertEquals("Overlay Start Time", 5000, overlayFrame1.getStartTime()); + assertEquals("Overlay Duration", 5000, overlayFrame1.getDuration()); + + Bitmap upddateBmp = mVideoEditorHelper.getBitmap(overlayFile2, 176, 144); + overlayFrame1.setBitmap(upddateBmp); + assertEquals("Overlay Update Bitmap", upddateBmp, overlayFrame1.getBitmap()); + upddateBmp.recycle(); + } + + /** + * To test Frame Overlay for Media Video Item : Set duration and Get + * Duration + */ + + // TODO : remove TC_API_042 + @LargeTest + public void testFrameOverlaySetAndGet() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + + "MPEG4_SP_640x480_30fps_512Kbps_0_27.mp4"; + final String overlayFile1 = INPUT_FILE_PATH + "IMG_640x480_Overlay1.png"; + boolean flagForException = false; + + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFile1, + 640, 480); + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final OverlayFrame overlayFrame1 = mVideoEditorHelper.createOverlay( + mediaVideoItem1, "overlayId1", mBitmap, 5000, 5000); + mediaVideoItem1.addOverlay(overlayFrame1); + overlayFrame1.setDuration(5000); + + assertEquals("Overlay Duration", 5000, overlayFrame1.getDuration()); + try { + overlayFrame1.setDuration(mediaVideoItem1.getDuration() + 10000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Overlay Duration > MediaVideo Item Duration", + flagForException); + + assertEquals("Overlay Duration", 5000, overlayFrame1.getDuration()); + flagForException = false; + + try { + overlayFrame1.setDuration(-1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Overlay Duration = -1", flagForException); + } + + /** + * To test Frame Overlay for Media Video Item : Set duration and Get + * Duration + */ + + // TODO : remove TC_API_043 + @LargeTest + public void testFrameOverlayInvalidTime() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + + "MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp"; + final String overlayFile1 = INPUT_FILE_PATH + "IMG_640x480_Overlay1.png"; + boolean flagForException = false; + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem1); + + try { + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFile1, + 640, 480); + mVideoEditorHelper.createOverlay(mediaVideoItem1, "overlayId1", + mBitmap, 400000000, 2000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Overlay With Invalid Start Time", flagForException); + + flagForException = false; + try { + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFile1, + 640, 480); + mVideoEditorHelper.createOverlay(mediaVideoItem1, "overlayId2", + mBitmap, -1, 2000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Overlay With Invalid Start Time", flagForException); + + flagForException = false; + try { + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFile1, + 640, 480); + mVideoEditorHelper.createOverlay(mediaVideoItem1, "overlayId3", + mBitmap, 2000, -1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Overlay With Invalid Start Time", flagForException); + } + + /** + * To test Frame Overlay for Media Image Item + */ + // TODO : remove TC_API_045 + @LargeTest + public void testFrameOverlayImageItem() throws Exception { + final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final String overlayFile1 = INPUT_FILE_PATH + "IMG_640x480_Overlay1.png"; + final String overlayFile2 = INPUT_FILE_PATH + "IMG_640x480_Overlay2.png"; + + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + imageItemFilename1, 10000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem1); + + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFile1, 640, + 480); + final OverlayFrame overlayFrame1 = mVideoEditorHelper.createOverlay( + mediaImageItem1, "overlayId1", mBitmap, 5000, 5000); + mediaImageItem1.addOverlay(overlayFrame1); + + assertEquals("Overlay : Media Item", mediaImageItem1, + overlayFrame1.getMediaItem()); + assertTrue("Overlay Id", overlayFrame1.getId().equals("overlayId1")); + assertEquals("Overlay Bitmap",mBitmap ,overlayFrame1.getBitmap()); + assertEquals("Overlay Start Time", 5000, overlayFrame1.getStartTime()); + assertEquals("Overlay Duration", 5000, overlayFrame1.getDuration()); + Bitmap upddateBmp = mVideoEditorHelper.getBitmap(overlayFile2, 640, 480); + + overlayFrame1.setBitmap(upddateBmp); + assertEquals("Overlay Update Bitmap", upddateBmp, overlayFrame1.getBitmap()); + upddateBmp.recycle(); + } + + /** + * To test Frame Overlay for Media Image Item : Set duration and Get + * Duration + */ + + // TODO : remove TC_API_046 + @LargeTest + public void testFrameOverlaySetAndGetImage() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final String overlayFile1 = INPUT_FILE_PATH + "IMG_640x480_Overlay1.png"; + boolean flagForException = false; + + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, 10000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem1); + + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFile1, + 640, 480); + final OverlayFrame overlayFrame1 = mVideoEditorHelper.createOverlay( + mediaImageItem1, "overlayId1", mBitmap, 5000, 5000); + mediaImageItem1.addOverlay(overlayFrame1); + + overlayFrame1.setDuration(5000); + assertEquals("Overlay Duration", 5000, overlayFrame1.getDuration()); + + try { + overlayFrame1.setDuration(mediaImageItem1.getDuration() + 10000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Overlay Duration > Media Item Duration", flagForException); + assertEquals("Overlay Duration", 5000, overlayFrame1.getDuration()); + + flagForException = false; + try { + overlayFrame1.setDuration(-1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Overlay Duration = -1", flagForException); + } + + /** + * To test Frame Overlay for Media Image Item :Invalid StartTime and + * Duration + */ + + // TODO : remove TC_API_047 + @LargeTest + public void testFrameOverlayInvalidTimeImage() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final String overlayFile1 = INPUT_FILE_PATH + "IMG_640x480_Overlay1.png"; + boolean flagForException = false; + + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, 10000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem1); + + try { + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFile1, + 640, 480); + mVideoEditorHelper.createOverlay(mediaImageItem1, "overlayId1", + mBitmap, 400000000, 2000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Overlay With Invalid Start Time", flagForException); + + flagForException = false; + try { + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFile1, + 640, 480); + mVideoEditorHelper.createOverlay(mediaImageItem1, "overlayId2", + mBitmap, -1, 2000); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Overlay With Invalid Start Time", flagForException); + + flagForException = false; + try { + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFile1, + 640, 480); + mVideoEditorHelper.createOverlay(mediaImageItem1, "overlayId3", + mBitmap, 2000, -1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Overlay With Invalid Start Time", flagForException); + } + + /** + * To Test Frame Overlay Media Image Item :JPG File + */ + + // TODO : remove TC_API_048 + @LargeTest + public void testFrameOverlayJPGImage() throws Exception { + + final String imageItemFilename = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final String overlayFile1 = INPUT_FILE_PATH + "IMG_640x480_Overlay1.png"; + boolean flagForException = false; + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + imageItemFilename, 10000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem1); + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFile1, 640, + 480); + mVideoEditorHelper.createOverlay(mediaImageItem1, "overlayId1", + mBitmap, 5000, 5000); + } + + /** + * To test Video Editor API + * + * @throws Exception + */ + // TODO : remove TC_API_049 + @LargeTest + public void testVideoEditorAPI() throws Exception { + + final String videoItemFileName1 = INPUT_FILE_PATH + + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4"; + final String videoItemFileName2 = INPUT_FILE_PATH + + "MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp"; + final String videoItemFileName3 = INPUT_FILE_PATH + + "MPEG4_SP_640x480_15fps_512kbps_AACLC_48khz_132kbps_s_0_26.mp4"; + final String imageItemFileName1 = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final String imageItemFileName2 = INPUT_FILE_PATH + "IMG_176x144.jpg"; + final String audioFilename1 = INPUT_FILE_PATH + + "AMRNB_8KHz_12.2Kbps_m_1_17.3gp"; + final String audioFilename2 = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + TransitionCrossfade transition2And4; + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFileName1, renderingMode); + mediaVideoItem1.setExtractBoundaries(0, 10000); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final MediaVideoItem mediaVideoItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + videoItemFileName2, renderingMode); + mediaVideoItem2.setExtractBoundaries(mediaVideoItem2.getDuration() / 4, + mediaVideoItem2.getDuration() / 2); + mVideoEditor.addMediaItem(mediaVideoItem2); + + final MediaVideoItem mediaVideoItem3 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m3", + videoItemFileName3, renderingMode); + mediaVideoItem3.setExtractBoundaries(mediaVideoItem3.getDuration() / 2, + mediaVideoItem3.getDuration()); + mVideoEditor.addMediaItem(mediaVideoItem3); + + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m4", + imageItemFileName1, 5000, renderingMode); + + final MediaImageItem mediaImageItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m5", + imageItemFileName2, 5000, renderingMode); + + List<MediaItem> mediaList = mVideoEditor.getAllMediaItems(); + assertEquals("Media Item List Size", 3, mediaList.size()); + + mVideoEditor.insertMediaItem(mediaImageItem1, mediaVideoItem2.getId()); + mediaList = mVideoEditor.getAllMediaItems(); + assertEquals("Media Item List Size", 4, mediaList.size()); + assertEquals("Media item 1", mediaVideoItem1, mediaList.get(0)); + assertEquals("Media item 2", mediaVideoItem2, mediaList.get(1)); + assertEquals("Media item 4", mediaImageItem1, mediaList.get(2)); + assertEquals("Media item 3", mediaVideoItem3, mediaList.get(3)); + + mVideoEditor.insertMediaItem(mediaImageItem2, mediaImageItem1.getId()); + mediaList = mVideoEditor.getAllMediaItems(); + assertEquals("Media Item List Size", 5, mediaList.size()); + assertEquals("Media item 1", mediaVideoItem1, mediaList.get(0)); + assertEquals("Media item 2", mediaVideoItem2, mediaList.get(1)); + assertEquals("Media item 4", mediaImageItem1, mediaList.get(2)); + assertEquals("Media item 5", mediaImageItem2, mediaList.get(3)); + assertEquals("Media item 3", mediaVideoItem3, mediaList.get(4)); + + mVideoEditor.moveMediaItem(mediaVideoItem1.getId(), mediaImageItem2.getId()); + mediaList = mVideoEditor.getAllMediaItems(); + assertEquals("Media Item List Size", 5, mediaList.size()); + assertEquals("Media item 2", mediaVideoItem2, mediaList.get(0)); + assertEquals("Media item 4", mediaImageItem1, mediaList.get(1)); + assertEquals("Media item 5", mediaImageItem2, mediaList.get(2)); + assertEquals("Media item 1", mediaVideoItem1, mediaList.get(3)); + assertEquals("Media item 3", mediaVideoItem3, mediaList.get(4)); + + assertEquals("Media Item 1", mediaVideoItem1, + mVideoEditor.getMediaItem(mediaVideoItem1.getId())); + + flagForException = false; + transition2And4 = null; + try{ + transition2And4 = mVideoEditorHelper.createTCrossFade( + "transition2And4", mediaVideoItem2, mediaImageItem1, 2000, + Transition.BEHAVIOR_MIDDLE_FAST); + mVideoEditor.addTransition(transition2And4); + } + catch (IllegalArgumentException e) { + flagForException = true; + } + assertFalse("Transition2and4 cannot be created", flagForException); + + + TransitionCrossfade transition1And3 = null; + flagForException = false; + try{ + transition1And3 = mVideoEditorHelper.createTCrossFade( + "transition1And3", mediaVideoItem1, mediaVideoItem2, 5000, + Transition.BEHAVIOR_MIDDLE_FAST); + mVideoEditor.addTransition(transition1And3); + }catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Transition1and3 cannot be created", flagForException); + + List<Transition> transitionList = mVideoEditor.getAllTransitions(); + assertEquals("Transition List", 1, transitionList.size()); + + assertEquals("Transition 2", transition2And4, + mVideoEditor.getTransition(transition2And4.getId())); + + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrack", audioFilename1); + mVideoEditor.addAudioTrack(audioTrack); + + List<AudioTrack> audioList = mVideoEditor.getAllAudioTracks(); + assertEquals("Audio List", 1, audioList.size()); + + final AudioTrack audioTrack1 = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrack1", audioFilename2); + flagForException = false; + try { + mVideoEditor.addAudioTrack(audioTrack1); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Audio Track support is 1 ", flagForException); + + flagForException = false; + try { + mVideoEditor.insertAudioTrack(audioTrack1,"audioTrack"); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Audio Track supports is 1 ", flagForException); + + assertEquals("Removing AudioTrack", audioTrack, + mVideoEditor.removeAudioTrack(audioTrack.getId())); + + assertEquals("Removing transition", transition2And4, + mVideoEditor.removeTransition(transition2And4.getId())); + + assertEquals("Removing Media Item", mediaVideoItem2, + mVideoEditor.removeMediaItem(mediaVideoItem2.getId())); + + mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_16_9); + assertEquals("Check Aspect Ratio", MediaProperties.ASPECT_RATIO_16_9, + mVideoEditor.getAspectRatio()); + + long storyBoardDuration = mediaVideoItem1.getTimelineDuration() + + mediaVideoItem3.getTimelineDuration() + + mediaImageItem1.getDuration() + + mediaImageItem2.getDuration(); + assertEquals("Story Board Duration", storyBoardDuration, + mVideoEditor.getDuration()); + } + + /** + * To add Audio Track Greater than MediaItem Duration + * + * @throws Exception + */ + // TODO : remove TC_API_050 + @LargeTest + public void testVideoLessThanAudio() throws Exception { + final String videoItemFileName1 = INPUT_FILE_PATH + + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4"; + final String audioTrackFilename = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFileName1, renderingMode); + mVideoEditor.addMediaItem(mediaVideoItem1); + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrackId", audioTrackFilename); + mVideoEditor.addAudioTrack(audioTrack); + assertEquals("Storyboard = mediaItem Duration", + mediaVideoItem1.getDuration(), mVideoEditor.getDuration()); + assertTrue("Audio Duration > mediaItem Duration", + (audioTrack.getDuration() > mediaVideoItem1.getDuration() ? + true : false)); + } + + /** + * To test Video Editor API with 1080 P + * + * @throws Exception + */ + // TODO : remove TC_API_051 + @LargeTest + public void testVideoContentHD() throws Exception { + final String videoItemFileName1 = INPUT_FILE_PATH + + "H264_BP_1920x1080_30fps_1200Kbps_1_10.mp4"; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final MediaVideoItem mediaVideoItem1; + boolean flagForException = false; + try { + mediaVideoItem1 = mVideoEditorHelper.createMediaItem(mVideoEditor, + "m1", videoItemFileName1, renderingMode); + } catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("VideoContent 1920x1080", flagForException); + } + + + /** + * To test: Remove audio track + * + * @throws Exception + */ + // TODO : remove TC_API_052 + @LargeTest + public void testRemoveAudioTrack() throws Exception { + final String audioFileName = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + boolean flagForException = false; + + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrack1", audioFileName); + mVideoEditor.addAudioTrack(audioTrack); + + assertEquals("Audio Track Item Duration", audioTrack.getDuration(), + audioTrack.getTimelineDuration()); + assertTrue("Audio Track ID", audioTrack.getId().equals("audioTrack1")); + assertNotNull("Remove Audio Track", + mVideoEditor.removeAudioTrack("audioTrack1")); + try{ + mVideoEditor.removeAudioTrack("audioTrack1"); + }catch (IllegalArgumentException e){ + flagForException = true; + } + assertTrue("Remove Audio Track not possible", flagForException); + } + + /** + * To test: Disable ducking + * + * @throws Exception + */ + // TODO : remove TC_API_053 + @LargeTest + public void testAudioDuckingDisable() throws Exception { + final String audioFileName = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrack", audioFileName); + mVideoEditor.addAudioTrack(audioTrack); + + audioTrack.disableDucking(); + assertFalse("Audio Track Ducking is Disabled", + audioTrack.isDuckingEnabled()); + } + + + // TODO : remove TC_API_054 + /** This test case is added with Test case ID TC_API_010 */ + + /** + * To test: Need a basic test case for the get value for TransitionAlpha + * ( ie. getBlendingPercent, getMaskFilename, isInvert) + * + * @throws Exception + */ + // TODO : remove TC_API_055 + @LargeTest + public void testTransitionAlphaBasic() throws Exception { + + final String videoItemFilename1 = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + final String maskFilename = INPUT_FILE_PATH + "IMG_640x480_Overlay1.png"; + boolean flagForException = false; + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(0, 15000); + + final MediaImageItem mediaImageItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", maskFilename, + 10000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaImageItem.setDuration(15000); + + mVideoEditor.addMediaItem(mediaVideoItem1); + mVideoEditor.addMediaItem(mediaImageItem); + final TransitionAlpha transition1And2 = + mVideoEditorHelper.createTAlpha("transition1And2", mediaVideoItem1, + mediaImageItem, 3000, Transition.BEHAVIOR_SPEED_UP, + maskFilename, 10, false); + mVideoEditor.addTransition(transition1And2); + assertTrue("Transition maskFile", + transition1And2.getMaskFilename().equals(maskFilename)); + assertEquals("Transition BlendingPercent", 10, + transition1And2.getBlendingPercent()); + assertFalse("Transition Invert", transition1And2.isInvert()); + } + + /** + * To test: NULL arguments to the Video Editor APIs + * + * @throws Exception + */ + // TODO : remove TC_API_056 + @LargeTest + public void testNullAPIs() throws Exception { + + final String videoItemFilename1 = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + final String maskFilename = INPUT_FILE_PATH + + "IMG_640x480_Overlay1.png"; + final String audioFileName = INPUT_FILE_PATH + + "AACLC_48KHz_256Kbps_s_1_17.3gp"; + boolean flagForException = false; + + try { + mVideoEditor.addAudioTrack(null); + } catch(IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Video Editor with null Audio Track", flagForException); + flagForException = false; + try { + mVideoEditor.addMediaItem(null); + } catch(IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Video Editor with NULL Image Item ", flagForException); + flagForException = false; + try { + mVideoEditor.addMediaItem(null); + } catch(IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Video Editor with NULL Video Item ", flagForException); + + MediaVideoItem mediaVideoItem1 = null; + try { + mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + } catch (IllegalArgumentException e) { + assertTrue("Cannot Create Video Item", false); + } + mediaVideoItem1.setExtractBoundaries(0, 15000); + mVideoEditor.addMediaItem(mediaVideoItem1); + flagForException = false; + try { + mediaVideoItem1.addEffect(null); + } catch(IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Video with null effect ", flagForException); + flagForException = false; + try { + mediaVideoItem1.addOverlay(null); + } catch(IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Video with null overlay ", flagForException); + + final MediaImageItem mediaImageItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", maskFilename, + 10000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaImageItem.setDuration(15000); + mVideoEditor.addMediaItem(mediaImageItem); + flagForException = false; + try { + mediaImageItem.addEffect(null); + } catch(IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Image with null effect ", flagForException); + flagForException = false; + try { + mediaImageItem.addOverlay(null); + } catch(IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Image with null overlay ", flagForException); + + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "audioTrack", audioFileName); + mVideoEditor.addAudioTrack(audioTrack); + + flagForException = false; + try { + mVideoEditor.addTransition(null); + } catch(IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Added null transition ", flagForException); + + flagForException = false; + try { + mVideoEditor.addTransition(null); + } catch(IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Added null transition ", flagForException); + + } +} diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorExportTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorExportTest.java new file mode 100755 index 000000000000..37b1f5492951 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorExportTest.java @@ -0,0 +1,818 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.functional; + +import java.io.File; + +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.media.videoeditor.AudioTrack; +import android.media.videoeditor.EffectColor; +import android.media.videoeditor.EffectKenBurns; +import android.media.videoeditor.MediaImageItem; +import android.media.videoeditor.MediaItem; +import android.media.videoeditor.MediaProperties; +import android.media.videoeditor.MediaVideoItem; +import android.media.videoeditor.OverlayFrame; +import android.media.videoeditor.Transition; +import android.media.videoeditor.TransitionAlpha; +import android.media.videoeditor.TransitionCrossfade; +import android.media.videoeditor.TransitionFadeBlack; +import android.media.videoeditor.TransitionSliding; +import android.media.videoeditor.VideoEditor; +import android.media.videoeditor.VideoEditor.ExportProgressListener; +import android.media.videoeditor.VideoEditor.MediaProcessingProgressListener; +import android.os.Environment; +import android.test.ActivityInstrumentationTestCase; + + +import android.util.Log; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import android.test.suitebuilder.annotation.LargeTest; +import com.android.mediaframeworktest.VideoEditorHelper; + +public class VideoEditorExportTest extends + ActivityInstrumentationTestCase<MediaFrameworkTest> { + private final String TAG = "TransitionTest"; + + private final String PROJECT_LOCATION = VideoEditorHelper.PROJECT_LOCATION_COMMON; + + private final String INPUT_FILE_PATH = VideoEditorHelper.INPUT_FILE_PATH_COMMON; + + private VideoEditor mVideoEditor; + + private VideoEditorHelper mVideoEditorHelper; + + // Declares the annotation for Preview Test Cases + public @interface TransitionTests { + } + + public VideoEditorExportTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + // setup for each test case. + super.setUp(); + mVideoEditorHelper = new VideoEditorHelper(); + // Create a random String which will be used as project path, where all + // project related files will be stored. + final String projectPath = + mVideoEditorHelper.createRandomFile(PROJECT_LOCATION); + mVideoEditor = mVideoEditorHelper.createVideoEditor(projectPath); + } + + @Override + protected void tearDown() throws Exception { + mVideoEditorHelper.destroyVideoEditor(mVideoEditor); + // Clean the directory created as project path + mVideoEditorHelper.deleteProject(new File(mVideoEditor.getPath())); + System.gc(); + super.tearDown(); + } + + /** + * To Test export : Merge and Trim different types of Video and Image files + */ + // TODO :remove TC_EXP_001 + @LargeTest + public void testExportMergeTrim() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final String videoItemFilename2 = INPUT_FILE_PATH + + "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4"; + final String videoItemFilename3 = INPUT_FILE_PATH + + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4"; + final String imageItemFilename2 = INPUT_FILE_PATH + "IMG_176x144.jpg"; + final String imageItemFilename3 = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final String outFilename = mVideoEditorHelper + .createRandomFile(mVideoEditor.getPath() + "/") + + ".3gp"; + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(2000, 7000); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final MediaImageItem mediaImageItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + imageItemFilename1, 3000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem2); + + final MediaVideoItem mediaVideoItem3 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m3", + videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem3.setExtractBoundaries(0, 2000); + mVideoEditor.addMediaItem(mediaVideoItem3); + + final MediaVideoItem mediaVideoItem4 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m4", + videoItemFilename3, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem4.setExtractBoundaries(mediaVideoItem4.getDuration()-5000, + mediaVideoItem4.getDuration()); + mVideoEditor.addMediaItem(mediaVideoItem4); + + final MediaImageItem mediaImageItem5 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m5", + imageItemFilename2, 4000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem5); + + final MediaImageItem mediaImageItem6 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m6", + imageItemFilename3, 2000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem6); + + mVideoEditor.generatePreview(new MediaProcessingProgressListener() { + public void onProgress(Object item, int action, int progress) { + } + }); + + try { + final int[] progressUpdate = new int[100]; + mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720, + MediaProperties.BITRATE_800K, new ExportProgressListener() { + int i = 0; + public void onProgress(VideoEditor ve, String outFileName, + int progress) { + progressUpdate[i++] = progress; + } + }); + mVideoEditorHelper.checkProgressCBValues(progressUpdate); + } catch (Exception e) { + assertTrue("Error in Export" + e.toString(), false); + } + final long storyBoardDuration = mediaVideoItem1.getTimelineDuration() + + mediaImageItem2.getDuration() + mediaVideoItem3.getTimelineDuration() + + mediaVideoItem4.getTimelineDuration() + mediaImageItem5.getDuration() + + mediaImageItem6.getDuration(); + mVideoEditorHelper.validateExport(mVideoEditor, outFilename, + MediaProperties.HEIGHT_720, 0, storyBoardDuration, + MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC); + mVideoEditorHelper.checkDeleteExistingFile(outFilename); + } + + /** + *To Test export : With Effect and Overlays on Different Media Items + */ + // TODO :remove TC_EXP_002 + @LargeTest + public void testExportEffectOverlay() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final String videoItemFilename2 = INPUT_FILE_PATH + + "H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp"; + final String videoItemFilename3 = INPUT_FILE_PATH + + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4"; + final String imageItemFilename2 = INPUT_FILE_PATH + "IMG_176x144.jpg"; + final String imageItemFilename3 = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final String outFilename = mVideoEditorHelper + .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp"; + + final String overlayFile = INPUT_FILE_PATH + "IMG_640x480_Overlay1.png"; + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(2000, 7000); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final EffectColor effectPink = + mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effectPink", + 0, 2000, EffectColor.TYPE_COLOR, EffectColor.PINK); + mediaVideoItem1.addEffect(effectPink); + + final EffectColor effectNegative = + mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effectNegative", + 3000, 4000, EffectColor.TYPE_NEGATIVE, 0); + mediaVideoItem1.addEffect(effectNegative); + + final MediaImageItem mediaImageItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + imageItemFilename1, 3000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem2); + + final EffectColor effectFifties = + mVideoEditorHelper.createEffectItem(mediaImageItem2, "effectFifties", + 0, 3000, EffectColor.TYPE_FIFTIES, 0); + mediaImageItem2.addEffect(effectFifties); + + final MediaVideoItem mediaVideoItem3 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m3", + videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem3); + mediaVideoItem3.setExtractBoundaries(0, 8000); + + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFile, + 640, 480); + final OverlayFrame overlayFrame = + mVideoEditorHelper.createOverlay(mediaVideoItem3, "overlay", + mBitmap, 2000, 5000); + mediaVideoItem3.addOverlay(overlayFrame); + + final EffectColor effectGreen = + mVideoEditorHelper.createEffectItem(mediaVideoItem3, "effectGreen", + 0, 2000, EffectColor.TYPE_COLOR, EffectColor.GREEN); + mediaVideoItem3.addEffect(effectGreen); + + final MediaVideoItem mediaVideoItem4 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m4", + videoItemFilename3, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem4.setExtractBoundaries(mediaVideoItem4.getDuration()-5000, + mediaVideoItem4.getDuration()); + mVideoEditor.addMediaItem(mediaVideoItem4); + + final EffectColor effectSepia = + mVideoEditorHelper.createEffectItem(mediaVideoItem4, "effectSepia", + 0, 2000, EffectColor.TYPE_SEPIA, 0); + mediaVideoItem4.addEffect(effectSepia); + + final MediaImageItem mediaImageItem5 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m5", + imageItemFilename2, 4000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem5); + + final EffectColor effectGray = + mVideoEditorHelper.createEffectItem(mediaImageItem5, "effectGray", + 0, 2000, EffectColor.TYPE_COLOR, EffectColor.GRAY); + mediaImageItem5.addEffect(effectGray); + + final MediaImageItem mediaImageItem6 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m6", + imageItemFilename3, 2000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem6); + + final EffectColor effectGradient = + mVideoEditorHelper.createEffectItem(mediaImageItem6, + "effectGradient", 0, 2000, EffectColor.TYPE_GRADIENT, + EffectColor.PINK); + mediaImageItem6.addEffect(effectGradient); + + mVideoEditor.generatePreview(new MediaProcessingProgressListener() { + public void onProgress(Object item, int action, int progress) { + } + }); + + try { + final int[] progressUpdate = new int[100]; + mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720, + MediaProperties.BITRATE_800K, new ExportProgressListener() { + int i = 0; + public void onProgress(VideoEditor ve, String outFileName, + int progress) { + progressUpdate[i++] = progress; + } + }); + mVideoEditorHelper.checkProgressCBValues(progressUpdate); + } catch (Exception e) { + assertTrue("Error in Export" + e.toString(), false); + } + final long storyBoardDuration = mediaVideoItem1.getTimelineDuration() + + mediaImageItem2.getDuration() + + mediaVideoItem3.getTimelineDuration() + + mediaVideoItem4.getTimelineDuration() + + mediaImageItem5.getDuration() + + mediaImageItem6.getDuration(); + mVideoEditorHelper.validateExport(mVideoEditor, outFilename, + MediaProperties.HEIGHT_720, 0, storyBoardDuration, + MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC); + mVideoEditorHelper.checkDeleteExistingFile(outFilename); + } + + /** + * To test export : with Image with KenBurnEffect + */ + // TODO : remove TC_EXP_003 + @LargeTest + public void testExportEffectKenBurn() throws Exception { + final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final int imageItemRenderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final String outFilename = mVideoEditorHelper + .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp"; + + final MediaImageItem mediaImageItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaImageItem1", + imageItemFileName, 5000, imageItemRenderingMode); + mVideoEditor.addMediaItem(mediaImageItem); + + final Rect startRect = new Rect((mediaImageItem.getHeight() / 3), + (mediaImageItem.getWidth() / 3), (mediaImageItem.getHeight() / 2), + (mediaImageItem.getWidth() / 2)); + + final Rect endRect = new Rect(0, 0, mediaImageItem.getWidth(), + mediaImageItem.getHeight()); + + final EffectKenBurns kbEffectOnMediaItem = new EffectKenBurns( + mediaImageItem, "KBOnM2", startRect, endRect, 500, 3000); + assertNotNull("EffectKenBurns", kbEffectOnMediaItem); + mediaImageItem.addEffect(kbEffectOnMediaItem); + + assertEquals("KenBurn Start Rect", startRect, + kbEffectOnMediaItem.getStartRect()); + assertEquals("KenBurn End Rect", endRect, + kbEffectOnMediaItem.getEndRect()); + + mVideoEditor.generatePreview(new MediaProcessingProgressListener() { + public void onProgress(Object item, int action, int progress) { + } + }); + + try { + final int[] progressUpdate = new int[100]; + mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720, + MediaProperties.BITRATE_800K, new ExportProgressListener() { + int i = 0; + public void onProgress(VideoEditor ve, String outFileName, + int progress) { + progressUpdate[i++] = progress; + } + }); + mVideoEditorHelper.checkProgressCBValues(progressUpdate); + } catch (Exception e) { + assertTrue("Error in Export" + e.toString(), false); + } + mVideoEditorHelper.validateExport(mVideoEditor, outFilename, + MediaProperties.HEIGHT_720, 0, mediaImageItem.getDuration(), + MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC); + mVideoEditorHelper.checkDeleteExistingFile(outFilename); + } + + /** + * To Test Export : With Video and Image and An Audio BackGround Track + */ + // TODO : remove TC_EXP_004 + @LargeTest + public void testExportAudio() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final String imageItemFileName = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final String outFilename = mVideoEditorHelper + .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp"; + final String audioTrackFilename = INPUT_FILE_PATH + + "AMRNB_8KHz_12.2Kbps_m_1_17.3gp"; + + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem.setExtractBoundaries(0, 10000); + mVideoEditor.addMediaItem(mediaVideoItem); + + final MediaImageItem mediaImageItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + imageItemFileName, 5000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem); + + final AudioTrack audioTrack = mVideoEditorHelper.createAudio( + mVideoEditor, "a1", audioTrackFilename); + audioTrack.setExtractBoundaries(2000, 5000); + mVideoEditor.addAudioTrack(audioTrack); + + audioTrack.disableDucking(); + audioTrack.enableLoop(); + audioTrack.setVolume(75); + + mVideoEditor.generatePreview(new MediaProcessingProgressListener() { + public void onProgress(Object item, int action, int progress) { + } + }); + + try { + final int[] progressUpdate = new int[100]; + mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720, + MediaProperties.BITRATE_800K, new ExportProgressListener() { + int i = 0; + public void onProgress(VideoEditor ve, String outFileName, + int progress) { + progressUpdate[i++] = progress; + } + }); + mVideoEditorHelper.checkProgressCBValues(progressUpdate); + } catch (Exception e) { + assertTrue("Error in Export" + e.toString(), false); + } + mVideoEditorHelper.validateExport(mVideoEditor, outFilename, + MediaProperties.HEIGHT_720, 0, (mediaVideoItem.getTimelineDuration() + + mediaImageItem.getDuration()), + MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC); + + mVideoEditorHelper.checkDeleteExistingFile(outFilename); + } + + /** + *To Test export : With Transition on Different Media Items + */ + // TODO :remove TC_EXP_005 + @LargeTest + public void testExportTransition() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final String videoItemFilename2 = INPUT_FILE_PATH + + "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4"; + final String videoItemFilename3 = INPUT_FILE_PATH + + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4"; + + final String imageItemFilename2 = INPUT_FILE_PATH + "IMG_176x144.jpg"; + final String imageItemFilename3 = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final String outFilename = mVideoEditorHelper + .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp"; + final String maskFilename = INPUT_FILE_PATH + + "TransitionSpiral_QVGA.jpg"; + + final MediaVideoItem mediaItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaItem1.setExtractBoundaries(2000, 7000); + mVideoEditor.addMediaItem(mediaItem1); + + final TransitionAlpha transition1 = + mVideoEditorHelper.createTAlpha("transition1", null, mediaItem1, + 2000, Transition.BEHAVIOR_LINEAR, maskFilename, 50, true); + mVideoEditor.addTransition(transition1); + + final MediaImageItem mediaItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + imageItemFilename1, 8000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaItem2); + + final MediaVideoItem mediaItem3 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m3", + videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaItem3.setExtractBoundaries(0, 8000); + mVideoEditor.addMediaItem(mediaItem3); + + final TransitionSliding transition2And3 = + mVideoEditorHelper.createTSliding("transition2", mediaItem2, + mediaItem3, 4000, Transition.BEHAVIOR_MIDDLE_FAST, + TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN); + mVideoEditor.addTransition(transition2And3); + + final MediaVideoItem mediaItem4 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m4", + videoItemFilename3, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaItem4); + mediaItem4.setExtractBoundaries(0, 8000); + + final TransitionCrossfade transition3And4 = + mVideoEditorHelper.createTCrossFade("transition3", mediaItem3, + mediaItem4, 3500, Transition.BEHAVIOR_MIDDLE_SLOW); + mVideoEditor.addTransition(transition3And4); + + final MediaImageItem mediaItem5 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m5", + imageItemFilename2, 7000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaItem5); + + final TransitionFadeBlack transition4And5 = + mVideoEditorHelper.createTFadeBlack("transition4", mediaItem4, + mediaItem5, 3500, Transition.BEHAVIOR_SPEED_DOWN); + mVideoEditor.addTransition(transition4And5); + + final MediaImageItem mediaItem6 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m6", + imageItemFilename3, 3000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaItem6); + + final TransitionSliding transition5And6 = + mVideoEditorHelper.createTSliding("transition5", mediaItem5, + mediaItem6, 1000/*4000*/, Transition.BEHAVIOR_SPEED_UP, + TransitionSliding.DIRECTION_LEFT_OUT_RIGHT_IN); + mVideoEditor.addTransition(transition5And6); + + final TransitionSliding transition6 = + mVideoEditorHelper.createTSliding("transition6", mediaItem6, null, + 1000 /*4000*/, Transition.BEHAVIOR_SPEED_UP, + TransitionSliding.DIRECTION_TOP_OUT_BOTTOM_IN); + mVideoEditor.addTransition(transition6); + + mVideoEditor.generatePreview(new MediaProcessingProgressListener() { + public void onProgress(Object item, int action, int progress) { + } + }); + + try { + final int[] progressUpdate = new int[100]; + mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720, + MediaProperties.BITRATE_800K, new ExportProgressListener() { + int i = 0; + public void onProgress(VideoEditor ve, String outFileName, + int progress) { + progressUpdate[i++] = progress; + } + }); + mVideoEditorHelper.checkProgressCBValues(progressUpdate); + } catch (Exception e) { + assertTrue("Error in Export" + e.toString(), false); + } + final long storyBoardDuration = mediaItem1.getTimelineDuration() + + mediaItem2.getTimelineDuration() + + mediaItem3.getTimelineDuration() - transition2And3.getDuration() + + mediaItem4.getTimelineDuration() - transition3And4.getDuration() + + mediaItem5.getTimelineDuration() - transition4And5.getDuration() + + mediaItem6.getTimelineDuration() - transition5And6.getDuration(); + mVideoEditorHelper.validateExport(mVideoEditor, outFilename, + MediaProperties.HEIGHT_720, 0, storyBoardDuration, + MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC); + mVideoEditorHelper.checkDeleteExistingFile(outFilename); + } + + /** + * To Test Export : Without any Media Items in the story Board + * + * @throws Exception + */ + // TODO :remove TC_EXP_006 + @LargeTest + public void testExportWithoutMediaItems() throws Exception { + boolean flagForException = false; + try { + final int[] progressUpdate = new int[100]; + mVideoEditor.export("/sdcard/Test.3gp", MediaProperties.HEIGHT_720, + MediaProperties.BITRATE_800K, new ExportProgressListener() { + int i = 0; + public void onProgress(VideoEditor ve, String outFileName, + int progress) { + progressUpdate[i++] = progress; + } + }); + mVideoEditorHelper.checkProgressCBValues(progressUpdate); + } catch (IllegalStateException e) { + flagForException = true; + } + assertTrue("Export without any MediaItems", flagForException); + } + + /** + * To Test Export : With Media Items add and removed in the story Board + * + * @throws Exception + */ + // TODO :remove TC_EXP_007 + @LargeTest + public void testExportWithoutMediaItemsAddRemove() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp"; + final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final String maskFilename = INPUT_FILE_PATH + "TransitionSpiral_QVGA.jpg"; + boolean flagForException = false; + + final MediaVideoItem mediaItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaItem1.setExtractBoundaries(0, 15000); + mVideoEditor.addMediaItem(mediaItem1); + + final MediaImageItem mediaItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + imageItemFilename1, 15000, + MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaItem2); + + final TransitionAlpha transition1 = + mVideoEditorHelper.createTAlpha("transition1", mediaItem1, mediaItem2, + 3000, Transition.BEHAVIOR_LINEAR, maskFilename, 50, false); + mVideoEditor.addTransition(transition1); + + final EffectColor effectColor = + mVideoEditorHelper.createEffectItem(mediaItem2, "effect", 12000, + 3000, EffectColor.TYPE_COLOR, EffectColor.PINK); + mediaItem2.addEffect(effectColor); + + mVideoEditor.removeMediaItem(mediaItem1.getId()); + mVideoEditor.removeMediaItem(mediaItem2.getId()); + try { + final int[] progressUpdate = new int[100]; + mVideoEditor.export("/sdcard/Test.3gp", MediaProperties.HEIGHT_720, + MediaProperties.BITRATE_800K, new ExportProgressListener() { + int i = 0; + public void onProgress(VideoEditor ve, String outFileName, + int progress) { + progressUpdate[i++] = progress; + } + }); + mVideoEditorHelper.checkProgressCBValues(progressUpdate); + } catch (IllegalStateException e) { + flagForException = true; + } + assertTrue("Export with MediaItem added and removed", flagForException); + } + + /** + * To Test Export : With Video and Image : MMS use case + * + * @throws Exception + */ + // TODO :remove TC_EXP_008 + @LargeTest + public void testExportMMS() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp"; + final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final String videoItemFilename2 = INPUT_FILE_PATH + + "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4"; + final String maskFilename = INPUT_FILE_PATH + "TransitionSpiral_QVGA.jpg"; + final String outFilename = mVideoEditorHelper + .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp"; + + final MediaVideoItem mediaItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaItem1.setExtractBoundaries(2000, 7000); + mVideoEditor.addMediaItem(mediaItem1); + + final TransitionAlpha transition1 = + mVideoEditorHelper.createTAlpha("transition1", null, mediaItem1, + 2000, Transition.BEHAVIOR_LINEAR, maskFilename, 50, true); + mVideoEditor.addTransition(transition1); + + final MediaImageItem mediaItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + imageItemFilename1, 8000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaItem2); + + final MediaVideoItem mediaItem3 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m3", + videoItemFilename2, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaItem3.setExtractBoundaries(0, 8000); + mVideoEditor.addMediaItem(mediaItem3); + + final TransitionSliding transition2And3 = + mVideoEditorHelper.createTSliding("transition2", mediaItem2, + mediaItem3, 4000, Transition.BEHAVIOR_MIDDLE_FAST, + TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN); + mVideoEditor.addTransition(transition2And3); + + final TransitionCrossfade transition3 = + mVideoEditorHelper.createTCrossFade("transition3", mediaItem3, null, + 3500, Transition.BEHAVIOR_MIDDLE_SLOW); + mVideoEditor.addTransition(transition3); + + final EffectColor effectColor = + mVideoEditorHelper.createEffectItem(mediaItem2, "effect", 0, + 3000, EffectColor.TYPE_COLOR, EffectColor.PINK); + mediaItem2.addEffect(effectColor); + + mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_11_9); + + try { + final int[] progressUpdate = new int[100]; + mVideoEditor.export(outFilename, MediaProperties.HEIGHT_144, + MediaProperties.BITRATE_800K, new ExportProgressListener() { + int i = 0; + public void onProgress(VideoEditor ve, String outFileName, + int progress) { + progressUpdate[i++] = progress; + } + }); + mVideoEditorHelper.checkProgressCBValues(progressUpdate); + } catch (Exception e) { + assertTrue("Error in Export" + e.toString(), false); + } + final long storyBoardDuration = mediaItem1.getTimelineDuration() + + mediaItem2.getTimelineDuration() + mediaItem3.getTimelineDuration() + - transition2And3.getDuration(); + + mVideoEditorHelper.validateExport(mVideoEditor, outFilename, + MediaProperties.HEIGHT_144, 0, storyBoardDuration, + MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC); + mVideoEditorHelper.checkDeleteExistingFile(outFilename); + } + + /** + * To Test Export :Media Item having duration of 1 Hour + * + * @throws Exception + */ + @LargeTest + public void testExportDuration1Hour() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + + "H264_BP_640x480_15fps_384kbps_60_0.mp4"; + final String outFilename = mVideoEditorHelper.createRandomFile( + mVideoEditor.getPath() + "/") + ".3gp"; + + final MediaVideoItem mediaItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaItem1); + try { + final int[] progressUpdate = new int[100]; + mVideoEditor.export(outFilename, MediaProperties.HEIGHT_144, + MediaProperties.BITRATE_800K, new ExportProgressListener() { + int i = 0; + public void onProgress(VideoEditor ve, String outFileName, + int progress) { + progressUpdate[i++] = progress; + } + }); + mVideoEditorHelper.checkProgressCBValues(progressUpdate); + }catch (Exception e) { + assertTrue("Error in Export" + e.toString(), false); + } + mVideoEditorHelper.validateExport(mVideoEditor, outFilename, + MediaProperties.HEIGHT_720, 0, mediaItem1.getDuration(), + MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC); + mVideoEditorHelper.checkDeleteExistingFile(outFilename); + } + + /** + * To Test Export : Storage location having very less space (Less than 100 + * KB) + * + * @throws Exception + */ + @LargeTest + public void testExportWithStorageFull() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + + "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4"; + final String outFilename = mVideoEditorHelper + .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp"; + boolean flagForException = false; + + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", videoItemFilename1, + MediaItem.RENDERING_MODE_BLACK_BORDER); + try { + final int[] progressUpdate = new int[100]; + mVideoEditor.export(outFilename, MediaProperties.HEIGHT_144, + MediaProperties.BITRATE_800K, new ExportProgressListener() { + int i = 0; + public void onProgress(VideoEditor ve, String outFileName, + int progress) { + progressUpdate[i++] = progress; + } + }); + mVideoEditorHelper.checkProgressCBValues(progressUpdate); + } catch (Exception e) { + flagForException = true; + } + assertTrue("Error in exporting file due to lack of storage space", + flagForException); + } + + /** + * To Test Export :Two Media Items added + * + * @throws Exception + */ + @LargeTest + public void testExportTwoVideos() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp"; + final String videoItemFileName1 = INPUT_FILE_PATH + + "H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4"; + final String outFilename = mVideoEditorHelper + .createRandomFile(mVideoEditor.getPath() + "/") + ".3gp"; + + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem); + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem1); + + mVideoEditor.generatePreview(new MediaProcessingProgressListener() { + public void onProgress(Object item, int action, int progress) { + } + }); + + try { + final int[] progressUpdate = new int[100]; + mVideoEditor.export(outFilename, MediaProperties.HEIGHT_720, + MediaProperties.BITRATE_800K, new ExportProgressListener() { + int i = 0; + public void onProgress(VideoEditor ve, String outFileName, + int progress) { + progressUpdate[i++] = progress; + } + }); + mVideoEditorHelper.checkProgressCBValues(progressUpdate); + } catch (Exception e) { + assertTrue("Error in Export" + e.toString(), false); + } + mVideoEditorHelper.validateExport(mVideoEditor, outFilename, + MediaProperties.HEIGHT_720, 0, + (mediaVideoItem.getDuration()+ mediaVideoItem1.getDuration()), + MediaProperties.VCODEC_H264BP, MediaProperties.ACODEC_AAC_LC); + mVideoEditorHelper.checkDeleteExistingFile(outFilename); + } +} diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorPreviewTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorPreviewTest.java new file mode 100644 index 000000000000..bd0a83845777 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/VideoEditorPreviewTest.java @@ -0,0 +1,1161 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.functional; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.Semaphore; + +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.media.videoeditor.AudioTrack; +import android.media.videoeditor.Effect; +import android.media.videoeditor.EffectColor; +import android.media.videoeditor.EffectKenBurns; +import android.media.videoeditor.MediaImageItem; +import android.media.videoeditor.MediaItem; +import android.media.videoeditor.MediaProperties; +import android.media.videoeditor.MediaVideoItem; +import android.media.videoeditor.Overlay; +import android.media.videoeditor.OverlayFrame; +import android.media.videoeditor.Transition; +import android.media.videoeditor.TransitionAlpha; +import android.media.videoeditor.TransitionCrossfade; +import android.media.videoeditor.TransitionFadeBlack; +import android.media.videoeditor.TransitionSliding; +import android.media.videoeditor.VideoEditor; +import android.media.videoeditor.VideoEditor.ExportProgressListener; +import android.media.videoeditor.VideoEditor.MediaProcessingProgressListener; +import android.media.videoeditor.VideoEditor.PreviewProgressListener; +import android.media.videoeditor.VideoEditor.OverlayData; +import android.os.Environment; +import android.test.ActivityInstrumentationTestCase; +import android.view.SurfaceHolder; + + +import com.android.mediaframeworktest.MediaFrameworkTest; +import android.test.suitebuilder.annotation.LargeTest; +import com.android.mediaframeworktest.VideoEditorHelper; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; + +import java.util.concurrent.TimeUnit; + +import android.util.Log; + +public class VideoEditorPreviewTest extends + ActivityInstrumentationTestCase<MediaFrameworkTest> { + private final String TAG = "VideoEditorTest"; + + private final String PROJECT_LOCATION = VideoEditorHelper.PROJECT_LOCATION_COMMON; + + private final String INPUT_FILE_PATH = VideoEditorHelper.INPUT_FILE_PATH_COMMON; + + private final String PROJECT_CLASS_NAME = + "android.media.videoeditor.VideoEditorImpl"; + + private VideoEditor mVideoEditor; + + private VideoEditorHelper mVideoEditorHelper; + + private class EventHandler extends Handler { + public EventHandler( Looper lp) + { + super(lp); + } + public void handleMessage(Message msg) + { + switch (msg.what) + { + default: + MediaFrameworkTest.testInvalidateOverlay(); + } + } + } + private EventHandler mEventHandler; + + private boolean previewStart; + private boolean previewStop; + + /* Minimum waiting time for Semaphore to wait for release */ + private final long minWaitingTime = 1000; + + // Declares the annotation for Preview Test Cases + public @interface Preview { + } + + public VideoEditorPreviewTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + + Looper looper; + if ((looper = Looper.myLooper()) != null) { + mEventHandler = new EventHandler(looper); + + } else { + //Handle error when looper can not be created. + ; + } + } + + @Override + protected void setUp() throws Exception { + // setup for each test case. + super.setUp(); + mVideoEditorHelper = new VideoEditorHelper(); + // Create a random String which will be used as project path, where all + // project related files will be stored. + final String projectPath = + mVideoEditorHelper.createRandomFile(PROJECT_LOCATION); + mVideoEditor = mVideoEditorHelper.createVideoEditor(projectPath); + } + + @Override + protected void tearDown() throws Exception { + mVideoEditorHelper.destroyVideoEditor(mVideoEditor); + // Clean the directory created as project path + mVideoEditorHelper.deleteProject(new File(mVideoEditor.getPath())); + System.gc(); + super.tearDown(); + } + + protected void setPreviewStart() { + previewStart = true; + } + protected void setPreviewStop() { + previewStop = true; + } + + protected void validatePreviewProgress(int startMs, int endMs, + boolean loop, long duration) throws Exception { + + final int[] progressUpdate = new int[100]; + final Semaphore blockTillPreviewCompletes = new Semaphore(1); + previewStart = false; + previewStop = false; + mVideoEditor.generatePreview(new MediaProcessingProgressListener() { + int i = 0; + public void onProgress(Object item, int action, int progress) { + progressUpdate[i++] = progress; + } + }); + mVideoEditorHelper.checkProgressCBValues(progressUpdate); + final SurfaceHolder surfaceHolder = + MediaFrameworkTest.mSurfaceView.getHolder(); + + long waitingTime = minWaitingTime; + if (endMs == -1) { + waitingTime += duration; + } + else { + waitingTime += (endMs - startMs); + } + blockTillPreviewCompletes.acquire(); + try { + mVideoEditor.startPreview(surfaceHolder, startMs, endMs, loop, 1, + new PreviewProgressListener() { + public void onProgress(VideoEditor videoEditor, long timeMs, + OverlayData overlayData) { + + if ( overlayData != null) { + if(overlayData.needsRendering()) { + overlayData.renderOverlay(MediaFrameworkTest.mDestBitmap); + mEventHandler.sendMessage(mEventHandler.obtainMessage(1, 2, 3)); + } + } + } + public void onStart(VideoEditor videoEditor) { + setPreviewStart(); + } + public void onStop(VideoEditor videoEditor) { + setPreviewStop(); + blockTillPreviewCompletes.release(); + } + }); + } catch (Exception e) { + blockTillPreviewCompletes.release(); + } + blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS); + + mVideoEditor.stopPreview(); + assertTrue("Preview Failed to start", previewStart); + assertTrue("Preview Failed to stop", previewStop); + + blockTillPreviewCompletes.release(); + } + + // ----------------------------------------------------------------- + // Preview + // ----------------------------------------------------------------- + + /** + *To test Preview : FULL Preview of current work (beginning till end) + */ + // TODO : remove TC_PRV_001 + @LargeTest + public void testPreviewTheStoryBoard() throws Exception { + final String videoItemFileName1 = INPUT_FILE_PATH + + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4"; + final String videoItemFileName2 = INPUT_FILE_PATH + + "MPEG4_SP_640x480_15fps_256kbps_0_30.mp4"; + final String videoItemFileName3 = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp"; + previewStart = false; + previewStop = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(0, 10000); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final MediaVideoItem mediaVideoItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem2", + videoItemFileName2, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem2); + mediaVideoItem2.setExtractBoundaries(0, 10000); + + final MediaVideoItem mediaVideoItem3 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem3", + videoItemFileName3, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem3.setExtractBoundaries(0, 10000); + + mVideoEditor.insertMediaItem(mediaVideoItem3, mediaVideoItem1.getId()); + List<MediaItem> mediaList = mVideoEditor.getAllMediaItems(); + assertEquals("Media Item 1", mediaVideoItem1, mediaList.get(0)); + assertEquals("Media Item 3", mediaVideoItem3, mediaList.get(1)); + assertEquals("Media Item 2", mediaVideoItem2, mediaList.get(2)); + + mediaVideoItem1.setRenderingMode(MediaItem.RENDERING_MODE_BLACK_BORDER); + assertEquals("Media Item 1 Rendering Mode", + MediaItem.RENDERING_MODE_BLACK_BORDER, + mediaVideoItem1.getRenderingMode()); + + mediaVideoItem2.setRenderingMode(MediaItem.RENDERING_MODE_BLACK_BORDER); + assertEquals("Media Item 2 Rendering Mode", + MediaItem.RENDERING_MODE_BLACK_BORDER, + mediaVideoItem2.getRenderingMode()); + + mediaVideoItem3.setRenderingMode(MediaItem.RENDERING_MODE_STRETCH); + assertEquals("Media Item 3 Rendering Mode", + MediaItem.RENDERING_MODE_STRETCH, + mediaVideoItem3.getRenderingMode()); + + mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_5_3); + assertEquals("Aspect Ratio", MediaProperties.ASPECT_RATIO_5_3, + mVideoEditor.getAspectRatio()); + + validatePreviewProgress(0, -1, false, mVideoEditor.getDuration()); + } + + /** + * To test Preview : Preview of start + 10 sec till end of story board + */ + // TODO : remove TC_PRV_002 + @LargeTest + public void testPreviewTheStoryBoardFromDuration() throws Exception { + final String videoItemFileName1 = INPUT_FILE_PATH + + "MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4"; + final String videoItemFileName2 = INPUT_FILE_PATH + + "MPEG4_SP_640x480_15fps_256kbps_0_30.mp4"; + final String videoItemFileName3 = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp"; + final Semaphore blockTillPreviewCompletes = new Semaphore(1); + previewStart = false; + previewStop = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(0, 10000); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final MediaVideoItem mediaVideoItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem2", + videoItemFileName2, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem2.setExtractBoundaries(0, 10000); + mVideoEditor.addMediaItem(mediaVideoItem2); + + final MediaVideoItem mediaVideoItem3 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem3", + videoItemFileName3, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem3.setExtractBoundaries(0, 10000); + + mVideoEditor.insertMediaItem(mediaVideoItem3, mediaVideoItem1.getId()); + + List<MediaItem> mediaList = mVideoEditor.getAllMediaItems(); + assertEquals("Media Item 1", mediaVideoItem1, mediaList.get(0)); + assertEquals("Media Item 3", mediaVideoItem3, mediaList.get(1)); + assertEquals("Media Item 2", mediaVideoItem2, mediaList.get(2)); + mediaVideoItem1.setRenderingMode(MediaItem.RENDERING_MODE_BLACK_BORDER); + + assertEquals("Media Item 1 Rendering Mode", + MediaItem.RENDERING_MODE_BLACK_BORDER, + mediaVideoItem1.getRenderingMode()); + mediaVideoItem2.setRenderingMode(MediaItem.RENDERING_MODE_BLACK_BORDER); + + assertEquals("Media Item 2 Rendering Mode", + MediaItem.RENDERING_MODE_BLACK_BORDER, + mediaVideoItem2.getRenderingMode()); + mediaVideoItem3.setRenderingMode(MediaItem.RENDERING_MODE_STRETCH); + + assertEquals("Media Item 3 Rendering Mode", + MediaItem.RENDERING_MODE_STRETCH, + mediaVideoItem3.getRenderingMode()); + + mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_5_3); + assertEquals("Aspect Ratio", MediaProperties.ASPECT_RATIO_5_3, + mVideoEditor.getAspectRatio()); + + validatePreviewProgress(10000, -1, false, mVideoEditor.getDuration()); + } + + /** + * To test Preview : Preview of current Effects applied + */ + // TODO : remove TC_PRV_003 + @LargeTest + public void testPreviewOfEffects() throws Exception { + final String videoItemFileName1 = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + + final Semaphore blockTillPreviewCompletes = new Semaphore(1); + previewStart = false; + previewStop = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "mediaVideoItem1", + videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final EffectColor effectNegative = + mVideoEditorHelper.createEffectItem(mediaVideoItem1, + "effectNegative", 0, 2000, EffectColor.TYPE_NEGATIVE, 0); + mediaVideoItem1.addEffect(effectNegative); + + final EffectColor effectGreen = + mVideoEditorHelper.createEffectItem(mediaVideoItem1, "effectGreen", + 2000, 3000, EffectColor.TYPE_COLOR, EffectColor.GREEN); + mediaVideoItem1.addEffect(effectGreen); + + final EffectColor effectFifties = + mVideoEditorHelper.createEffectItem(mediaVideoItem1, + "effectFifties", 5000, 4000, EffectColor.TYPE_FIFTIES, 0); + mediaVideoItem1.addEffect(effectFifties); + + List<Effect> effectList = mediaVideoItem1.getAllEffects(); + assertEquals("Effect List Size", 3, effectList.size()); + assertEquals("Effect negative", effectNegative, effectList.get(0)); + assertEquals("Effect Green", effectGreen, effectList.get(1)); + assertEquals("Effect Fifties", effectFifties, effectList.get(2)); + + mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_4_3); + assertEquals("Aspect Ratio", MediaProperties.ASPECT_RATIO_4_3, + mVideoEditor.getAspectRatio()); + + final long storyboardDuration = mVideoEditor.getDuration() ; + validatePreviewProgress(0, (int)(storyboardDuration/2), false, (storyboardDuration/2)); + + assertEquals("Removing Effect : Negative", effectNegative, + mediaVideoItem1.removeEffect(effectNegative.getId())); + + effectList = mediaVideoItem1.getAllEffects(); + + assertEquals("Effect List Size", 2, effectList.size()); + assertEquals("Effect Green", effectGreen, effectList.get(0)); + assertEquals("Effect Fifties", effectFifties, effectList.get(1)); + + validatePreviewProgress(0, -1, false, mVideoEditor.getDuration()); + } + + /** + *To test Preview : Preview of current Transitions applied (with multiple + * generatePreview) + */ + // TODO : remove TC_PRV_004 + @LargeTest + public void testPreviewWithTransition() throws Exception { + + final String videoItemFileName1 = INPUT_FILE_PATH + + "H263_profile0_176x144_10fps_96kbps_0_25.3gp"; + final String imageItemFileName1 = INPUT_FILE_PATH + + "IMG_1600x1200.jpg"; + final String videoItemFileName2 = INPUT_FILE_PATH + + "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4"; + final String maskFilename = INPUT_FILE_PATH + + "TransitionSpiral_QVGA.jpg"; + previewStart = false; + previewStop = false; + + final Semaphore blockTillPreviewCompletes = new Semaphore(1); + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(0, 10000); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + imageItemFileName1, 10000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem1); + + final MediaVideoItem mediaVideoItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m3", + videoItemFileName2, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem2.setExtractBoundaries(0, 10000); + mVideoEditor.addMediaItem(mediaVideoItem2); + + final TransitionCrossfade transition1And2CrossFade = + mVideoEditorHelper.createTCrossFade("transition_1_2_CF", + mediaVideoItem1, mediaImageItem1, 2000, + Transition.BEHAVIOR_MIDDLE_FAST); + mVideoEditor.addTransition(transition1And2CrossFade); + + final TransitionAlpha transition2And3Alpha = + mVideoEditorHelper.createTAlpha("transition_2_3", mediaImageItem1, + mediaVideoItem2, 4000, Transition.BEHAVIOR_SPEED_UP, + maskFilename, 50, true); + mVideoEditor.addTransition(transition2And3Alpha); + + final TransitionFadeBlack transition1FadeBlack = + mVideoEditorHelper.createTFadeBlack("transition_1FB", null, + mediaVideoItem1, 2000, Transition.BEHAVIOR_MIDDLE_FAST); + mVideoEditor.addTransition(transition1FadeBlack); + + List<Transition> transitionList = mVideoEditor.getAllTransitions(); + assertEquals("Transition List Size", 3, transitionList.size()); + assertEquals("Transition 1", transition1And2CrossFade, + transitionList.get(0)); + assertEquals("Transition 2", transition2And3Alpha, transitionList.get(1)); + assertEquals("Transition 3", transition1FadeBlack, transitionList.get(2)); + + mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_3_2); + + final int[] progressValues = new int[300]; + mVideoEditor.generatePreview(new MediaProcessingProgressListener() { + int i = 0; + + public void onProgress(Object item, int action, int progress) { + if (item instanceof TransitionCrossfade) { + progressValues[i] = progress; + assertEquals("Object", item, transition1And2CrossFade); + assertEquals("Action", action, + MediaProcessingProgressListener.ACTION_ENCODE); + } else if (item instanceof TransitionAlpha) { + progressValues[i] = progress; + assertEquals("Object", item, transition2And3Alpha); + assertEquals("Action", action, + MediaProcessingProgressListener.ACTION_ENCODE); + } else if (item instanceof TransitionFadeBlack) { + progressValues[i] = progress; + assertEquals("Object", item, transition1FadeBlack); + assertEquals("Action", action, + MediaProcessingProgressListener.ACTION_ENCODE); + } + i++; + } + }); + + mVideoEditorHelper.checkProgressCBValues(progressValues); + final SurfaceHolder surfaceHolder = + MediaFrameworkTest.mSurfaceView.getHolder(); + + long waitingTime = minWaitingTime + 10000; + + blockTillPreviewCompletes.acquire(); + try { + mVideoEditor.startPreview(surfaceHolder, 0, 10000, false, 1, + new PreviewProgressListener() { + public void onProgress(VideoEditor videoEditor, long timeMs, + OverlayData overlayData) { + } + public void onStart(VideoEditor videoEditor) { + setPreviewStart(); + } + public void onStop(VideoEditor videoEditor) { + setPreviewStop(); + blockTillPreviewCompletes.release(); + } + }); + } catch (Exception e) { + blockTillPreviewCompletes.release(); + } + blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS); + mVideoEditor.stopPreview(); + blockTillPreviewCompletes.release(); + assertTrue("Preview Failed to start", previewStart); + assertTrue("Preview Failed to stop", previewStop); + + assertEquals("Removing Transition " + transition1And2CrossFade.getId(), + transition1And2CrossFade, + mVideoEditor.removeTransition(transition1And2CrossFade.getId())); + transitionList = mVideoEditor.getAllTransitions(); + assertEquals("Transition List Size", 2, transitionList.size()); + assertEquals("Transition 1", transition2And3Alpha, transitionList.get(0)); + assertEquals("Transition 2", transition1FadeBlack, transitionList.get(1)); + + validatePreviewProgress(0, -1, false, mVideoEditor.getDuration()); + + + final TransitionSliding transition1And2Sliding = + mVideoEditorHelper.createTSliding("transition_1_2Sliding", + mediaVideoItem1, mediaImageItem1, 4000, + Transition.BEHAVIOR_MIDDLE_FAST, + TransitionSliding.DIRECTION_LEFT_OUT_RIGHT_IN); + mVideoEditor.addTransition(transition1And2Sliding); + + transitionList = mVideoEditor.getAllTransitions(); + assertEquals("Transition List Size", 3, transitionList.size()); + assertEquals("Transition 1", transition2And3Alpha, transitionList.get(0)); + assertEquals("Transition 2", transition1FadeBlack, transitionList.get(1)); + assertEquals("Transition 3", transition1And2Sliding, + transitionList.get(2)); + + validatePreviewProgress(5000, -1, false, (mVideoEditor.getDuration())); + + } + + /** + * To test Preview : Preview of current Overlay applied + */ + // TODO : remove TC_PRV_005 + @LargeTest + public void testPreviewWithOverlay() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp"; + final String overlayFilename1 = INPUT_FILE_PATH + + "IMG_640x480_Overlay1.png"; + final String overlayFilename2 = INPUT_FILE_PATH + + "IMG_640x480_Overlay2.png"; + final int previewFrom = 5000; + final int previewTo = 10000; + final boolean previewLoop = false; + final int previewCallbackFrameCount = 1; + final int setAspectRatio = MediaProperties.ASPECT_RATIO_4_3; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + final Semaphore blockTillPreviewCompletes = new Semaphore(1); + previewStart = false; + previewStop = false; + boolean flagForException = false; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFileName, renderingMode); + mVideoEditor.addMediaItem(mediaVideoItem); + mediaVideoItem.setExtractBoundaries(0, 10000); + + final Bitmap mBitmap1 = mVideoEditorHelper.getBitmap(overlayFilename1, + 640, 480); + final OverlayFrame overlayOnMvi1 = + mVideoEditorHelper.createOverlay(mediaVideoItem, "OverlayOnMvi1", + mBitmap1, 0, 5000); + mediaVideoItem.addOverlay(overlayOnMvi1); + + final Bitmap mBitmap2 = mVideoEditorHelper.getBitmap(overlayFilename2, + 640, 480); + final OverlayFrame overlayOnMvi2 = + mVideoEditorHelper.createOverlay(mediaVideoItem, "OverlayOnMvi2", + mBitmap2, 5000, 9000); + mediaVideoItem.addOverlay(overlayOnMvi2); + + List<Overlay> overlayList = mediaVideoItem.getAllOverlays(); + assertEquals("Overlay Size", 2, overlayList.size()); + assertEquals("Overlay 1", overlayOnMvi1, overlayList.get(0)); + assertEquals("Overlay 2", overlayOnMvi2, overlayList.get(1)); + + mVideoEditor.setAspectRatio(setAspectRatio); + + validatePreviewProgress(0 /* previewFrom */, -1, previewLoop, + mVideoEditor.getDuration()); + } + + /** + * To test Preview : Preview of current Trim applied (with default aspect + * ratio) + */ + // TODO : remove TC_PRV_006 + @LargeTest + public void testPreviewWithTrim() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_192kbps_1_5.mp4"; + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFileName, MediaItem.RENDERING_MODE_CROPPING); + final Semaphore blockTillPreviewCompletes = new Semaphore(1); + boolean flagForException = false; + previewStart = false; + previewStop = false; + mediaVideoItem.setExtractBoundaries(mediaVideoItem.getDuration() / 2, + mediaVideoItem.getDuration()); + mVideoEditor.addMediaItem(mediaVideoItem); + + validatePreviewProgress(1000, -1, false, mVideoEditor.getDuration()); + } + + /** + * To test Preview : Preview of current work having Overlay and Effect + * applied + */ + + // TODO : remove TC_PRV_007 + @LargeTest + public void testPreviewWithOverlayEffectKenBurn() throws Exception { + + final String videoItemFileName = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_192kbps_1_5.mp4"; + final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final String videoItemFileName1 = INPUT_FILE_PATH + + "MPEG4_SP_640x480_15fps_512kbps_AACLC_48khz_132kbps_s_0_26.mp4"; + final String overlayFilename = INPUT_FILE_PATH + + "IMG_640x480_Overlay1.png"; + final Semaphore blockTillPreviewCompletes = new Semaphore(1); + previewStart = false; + previewStop = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final MediaImageItem mediaImageItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + imageItemFileName, 10000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem2); + + final MediaVideoItem mediaVideoItem3 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m3", + videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem3); + + final EffectColor effectColor = + mVideoEditorHelper.createEffectItem(mediaVideoItem1, "Effect1", + 1000, 3000, EffectColor.TYPE_COLOR, EffectColor.GREEN); + mediaVideoItem1.addEffect(effectColor); + + final Rect startRect = new Rect((mediaImageItem2.getHeight() / 3), + (mediaImageItem2.getWidth() / 3), (mediaImageItem2.getHeight() / 2), + (mediaImageItem2.getWidth() / 2)); + final Rect endRect = new Rect(0, 0, mediaImageItem2.getWidth(), + mediaImageItem2.getHeight()); + + final EffectKenBurns kbeffectOnMI2 = new EffectKenBurns(mediaImageItem2, + "KBOnM2", startRect, endRect, 0, 10000); + assertNotNull("EffectKenBurns", kbeffectOnMI2); + mediaImageItem2.addEffect(kbeffectOnMI2); + + final Bitmap mBitmap = mVideoEditorHelper.getBitmap(overlayFilename, + 640, 480); + final OverlayFrame overlayFrame = + mVideoEditorHelper.createOverlay(mediaVideoItem3, "OverlayID", + mBitmap, (mediaImageItem2.getDuration() / 4), + (mediaVideoItem3.getDuration() / 3)); + mediaVideoItem3.addOverlay(overlayFrame); + + validatePreviewProgress(5000, -1, false, mVideoEditor.getDuration()); + } + + /** + *To test Preview : Export during preview + */ + // TODO : remove TC_PRV_008 + @LargeTest + public void testPreviewDuringExport() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_192kbps_1_5.mp4"; + final Semaphore blockTillPreviewCompletes = new Semaphore(1); + previewStart = false; + previewStop = false; + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(0, 20000); + mVideoEditor.addMediaItem(mediaVideoItem1); + + mVideoEditor.generatePreview(new MediaProcessingProgressListener() { + public void onProgress(Object item, int action, int progress) { + } + }); + + long waitingTime = minWaitingTime + mVideoEditor.getDuration(); + + blockTillPreviewCompletes.acquire(); + + final SurfaceHolder surfaceHolder = + MediaFrameworkTest.mSurfaceView.getHolder(); + try { + mVideoEditor.startPreview(surfaceHolder, 5000, -1, false, 1, + new PreviewProgressListener() { + final String fileName = mVideoEditor.getPath() + "\test.3gp"; + final int height = MediaProperties.HEIGHT_360; + final int bitrate = MediaProperties.BITRATE_512K; + public void onProgress(VideoEditor videoEditor, long timeMs, + OverlayData overlayData) { + if (timeMs >= 10000) + try { + videoEditor.export(fileName, height, bitrate, + new ExportProgressListener() { + public void onProgress(VideoEditor ve, + String outFileName,int progress) { + + } + }); + } catch (IOException e) { + assertTrue("UnExpected Error in Export" + + e.toString(), false); + } + } + public void onStart(VideoEditor videoEditor) { + setPreviewStart(); + } + public void onStop(VideoEditor videoEditor) { + setPreviewStop(); + blockTillPreviewCompletes.release(); + } + }); + } catch (Exception e) { + blockTillPreviewCompletes.release(); + } + + blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS); + mVideoEditor.stopPreview(); + assertTrue("Preview Failed to start", previewStart); + assertTrue("Preview Failed to stop", previewStop); + blockTillPreviewCompletes.release(); + } + + /** + * To test Preview : Preview of current Effects applied (with from time > + * total duration) + */ + // TODO : remove TC_PRV_009 + @LargeTest + public void testPreviewWithDurationGreaterThanMediaDuration() + throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_192kbps_1_5.mp4"; + final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; + boolean flagForException = false; + final Semaphore blockTillPreviewCompletes = new Semaphore(1); + + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFileName, renderingMode); + try { + mediaVideoItem1.setExtractBoundaries(0, 20000); + } catch (Exception e) { + assertTrue("Exception during setExtract Boundaries", false); + } + mVideoEditor.addMediaItem(mediaVideoItem1); + final SurfaceHolder surfaceHolder = + MediaFrameworkTest.mSurfaceView.getHolder(); + long waitingTime = minWaitingTime + (mVideoEditor.getDuration() - 30000); + if(waitingTime < 0) + { + waitingTime = minWaitingTime; + } + + blockTillPreviewCompletes.acquire(); + try { + mVideoEditor.startPreview(surfaceHolder, 30000, -1, true, 1, + new PreviewProgressListener() { + public void onProgress(VideoEditor videoEditor, long timeMs, + OverlayData overlayData) { + } + public void onStart(VideoEditor videoEditor) { + setPreviewStart(); + } + public void onStop(VideoEditor videoEditor) { + setPreviewStop(); + blockTillPreviewCompletes.release(); + } + }); + + } catch (IllegalArgumentException e) { + blockTillPreviewCompletes.release(); + flagForException = true; + } + blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS); + assertTrue("Expected Error in Preview", flagForException); + mVideoEditor.stopPreview(); + blockTillPreviewCompletes.release(); + } + + /** + * To test Preview : Preview of current Effects applied (with Render Preview + * Frame) + */ + // TODO : remove TC_PRV_010 + @LargeTest + public void testPreviewWithRenderPreviewFrame() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + final Semaphore blockTillPreviewCompletes = new Semaphore(1); + boolean flagForException = false; + OverlayData overlayData1 = new OverlayData(); + previewStart = false; + previewStop = false; + + final String overlayFilename1 = INPUT_FILE_PATH + + "IMG_640x480_Overlay1.png"; + + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, + "m1", videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem); + + final EffectColor effectPink = + mVideoEditorHelper.createEffectItem(mediaVideoItem, + "effectNegativeOnMvi", 1000, 3000, EffectColor.TYPE_COLOR, + EffectColor.PINK); + mediaVideoItem.addEffect(effectPink); + + mVideoEditor.generatePreview(new MediaProcessingProgressListener() { + public void onProgress(Object item, int action, int progress) { + } + }); + final SurfaceHolder surfaceHolder = + MediaFrameworkTest.mSurfaceView.getHolder(); + + assertEquals("Render preview Frame at 5 Sec", 5000, + mVideoEditor.renderPreviewFrame(surfaceHolder, 5000, + overlayData1)); + + assertEquals("Render preview Frame at 7 Sec", 7000, + mVideoEditor.renderPreviewFrame(surfaceHolder, 7000, + overlayData1)); + + long waitingTime = minWaitingTime + (mVideoEditor.getDuration() - 5000); + + blockTillPreviewCompletes.acquire(); + try { + mVideoEditor.startPreview(surfaceHolder, 5000, -1, false, 1, + new PreviewProgressListener() { + public void onProgress(VideoEditor videoEditor, long timeMs, + OverlayData overlayData) { + } + public void onStart(VideoEditor videoEditor) { + setPreviewStart(); + } + public void onStop(VideoEditor videoEditor) { + setPreviewStop(); + blockTillPreviewCompletes.release(); + } + }); + } catch (Exception e) { + blockTillPreviewCompletes.release(); + } + blockTillPreviewCompletes.tryAcquire(waitingTime, TimeUnit.MILLISECONDS); + mVideoEditor.stopPreview(); + assertTrue("Preview Failed to start", previewStart); + assertTrue("Preview Failed to stop", previewStop); + blockTillPreviewCompletes.release(); + } + + /** + * To test Preview : Preview of current work from selected jump location + * till end with Audio Track + */ + // TODO : remove TC_PRV_011 + @LargeTest + public void testPreviewWithEndAudioTrack() throws Exception { + final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final String videoItemFileName = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + final String imageItemFilename2 = INPUT_FILE_PATH + "IMG_640x480.jpg"; + final String audioFilename = INPUT_FILE_PATH + + "AMRNB_8KHz_12.2Kbps_m_1_17.3gp"; + + boolean flagForException = false; + previewStart = false; + previewStop = false; + final MediaImageItem mediaImageItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + imageItemFilename1, 7000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem1); + + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem.setExtractBoundaries(1000, 8000); + mVideoEditor.addMediaItem(mediaVideoItem); + + final MediaImageItem mediaImageItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m3", + imageItemFilename2, 7000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem2); + + final AudioTrack audioTrack = + mVideoEditorHelper.createAudio(mVideoEditor, "a1", audioFilename); + mVideoEditor.addAudioTrack(audioTrack); + + List<AudioTrack> audioList = mVideoEditor.getAllAudioTracks(); + assertEquals("Audio Track List size", 1, audioList.size()); + assertEquals("Audio Track", audioTrack, audioList.get(0)); + mVideoEditor.setAspectRatio(MediaProperties.ASPECT_RATIO_4_3); + + validatePreviewProgress(10000, -1, false, mVideoEditor.getDuration()); + } + + /** + * To test render Preview Frame + */ + // TODO : remove TC_PRV_012 + @LargeTest + public void testRenderPreviewFrame() throws Exception { + final String videoItemFileName1 = INPUT_FILE_PATH + + "H264_BP_1080x720_30fps_800kbps_1_17.mp4"; + final String videoItemFileName2 = INPUT_FILE_PATH + + "MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4"; + final String videoItemFileName3 = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + final String imageItemFilename1 = INPUT_FILE_PATH + + "IMG_1600x1200.jpg"; + final String imageItemFilename2 = INPUT_FILE_PATH + + "IMG_176x144.jpg"; + final String audioFilename = INPUT_FILE_PATH + + "AMRNB_8KHz_12.2Kbps_m_1_17.3gp"; + OverlayData overlayData1 = new OverlayData(); + previewStart = false; + previewStop = false; + final MediaVideoItem mediaVideoItem1 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFileName1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(0, 10000); + mVideoEditor.addMediaItem(mediaVideoItem1); + + final MediaVideoItem mediaVideoItem2 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + videoItemFileName2, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(mediaVideoItem2.getDuration() / 4, + mediaVideoItem2.getDuration() / 2); + mVideoEditor.addMediaItem(mediaVideoItem2); + + final MediaVideoItem mediaVideoItem3 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m3", + videoItemFileName3, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem1.setExtractBoundaries(mediaVideoItem2.getDuration() / 2, + mediaVideoItem2.getDuration()); + mVideoEditor.addMediaItem(mediaVideoItem3); + + final MediaImageItem mediaImageItem4 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m4", + imageItemFilename1, 5000, MediaItem.RENDERING_MODE_BLACK_BORDER); + + final MediaImageItem mediaImageItem5 = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m5", + imageItemFilename2, 5000, MediaItem.RENDERING_MODE_BLACK_BORDER); + + List<MediaItem> mediaList = mVideoEditor.getAllMediaItems(); + assertEquals("Media Item List Size", 3, mediaList.size()); + + mVideoEditor.insertMediaItem(mediaImageItem4, mediaVideoItem2.getId()); + mediaList = mVideoEditor.getAllMediaItems(); + assertEquals("Media Item List Size", 4, mediaList.size()); + assertEquals("Media item 1", mediaVideoItem1, mediaList.get(0)); + assertEquals("Media item 2", mediaVideoItem2, mediaList.get(1)); + assertEquals("Media item 4", mediaImageItem4, mediaList.get(2)); + assertEquals("Media item 3", mediaVideoItem3, mediaList.get(3)); + + mVideoEditor.insertMediaItem(mediaImageItem5, mediaImageItem4.getId()); + mediaList = mVideoEditor.getAllMediaItems(); + assertEquals("Media Item List Size", 5, mediaList.size()); + assertEquals("Media item 1", mediaVideoItem1, mediaList.get(0)); + assertEquals("Media item 2", mediaVideoItem2, mediaList.get(1)); + assertEquals("Media item 4", mediaImageItem4, mediaList.get(2)); + assertEquals("Media item 5", mediaImageItem5, mediaList.get(3)); + assertEquals("Media item 3", mediaVideoItem3, mediaList.get(4)); + + mVideoEditor.moveMediaItem(mediaVideoItem1.getId(), + mediaImageItem5.getId()); + mediaList = mVideoEditor.getAllMediaItems(); + assertEquals("Media Item List Size", 5, mediaList.size()); + assertEquals("Media item 2", mediaVideoItem2, mediaList.get(0)); + assertEquals("Media item 4", mediaImageItem4, mediaList.get(1)); + assertEquals("Media item 5", mediaImageItem5, mediaList.get(2)); + assertEquals("Media item 1", mediaVideoItem1, mediaList.get(3)); + assertEquals("Media item 3", mediaVideoItem3, mediaList.get(4)); + + final TransitionCrossfade transition2And4CrossFade = + mVideoEditorHelper.createTCrossFade("transition2And4CrossFade", + mediaVideoItem2, mediaImageItem4, 2000, + Transition.BEHAVIOR_MIDDLE_FAST); + mVideoEditor.addTransition(transition2And4CrossFade); + + final TransitionCrossfade transition1And3CrossFade = + mVideoEditorHelper.createTCrossFade("transition1And3CrossFade", + mediaVideoItem1, mediaVideoItem3, 5000, + Transition.BEHAVIOR_MIDDLE_FAST); + mVideoEditor.addTransition(transition1And3CrossFade); + + final AudioTrack audioTrack = + mVideoEditorHelper.createAudio(mVideoEditor, "a1", audioFilename); + audioTrack.setExtractBoundaries(0, 2000); + mVideoEditor.addAudioTrack(audioTrack); + + audioTrack.enableLoop(); + + mVideoEditor.generatePreview(new MediaProcessingProgressListener() { + public void onProgress(Object item, int action, int progress) { + } + }); + + final SurfaceHolder surfaceHolder = + MediaFrameworkTest.mSurfaceView.getHolder(); + + mVideoEditor.renderPreviewFrame(surfaceHolder, mVideoEditor.getDuration()/4, overlayData1); + Thread.sleep(1000); + mVideoEditor.renderPreviewFrame(surfaceHolder, mVideoEditor.getDuration()/2, overlayData1); + Thread.sleep(1000); + mVideoEditor.renderPreviewFrame(surfaceHolder, mVideoEditor.getDuration(), overlayData1); + + } + + /** + * To Test Preview : Without any Media Items in the story Board + */ + // TODO : remove TC_PRV_013 + @LargeTest + public void testStartPreviewWithoutMediaItems() throws Exception { + boolean flagForException = false; + + final SurfaceHolder surfaceHolder = + MediaFrameworkTest.mSurfaceView.getHolder(); + try{ + mVideoEditor.startPreview(surfaceHolder, 0, -1, false, 1, + new PreviewProgressListener() { + public void onProgress(VideoEditor videoEditor, long timeMs, + OverlayData overlayData) { + } + public void onStart(VideoEditor videoEditor) { + setPreviewStart(); + } + public void onStop(VideoEditor videoEditor) { + setPreviewStop(); + } + }); + }catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Preview without Media Items", flagForException); + } + + /** + * To Test Preview : Add Media and Remove Media Item (Without any Media + * Items in the story Board) + */ + // TODO : remove TC_PRV_014 + @LargeTest + public void testStartPreviewAddRemoveMediaItems() throws Exception { + final String videoItemFilename1 = INPUT_FILE_PATH + + "H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp"; + final String imageItemFilename1 = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; + final String alphaFilename = INPUT_FILE_PATH + + "TransitionSpiral_QVGA.jpg"; + boolean flagForException = false; + + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m1", + videoItemFilename1, MediaItem.RENDERING_MODE_BLACK_BORDER); + mediaVideoItem.setExtractBoundaries(0, 15000); + mVideoEditor.addMediaItem(mediaVideoItem); + + final MediaImageItem mediaImageItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, "m2", + imageItemFilename1, 15000, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaImageItem); + + final TransitionAlpha transition1And2 = + mVideoEditorHelper.createTAlpha("transition", mediaVideoItem, + mediaImageItem, 3000, Transition.BEHAVIOR_SPEED_UP, + alphaFilename, 10, false); + mVideoEditor.addTransition(transition1And2); + + final EffectColor effectColor = + mVideoEditorHelper.createEffectItem(mediaImageItem, "effect", 5000, + 3000, EffectColor.TYPE_COLOR, EffectColor.PINK); + mediaImageItem.addEffect(effectColor); + + assertEquals("removing Media item 1", mediaVideoItem, + mVideoEditor.removeMediaItem(mediaVideoItem.getId())); + assertEquals("removing Media item 2", mediaImageItem, + mVideoEditor.removeMediaItem(mediaImageItem.getId())); + + try{ + mVideoEditor.generatePreview(new MediaProcessingProgressListener() { + public void onProgress(Object item, int action, int progress) { + } + }); + final SurfaceHolder surfaceHolder = + MediaFrameworkTest.mSurfaceView.getHolder(); + mVideoEditor.startPreview(surfaceHolder, 0, -1, false, 1, + new PreviewProgressListener() { + public void onProgress(VideoEditor videoEditor, long timeMs, + OverlayData overlayData) { + } + public void onStart(VideoEditor videoEditor) { + setPreviewStart(); + } + public void onStop(VideoEditor videoEditor) { + setPreviewStop(); + } + }); + }catch (IllegalArgumentException e) { + flagForException = true; + } + assertTrue("Preview with removed Media Items", flagForException); + + } + + /** + * To test Preview : Preview of current Effects applied (with Render Preview + * Frame) + */ + // TODO : remove TC_PRV_015 + @LargeTest + public void testPreviewWithRenderPreviewFrameWithoutGenerate() throws Exception { + final String videoItemFileName = INPUT_FILE_PATH + + "H264_BP_640x480_30fps_256kbps_1_17.mp4"; + boolean flagForException = false; + long duration = 0; + OverlayData overlayData1 = new OverlayData(); + + final MediaVideoItem mediaVideoItem = + mVideoEditorHelper.createMediaItem(mVideoEditor, + "m1", videoItemFileName, MediaItem.RENDERING_MODE_BLACK_BORDER); + mVideoEditor.addMediaItem(mediaVideoItem); + + final SurfaceHolder surfaceHolder = + MediaFrameworkTest.mSurfaceView.getHolder(); + duration = mVideoEditor.getDuration(); + /* RenderPreviewFrame returns -1 to indicate last frame */ + try { + assertEquals("Render preview Frame at item duration", -1, + mVideoEditor.renderPreviewFrame(surfaceHolder, duration, + overlayData1)); + } catch ( Exception e) { + assertTrue (" Render Preview Frame without generate", false); + } + duration = mVideoEditor.getDuration() + 1000; + try { + mVideoEditor.renderPreviewFrame(surfaceHolder, duration, + overlayData1); + } catch ( IllegalStateException e) { + flagForException = true; + } + assertTrue (" Preview time greater than duration", flagForException); + } + +} diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java index 95b7386531f3..b2086d6d4e86 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java @@ -18,6 +18,9 @@ package com.android.mediaframeworktest.stress; import com.android.mediaframeworktest.MediaFrameworkTest; +import android.app.Activity; +import android.app.Instrumentation; +import android.content.Intent; import android.hardware.Camera; import android.media.MediaPlayer; import android.media.MediaRecorder; @@ -27,121 +30,122 @@ import android.util.Log; import android.view.SurfaceHolder; import com.android.mediaframeworktest.MediaNames; +import com.android.mediaframeworktest.functional.CodecTest; -import java.util.Random; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.Writer; + +import android.test.AndroidTestCase; +import android.test.InstrumentationTestCase; /** * Junit / Instrumentation test case for the media player */ -public class MediaPlayerStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { +public class MediaPlayerStressTest extends InstrumentationTestCase { private String TAG = "MediaPlayerStressTest"; - private MediaRecorder mRecorder; - private Camera mCamera; - - private static final int NUMBER_OF_RANDOM_REPOSITION_AND_PLAY = 10; - private static final int NUMBER_OF_RANDOM_REPOSITION_AND_PLAY_SHORT = 5; - private static final int NUMBER_OF_STRESS_LOOPS = 500; - private static final int PLAYBACK_END_TOLERANCE = 30000; - private static final int WAIT_UNTIL_PLAYBACK_FINISH = 515000 ; public MediaPlayerStressTest() { - super("com.android.mediaframeworktest", MediaFrameworkTest.class); } protected void setUp() throws Exception { - getActivity(); super.setUp(); } - @LargeTest - public void testStressHWDecoderRelease() throws Exception { - SurfaceHolder mSurfaceHolder; - long randomseed = System.currentTimeMillis(); - Random generator = new Random(randomseed); - Log.v(TAG, "Random seed: " + randomseed); - int video_duration = MediaNames.STREAM_H264_480_360_1411k_DURATION; - int random_play_time; - - mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); - try { - //assertTrue(MediaFrameworkTest.checkStreamingServer()); - for (int i = 0; i < NUMBER_OF_STRESS_LOOPS; i++) { - MediaPlayer mp = new MediaPlayer(); - mp.setDataSource(MediaNames.STREAM_H264_480_360_1411k); - mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder()); - mp.prepare(); - mp.start(); - // seek and play - for (int j = 0; j < generator.nextInt(10); j++) { - random_play_time = - generator.nextInt(MediaNames.STREAM_H264_480_360_1411k_DURATION / 2); - Log.v(TAG, "Play time = " + random_play_time); - Thread.sleep(random_play_time); - int seek_time = MediaNames.STREAM_H264_480_360_1411k_DURATION / 2; - Log.v(TAG, "Seek time = " + seek_time); - mp.seekTo(seek_time); - } - mp.release(); - } + private int mTotalPlaybackError = 0; + private int mTotalComplete = 0; + private int mTotalInfoUnknown = 0; + private int mTotalVideoTrackLagging = 0; + private int mTotalBadInterleaving = 0; + private int mTotalNotSeekable = 0; + private int mTotalMetaDataUpdate = 0; + + private void writeTestOutput(String filename, Writer output) throws Exception{ + output.write("File Name: " + filename); + output.write(" Complete: " + CodecTest.onCompleteSuccess); + output.write(" Error: " + CodecTest.mPlaybackError); + output.write(" Unknown Info: " + CodecTest.mMediaInfoUnknownCount); + output.write(" Track Lagging: " + CodecTest.mMediaInfoVideoTrackLaggingCount); + output.write(" Bad Interleaving: " + CodecTest.mMediaInfoBadInterleavingCount); + output.write(" Not Seekable: " + CodecTest.mMediaInfoNotSeekableCount); + output.write(" Info Meta data update: " + CodecTest.mMediaInfoMetdataUpdateCount); + output.write("\n"); + } + + private void writeTestSummary(Writer output) throws Exception{ + output.write("Total Result:\n"); + output.write("Total Complete: " + mTotalComplete + "\n"); + output.write("Total Error: " + mTotalPlaybackError + "\n"); + output.write("Total Unknown Info: " + mTotalInfoUnknown + "\n"); + output.write("Total Track Lagging: " + mTotalVideoTrackLagging + "\n" ); + output.write("Total Bad Interleaving: " + mTotalBadInterleaving + "\n"); + output.write("Total Not Seekable: " + mTotalNotSeekable + "\n"); + output.write("Total Info Meta data update: " + mTotalMetaDataUpdate + "\n"); + output.write("\n"); + } - } catch (Exception e) { - Log.v(TAG, e.toString()); - assertTrue("testStressHWDecoderRelease", false); + private void updateTestResult(){ + if (CodecTest.onCompleteSuccess){ + mTotalComplete++; + } + else if (CodecTest.mPlaybackError){ + mTotalPlaybackError++; } + mTotalInfoUnknown += CodecTest.mMediaInfoUnknownCount; + mTotalVideoTrackLagging += CodecTest.mMediaInfoVideoTrackLaggingCount; + mTotalBadInterleaving += CodecTest.mMediaInfoBadInterleavingCount; + mTotalNotSeekable += CodecTest.mMediaInfoNotSeekableCount; + mTotalMetaDataUpdate += CodecTest.mMediaInfoMetdataUpdateCount; } + //Test that will start the playback for all the videos + //under the samples folder @LargeTest - public void testStressGetCurrentPosition() throws Exception { - SurfaceHolder mSurfaceHolder; - long randomseed = System.currentTimeMillis(); - Random generator = new Random(randomseed); - Log.v(TAG, "Random seed: " + randomseed); - int video_duration = MediaNames.VIDEO_H263_AMR_DURATION; - int random_play_time = 0; - int random_seek_time = 0; - int random_no_of_seek = 0; - - mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); - try { - for (int i = 0; i < NUMBER_OF_STRESS_LOOPS; i++) { - MediaPlayer mp = new MediaPlayer(); - mp.setDataSource(MediaNames.VIDEO_H263_AMR); - mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder()); - mp.prepare(); - mp.start(); - random_no_of_seek = generator.nextInt(10); - // make sure the seek at least run once. - if (random_no_of_seek == 0) { - random_no_of_seek = 1; - } - Log.v(TAG, "random_seek = " + random_no_of_seek); - // Play for 10 seconds then random seekTo - for (int j = 0; j < random_no_of_seek; j++) { - random_play_time = - generator.nextInt(video_duration / 100); - Log.v(TAG, "Play time = " + random_play_time); - Thread.sleep(random_play_time); - random_seek_time = - generator.nextInt(video_duration / 2); - Log.v(TAG, "Seek time = " + random_seek_time); - mp.seekTo(random_seek_time); - } - //Seek to 10s from the end of the video - mp.seekTo(video_duration - 10000); - //After reposition, play 30 seconds the video should be finished. - Thread.sleep(PLAYBACK_END_TOLERANCE); - Log.v(TAG, "CurrentPosition = " + mp.getCurrentPosition()); - if ( mp.isPlaying() || mp.getCurrentPosition() - > (video_duration)){ - assertTrue("Current PlayTime greater than duration", false); + public void testVideoPlayback() throws Exception { + String fileWithError = "Filename:\n"; + File playbackOutput = new File("/sdcard/PlaybackTestResult.txt"); + Writer output = new BufferedWriter(new FileWriter(playbackOutput, true)); + + boolean testResult = true; + // load directory files + boolean onCompleteSuccess = false; + File dir = new File(MediaNames.MEDIA_SAMPLE_POOL); + + Instrumentation inst = getInstrumentation(); + Intent intent = new Intent(); + + intent.setClass(getInstrumentation().getTargetContext(), MediaFrameworkTest.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + String[] children = dir.list(); + if (children == null) { + Log.v("MediaPlayerApiTest:testMediaSamples", "dir is empty"); + return; + } else { + for (int i = 0; i < children.length; i++) { + Activity act = inst.startActivitySync(intent); + //Get filename of directory + String filename = children[i]; + onCompleteSuccess = + CodecTest.playMediaSamples(dir + "/" + filename); + if (!onCompleteSuccess){ + //Don't fail the test right away, print out the failure file. + fileWithError += filename + '\n'; + Log.v(TAG, "Failure File : " + fileWithError); + testResult = false; } - mp.release(); + Thread.sleep(3000); + //Call onCreat to recreate the surface + act.finish(); + //Write test result to an output file + writeTestOutput(filename,output); + //Get the summary + updateTestResult(); } - - } catch (Exception e) { - Log.v(TAG, e.toString()); - assertTrue("testStressGetCurrentPosition", false); - } + writeTestSummary(output); + output.close(); + assertTrue("testMediaSamples", testResult); + } } -} - +}
\ No newline at end of file diff --git a/media/tests/contents/media_api/video/H263_500_AMRNB_12.3gp b/media/tests/contents/media_api/video/H263_500_AMRNB_12.3gp Binary files differnew file mode 100755 index 000000000000..46bb2b1511de --- /dev/null +++ b/media/tests/contents/media_api/video/H263_500_AMRNB_12.3gp diff --git a/media/tests/contents/media_api/video/H263_56_AAC_24.3gp b/media/tests/contents/media_api/video/H263_56_AAC_24.3gp Binary files differnew file mode 100755 index 000000000000..1fb11925fee8 --- /dev/null +++ b/media/tests/contents/media_api/video/H263_56_AAC_24.3gp diff --git a/media/tests/contents/media_api/video/H263_56_AMRNB_6.3gp b/media/tests/contents/media_api/video/H263_56_AMRNB_6.3gp Binary files differnew file mode 100755 index 000000000000..b6eb6a18982f --- /dev/null +++ b/media/tests/contents/media_api/video/H263_56_AMRNB_6.3gp diff --git a/media/tests/contents/media_api/video/H264_320_AAC_64.3gp b/media/tests/contents/media_api/video/H264_320_AAC_64.3gp Binary files differnew file mode 100755 index 000000000000..04680ce3c2be --- /dev/null +++ b/media/tests/contents/media_api/video/H264_320_AAC_64.3gp diff --git a/media/tests/contents/media_api/video/H264_320_AMRNB_6.3gp b/media/tests/contents/media_api/video/H264_320_AMRNB_6.3gp Binary files differnew file mode 100755 index 000000000000..bc533a221958 --- /dev/null +++ b/media/tests/contents/media_api/video/H264_320_AMRNB_6.3gp diff --git a/media/tests/contents/media_api/video/H264_500_AAC_128.3gp b/media/tests/contents/media_api/video/H264_500_AAC_128.3gp Binary files differnew file mode 100755 index 000000000000..05d67eaeed92 --- /dev/null +++ b/media/tests/contents/media_api/video/H264_500_AAC_128.3gp diff --git a/media/tests/contents/media_api/video/H264_HVGA_500_NO_AUDIO.3gp b/media/tests/contents/media_api/video/H264_HVGA_500_NO_AUDIO.3gp Binary files differnew file mode 100755 index 000000000000..13642b25ef1e --- /dev/null +++ b/media/tests/contents/media_api/video/H264_HVGA_500_NO_AUDIO.3gp diff --git a/media/tests/contents/media_api/video/H264_QVGA_500_NO_AUDIO.3gp b/media/tests/contents/media_api/video/H264_QVGA_500_NO_AUDIO.3gp Binary files differnew file mode 100755 index 000000000000..13642b25ef1e --- /dev/null +++ b/media/tests/contents/media_api/video/H264_QVGA_500_NO_AUDIO.3gp diff --git a/media/tests/contents/media_api/video/MPEG4_320_AAC_64.mp4 b/media/tests/contents/media_api/video/MPEG4_320_AAC_64.mp4 Binary files differnew file mode 100755 index 000000000000..90f185606d0f --- /dev/null +++ b/media/tests/contents/media_api/video/MPEG4_320_AAC_64.mp4 diff --git a/media/tests/contents/media_api/video/border_large.3gp b/media/tests/contents/media_api/video/border_large.3gp Binary files differnew file mode 100755 index 000000000000..e6221604bda4 --- /dev/null +++ b/media/tests/contents/media_api/video/border_large.3gp diff --git a/media/tests/contents/media_api/videoeditor/AACLC_44.1kHz_256kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/AACLC_44.1kHz_256kbps_s_1_17.mp4 Binary files differnew file mode 100644 index 000000000000..32d422129b74 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/AACLC_44.1kHz_256kbps_s_1_17.mp4 diff --git a/media/tests/contents/media_api/videoeditor/AACLC_48KHz_256Kbps_s_1_17.3gp b/media/tests/contents/media_api/videoeditor/AACLC_48KHz_256Kbps_s_1_17.3gp Binary files differnew file mode 100644 index 000000000000..f911cd3874cc --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/AACLC_48KHz_256Kbps_s_1_17.3gp diff --git a/media/tests/contents/media_api/videoeditor/AMRNB_8KHz_12.2Kbps_m_1_17.3gp b/media/tests/contents/media_api/videoeditor/AMRNB_8KHz_12.2Kbps_m_1_17.3gp Binary files differnew file mode 100644 index 000000000000..f6fccef06867 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/AMRNB_8KHz_12.2Kbps_m_1_17.3gp diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_256kbps_0_25.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_256kbps_0_25.3gp Binary files differnew file mode 100644 index 000000000000..593166b2f301 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_256kbps_0_25.3gp diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_256kbps_1_17.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_256kbps_1_17.3gp Binary files differnew file mode 100644 index 000000000000..0138d8043ca2 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_256kbps_1_17.3gp diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_96kbps_0_25.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_96kbps_0_25.3gp Binary files differnew file mode 100644 index 000000000000..08d97d543e80 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_96kbps_0_25.3gp diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_128kbps_1_35.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_128kbps_1_35.3gp Binary files differnew file mode 100644 index 000000000000..b73be034dc90 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_128kbps_1_35.3gp diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_16kHz_32kbps_m_0_26.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_16kHz_32kbps_m_0_26.3gp Binary files differnew file mode 100644 index 000000000000..4bcb3b59d728 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_16kHz_32kbps_m_0_26.3gp diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp Binary files differnew file mode 100644 index 000000000000..0629f386570f --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp Binary files differnew file mode 100644 index 000000000000..c5cd1296bc0a --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_1080x720_30fps_12Mbps_AACLC_44.1khz_64kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_1080x720_30fps_12Mbps_AACLC_44.1khz_64kbps_s_1_17.mp4 Binary files differnew file mode 100644 index 000000000000..8486f5515b7d --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_1080x720_30fps_12Mbps_AACLC_44.1khz_64kbps_s_1_17.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_1080x720_30fps_800kbps_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_1080x720_30fps_800kbps_1_17.mp4 Binary files differnew file mode 100644 index 000000000000..217305537f5a --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_1080x720_30fps_800kbps_1_17.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_1280x1080_30fps_1200Kbps_1_10.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_1280x1080_30fps_1200Kbps_1_10.mp4 Binary files differnew file mode 100644 index 000000000000..27eab58f6d41 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_1280x1080_30fps_1200Kbps_1_10.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_1280x720_15fps_512kbps_AACLC_16khz_48kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_1280x720_15fps_512kbps_AACLC_16khz_48kbps_s_1_17.mp4 Binary files differnew file mode 100644 index 000000000000..457dd96eaa7f --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_1280x720_15fps_512kbps_AACLC_16khz_48kbps_s_1_17.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_176x144_15fps_144kbps_AMRNB_8kHz_12.2kbps_m_1_17.3gp b/media/tests/contents/media_api/videoeditor/H264_BP_176x144_15fps_144kbps_AMRNB_8kHz_12.2kbps_m_1_17.3gp Binary files differnew file mode 100644 index 000000000000..dae2062df477 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_176x144_15fps_144kbps_AMRNB_8kHz_12.2kbps_m_1_17.3gp diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_1920x1080_30fps_1200Kbps_1_10.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_1920x1080_30fps_1200Kbps_1_10.mp4 Binary files differnew file mode 100644 index 000000000000..c66ccedced56 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_1920x1080_30fps_1200Kbps_1_10.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4 Binary files differnew file mode 100644 index 000000000000..e026fa27607d --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_32kbps_m_1_17.3gp b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_32kbps_m_1_17.3gp Binary files differnew file mode 100644 index 000000000000..f9e7306c1b97 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_32kbps_m_1_17.3gp diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp Binary files differnew file mode 100644 index 000000000000..f9e7306c1b97 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_384kbps_60_0.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_384kbps_60_0.mp4 Binary files differnew file mode 100644 index 000000000000..05224ea07fca --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_384kbps_60_0.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_30fps_192kbps_1_5.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_30fps_192kbps_1_5.mp4 Binary files differnew file mode 100644 index 000000000000..6ac0480bb46f --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_30fps_192kbps_1_5.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_30fps_256kbps_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_30fps_256kbps_1_17.mp4 Binary files differnew file mode 100644 index 000000000000..d589bfbfe7d4 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_30fps_256kbps_1_17.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_720x480_25fps_256kbps_AMRNB_8khz_12.2kbps_m_0_26.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_720x480_25fps_256kbps_AMRNB_8khz_12.2kbps_m_0_26.mp4 Binary files differnew file mode 100644 index 000000000000..6bfbe8bdd62b --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_720x480_25fps_256kbps_AMRNB_8khz_12.2kbps_m_0_26.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_1_17.mp4 Binary files differnew file mode 100644 index 000000000000..4998ccc986a5 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_1_17.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4 Binary files differnew file mode 100644 index 000000000000..6809e7f61c46 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_AMRNB_8KHz_12.2Kbps_m_0_26.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_AMRNB_8KHz_12.2Kbps_m_0_26.mp4 Binary files differnew file mode 100644 index 000000000000..74ae62a06345 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_AMRNB_8KHz_12.2Kbps_m_0_26.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 Binary files differnew file mode 100755 index 000000000000..be050dc2a006 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 diff --git a/media/tests/contents/media_api/videoeditor/H264_MP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_MP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 Binary files differnew file mode 100644 index 000000000000..178431d4dac6 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/H264_MP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 diff --git a/media/tests/contents/media_api/videoeditor/IMG_1600x1200.jpg b/media/tests/contents/media_api/videoeditor/IMG_1600x1200.jpg Binary files differnew file mode 100644 index 000000000000..b09cb147ad94 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/IMG_1600x1200.jpg diff --git a/media/tests/contents/media_api/videoeditor/IMG_176x144.jpg b/media/tests/contents/media_api/videoeditor/IMG_176x144.jpg Binary files differnew file mode 100644 index 000000000000..97a7ba5c32f7 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/IMG_176x144.jpg diff --git a/media/tests/contents/media_api/videoeditor/IMG_176x144_Overlay1.png b/media/tests/contents/media_api/videoeditor/IMG_176x144_Overlay1.png Binary files differnew file mode 100644 index 000000000000..147a92521278 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/IMG_176x144_Overlay1.png diff --git a/media/tests/contents/media_api/videoeditor/IMG_176x144_Overlay2.png b/media/tests/contents/media_api/videoeditor/IMG_176x144_Overlay2.png Binary files differnew file mode 100644 index 000000000000..ba206262f5ea --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/IMG_176x144_Overlay2.png diff --git a/media/tests/contents/media_api/videoeditor/IMG_320x240.jpg b/media/tests/contents/media_api/videoeditor/IMG_320x240.jpg Binary files differnew file mode 100644 index 000000000000..ec5b5bf96ee1 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/IMG_320x240.jpg diff --git a/media/tests/contents/media_api/videoeditor/IMG_640x480.gif b/media/tests/contents/media_api/videoeditor/IMG_640x480.gif Binary files differnew file mode 100644 index 000000000000..19548df8fc77 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/IMG_640x480.gif diff --git a/media/tests/contents/media_api/videoeditor/IMG_640x480.jpg b/media/tests/contents/media_api/videoeditor/IMG_640x480.jpg Binary files differnew file mode 100644 index 000000000000..c6a96b1e4701 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/IMG_640x480.jpg diff --git a/media/tests/contents/media_api/videoeditor/IMG_640x480.png b/media/tests/contents/media_api/videoeditor/IMG_640x480.png Binary files differnew file mode 100644 index 000000000000..ba206262f5ea --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/IMG_640x480.png diff --git a/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay1.png b/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay1.png Binary files differnew file mode 100644 index 000000000000..ba206262f5ea --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay1.png diff --git a/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay2.png b/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay2.png Binary files differnew file mode 100755 index 000000000000..0f32131a632e --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay2.png diff --git a/media/tests/contents/media_api/videoeditor/MP3_48KHz_128kbps_s_1_17.mp3 b/media/tests/contents/media_api/videoeditor/MP3_48KHz_128kbps_s_1_17.mp3 Binary files differnew file mode 100644 index 000000000000..e0d6a1799b3b --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MP3_48KHz_128kbps_s_1_17.mp3 diff --git a/media/tests/contents/media_api/videoeditor/MPEG2_640x480_30fps_192kbps_1_5.mp4 b/media/tests/contents/media_api/videoeditor/MPEG2_640x480_30fps_192kbps_1_5.mp4 Binary files differnew file mode 100644 index 000000000000..22a92b2f8d48 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MPEG2_640x480_30fps_192kbps_1_5.mp4 diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_12fps_92kbps_AMRNB_8KHz_12.2kbps_m_0_27.3gp b/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_12fps_92kbps_AMRNB_8KHz_12.2kbps_m_0_27.3gp Binary files differnew file mode 100644 index 000000000000..a73c4821fb46 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_12fps_92kbps_AMRNB_8KHz_12.2kbps_m_0_27.3gp diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp b/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp Binary files differnew file mode 100644 index 000000000000..333b88039a7d --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp Binary files differnew file mode 100644 index 000000000000..75a0036368bf --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.mp4 Binary files differnew file mode 100644 index 000000000000..75a0036368bf --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.mp4 diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_256kbps_0_30.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_256kbps_0_30.mp4 Binary files differnew file mode 100644 index 000000000000..be15e907d5ab --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_256kbps_0_30.mp4 diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_512kbps_AACLC_48khz_132kbps_s_0_26.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_512kbps_AACLC_48khz_132kbps_s_0_26.mp4 Binary files differnew file mode 100644 index 000000000000..d165d685cb6b --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_512kbps_AACLC_48khz_132kbps_s_0_26.mp4 diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_30fps_512Kbps_0_23.3gp b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_30fps_512Kbps_0_23.3gp Binary files differnew file mode 100644 index 000000000000..c12f2c86097a --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_30fps_512Kbps_0_23.3gp diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_30fps_512Kbps_0_27.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_30fps_512Kbps_0_27.mp4 Binary files differnew file mode 100644 index 000000000000..13ad5dbc5141 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_30fps_512Kbps_0_27.mp4 diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_161kbps_s_0_26.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_161kbps_s_0_26.mp4 Binary files differnew file mode 100644 index 000000000000..8b72c8437d3c --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_161kbps_s_0_26.mp4 diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4 Binary files differnew file mode 100644 index 000000000000..8752fc563640 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4 diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4 Binary files differnew file mode 100644 index 000000000000..829af3504286 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4 diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_854x480_15fps_256kbps_AACLC_16khz_48kbps_s_0_26.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_854x480_15fps_256kbps_AACLC_16khz_48kbps_s_0_26.mp4 Binary files differnew file mode 100644 index 000000000000..8b60f433beb3 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/MPEG4_SP_854x480_15fps_256kbps_AACLC_16khz_48kbps_s_0_26.mp4 diff --git a/media/tests/contents/media_api/videoeditor/Text_FileRenamedTo3gp.3gp b/media/tests/contents/media_api/videoeditor/Text_FileRenamedTo3gp.3gp new file mode 100644 index 000000000000..02103c6d36e7 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/Text_FileRenamedTo3gp.3gp @@ -0,0 +1 @@ +This is a text file
\ No newline at end of file diff --git a/media/tests/contents/media_api/videoeditor/TransitionSpiral_QVGA.jpg b/media/tests/contents/media_api/videoeditor/TransitionSpiral_QVGA.jpg Binary files differnew file mode 100644 index 000000000000..0863df9eb547 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/TransitionSpiral_QVGA.jpg diff --git a/media/tests/contents/media_api/videoeditor/corrupted_H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4 b/media/tests/contents/media_api/videoeditor/corrupted_H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4 Binary files differnew file mode 100644 index 000000000000..31627c7a8dd5 --- /dev/null +++ b/media/tests/contents/media_api/videoeditor/corrupted_H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4 diff --git a/native/android/input.cpp b/native/android/input.cpp index a96240c10e81..ed2666764472 100644 --- a/native/android/input.cpp +++ b/native/android/input.cpp @@ -172,6 +172,11 @@ float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointe return static_cast<const MotionEvent*>(motion_event)->getOrientation(pointer_index); } +float AMotionEvent_getAxisValue(const AInputEvent* motion_event, + int32_t axis, size_t pointer_index) { + return static_cast<const MotionEvent*>(motion_event)->getAxisValue(axis, pointer_index); +} + size_t AMotionEvent_getHistorySize(const AInputEvent* motion_event) { return static_cast<const MotionEvent*>(motion_event)->getHistorySize(); } @@ -248,6 +253,12 @@ float AMotionEvent_getHistoricalOrientation(AInputEvent* motion_event, size_t po pointer_index, history_index); } +float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event, + int32_t axis, size_t pointer_index, size_t history_index) { + return static_cast<const MotionEvent*>(motion_event)->getHistoricalAxisValue( + axis, pointer_index, history_index); +} + void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper, int ident, ALooper_callbackFunc callback, void* data) { diff --git a/native/include/android/input.h b/native/include/android/input.h index e196686ea37d..ee05020913b8 100644 --- a/native/include/android/input.h +++ b/native/include/android/input.h @@ -321,6 +321,21 @@ enum { }; /* + * Constants that identify each individual axis of a motion event. + */ +enum { + AMOTION_EVENT_AXIS_X = 0, + AMOTION_EVENT_AXIS_Y = 1, + AMOTION_EVENT_AXIS_PRESSURE = 2, + AMOTION_EVENT_AXIS_SIZE = 3, + AMOTION_EVENT_AXIS_TOUCH_MAJOR = 4, + AMOTION_EVENT_AXIS_TOUCH_MINOR = 5, + AMOTION_EVENT_AXIS_TOOL_MAJOR = 6, + AMOTION_EVENT_AXIS_TOOL_MINOR = 7, + AMOTION_EVENT_AXIS_ORIENTATION = 8, +}; + +/* * Input sources. * * Refer to the documentation on android.view.InputDevice for more details about input sources @@ -333,6 +348,7 @@ enum { AINPUT_SOURCE_CLASS_POINTER = 0x00000002, AINPUT_SOURCE_CLASS_NAVIGATION = 0x00000004, AINPUT_SOURCE_CLASS_POSITION = 0x00000008, + AINPUT_SOURCE_CLASS_JOYSTICK = 0x00000010, }; enum { @@ -340,10 +356,12 @@ enum { AINPUT_SOURCE_KEYBOARD = 0x00000100 | AINPUT_SOURCE_CLASS_BUTTON, AINPUT_SOURCE_DPAD = 0x00000200 | AINPUT_SOURCE_CLASS_BUTTON, + AINPUT_SOURCE_GAMEPAD = 0x00000400 | AINPUT_SOURCE_CLASS_BUTTON, AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER, AINPUT_SOURCE_MOUSE = 0x00002000 | AINPUT_SOURCE_CLASS_POINTER, AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION, AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION, + AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK, AINPUT_SOURCE_ANY = 0xffffff00, }; @@ -365,18 +383,20 @@ enum { * * Refer to the documentation on android.view.InputDevice for more details about input sources * and their correct interpretation. + * + * DEPRECATION NOTICE: These constants are deprecated. Use AMOTION_EVENT_AXIS_* constants instead. */ enum { - AINPUT_MOTION_RANGE_X = 0, - AINPUT_MOTION_RANGE_Y = 1, - AINPUT_MOTION_RANGE_PRESSURE = 2, - AINPUT_MOTION_RANGE_SIZE = 3, - AINPUT_MOTION_RANGE_TOUCH_MAJOR = 4, - AINPUT_MOTION_RANGE_TOUCH_MINOR = 5, - AINPUT_MOTION_RANGE_TOOL_MAJOR = 6, - AINPUT_MOTION_RANGE_TOOL_MINOR = 7, - AINPUT_MOTION_RANGE_ORIENTATION = 8, -}; + AINPUT_MOTION_RANGE_X = AMOTION_EVENT_AXIS_X, + AINPUT_MOTION_RANGE_Y = AMOTION_EVENT_AXIS_Y, + AINPUT_MOTION_RANGE_PRESSURE = AMOTION_EVENT_AXIS_PRESSURE, + AINPUT_MOTION_RANGE_SIZE = AMOTION_EVENT_AXIS_SIZE, + AINPUT_MOTION_RANGE_TOUCH_MAJOR = AMOTION_EVENT_AXIS_TOUCH_MAJOR, + AINPUT_MOTION_RANGE_TOUCH_MINOR = AMOTION_EVENT_AXIS_TOUCH_MINOR, + AINPUT_MOTION_RANGE_TOOL_MAJOR = AMOTION_EVENT_AXIS_TOOL_MAJOR, + AINPUT_MOTION_RANGE_TOOL_MINOR = AMOTION_EVENT_AXIS_TOOL_MINOR, + AINPUT_MOTION_RANGE_ORIENTATION = AMOTION_EVENT_AXIS_ORIENTATION, +} __attribute__ ((deprecated)); /* @@ -523,7 +543,7 @@ float AMotionEvent_getY(const AInputEvent* motion_event, size_t pointer_index); /* Get the current pressure of this event for the given pointer index. * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure), - * however values higher than 1 may be generated depending on the calibration of + * although values higher than 1 may be generated depending on the calibration of * the input device. */ float AMotionEvent_getPressure(const AInputEvent* motion_event, size_t pointer_index); @@ -565,6 +585,10 @@ float AMotionEvent_getToolMinor(const AInputEvent* motion_event, size_t pointer_ * (finger pointing fully right). */ float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointer_index); +/* Get the value of the request axis for the given pointer index. */ +float AMotionEvent_getAxisValue(const AInputEvent* motion_event, + int32_t axis, size_t pointer_index); + /* Get the number of historical points in this event. These are movements that * have occurred between this event and the previous event. This only applies * to AMOTION_EVENT_ACTION_MOVE events -- all other actions will have a size of 0. @@ -611,7 +635,7 @@ float AMotionEvent_getHistoricalY(AInputEvent* motion_event, size_t pointer_inde /* Get the historical pressure of this event for the given pointer index that * occurred between this event and the previous motion event. * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure), - * however values higher than 1 may be generated depending on the calibration of + * although values higher than 1 may be generated depending on the calibration of * the input device. */ float AMotionEvent_getHistoricalPressure(AInputEvent* motion_event, size_t pointer_index, size_t history_index); @@ -666,6 +690,11 @@ float AMotionEvent_getHistoricalToolMinor(const AInputEvent* motion_event, size_ float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); +/* Get the historical value of the request axis for the given pointer index + * that occurred between this event and the previous motion event. */ +float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event, + int32_t axis, size_t pointer_index, size_t history_index); + /* * Input queue diff --git a/native/include/android/keycodes.h b/native/include/android/keycodes.h index b026a0c9421b..c4a7eff947b1 100644 --- a/native/include/android/keycodes.h +++ b/native/include/android/keycodes.h @@ -231,6 +231,22 @@ enum { AKEYCODE_PROG_YELLOW = 185, AKEYCODE_PROG_BLUE = 186, AKEYCODE_APP_SWITCH = 187, + AKEYCODE_BUTTON_1 = 188, + AKEYCODE_BUTTON_2 = 189, + AKEYCODE_BUTTON_3 = 190, + AKEYCODE_BUTTON_4 = 191, + AKEYCODE_BUTTON_5 = 192, + AKEYCODE_BUTTON_6 = 193, + AKEYCODE_BUTTON_7 = 194, + AKEYCODE_BUTTON_8 = 195, + AKEYCODE_BUTTON_9 = 196, + AKEYCODE_BUTTON_10 = 197, + AKEYCODE_BUTTON_11 = 198, + AKEYCODE_BUTTON_12 = 199, + AKEYCODE_BUTTON_13 = 200, + AKEYCODE_BUTTON_14 = 201, + AKEYCODE_BUTTON_15 = 202, + AKEYCODE_BUTTON_16 = 203, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java index 1b66662ec1ce..df0e0fb1c25e 100644 --- a/obex/javax/obex/ObexHelper.java +++ b/obex/javax/obex/ObexHelper.java @@ -32,11 +32,11 @@ package javax.obex; -import android.security.Md5MessageDigest; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; @@ -916,8 +916,12 @@ public final class ObexHelper { * @return the MD5 hash of the byte array */ public static byte[] computeMd5Hash(byte[] in) { - Md5MessageDigest md5 = new Md5MessageDigest(); - return md5.digest(in); + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + return md5.digest(in); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } } /** diff --git a/opengl/java/android/opengl/GLErrorWrapper.java b/opengl/java/android/opengl/GLErrorWrapper.java index 9b66e497ae0d..66159a82b665 100644 --- a/opengl/java/android/opengl/GLErrorWrapper.java +++ b/opengl/java/android/opengl/GLErrorWrapper.java @@ -1377,5 +1377,287 @@ class GLErrorWrapper extends GLWrapperBase { checkError(); } + @Override + public void glBindFramebufferOES(int target, int framebuffer) { + checkThread(); + mgl11ExtensionPack.glBindFramebufferOES(target, framebuffer); + checkError(); + } + + @Override + public void glBindRenderbufferOES(int target, int renderbuffer) { + checkThread(); + mgl11ExtensionPack.glBindRenderbufferOES(target, renderbuffer); + checkError(); + } + + @Override + public void glBlendEquation(int mode) { + checkThread(); + mgl11ExtensionPack.glBlendEquation(mode); + checkError(); + } + + @Override + public void glBlendEquationSeparate(int modeRGB, int modeAlpha) { + checkThread(); + mgl11ExtensionPack.glBlendEquationSeparate(modeRGB, modeAlpha); + checkError(); + } + + @Override + public void glBlendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, + int dstAlpha) { + checkThread(); + mgl11ExtensionPack.glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); + checkError(); + } + + @Override + public int glCheckFramebufferStatusOES(int target) { + checkThread(); + int result = mgl11ExtensionPack.glCheckFramebufferStatusOES(target); + checkError(); + return result; + } + + @Override + public void glDeleteFramebuffersOES(int n, int[] framebuffers, int offset) { + checkThread(); + mgl11ExtensionPack.glDeleteFramebuffersOES(n, framebuffers, offset); + checkError(); + } + + @Override + public void glDeleteFramebuffersOES(int n, IntBuffer framebuffers) { + checkThread(); + mgl11ExtensionPack.glDeleteFramebuffersOES(n, framebuffers); + checkError(); + } + + @Override + public void glDeleteRenderbuffersOES(int n, int[] renderbuffers, int offset) { + checkThread(); + mgl11ExtensionPack.glDeleteRenderbuffersOES(n, renderbuffers, offset); + checkError(); + } + + @Override + public void glDeleteRenderbuffersOES(int n, IntBuffer renderbuffers) { + checkThread(); + mgl11ExtensionPack.glDeleteRenderbuffersOES(n, renderbuffers); + checkError(); + } + + @Override + public void glFramebufferRenderbufferOES(int target, int attachment, + int renderbuffertarget, int renderbuffer) { + checkThread(); + mgl11ExtensionPack.glFramebufferRenderbufferOES(target, attachment, renderbuffertarget, renderbuffer); + checkError(); + } + + @Override + public void glFramebufferTexture2DOES(int target, int attachment, + int textarget, int texture, int level) { + checkThread(); + mgl11ExtensionPack.glFramebufferTexture2DOES(target, attachment, textarget, texture, level); + checkError(); + } + + @Override + public void glGenerateMipmapOES(int target) { + checkThread(); + mgl11ExtensionPack.glGenerateMipmapOES(target); + checkError(); + } + + @Override + public void glGenFramebuffersOES(int n, int[] framebuffers, int offset) { + checkThread(); + mgl11ExtensionPack.glGenFramebuffersOES(n, framebuffers, offset); + checkError(); + } + + @Override + public void glGenFramebuffersOES(int n, IntBuffer framebuffers) { + checkThread(); + mgl11ExtensionPack.glGenFramebuffersOES(n, framebuffers); + checkError(); + } + + @Override + public void glGenRenderbuffersOES(int n, int[] renderbuffers, int offset) { + checkThread(); + mgl11ExtensionPack.glGenRenderbuffersOES(n, renderbuffers, offset); + checkError(); + } + + @Override + public void glGenRenderbuffersOES(int n, IntBuffer renderbuffers) { + checkThread(); + mgl11ExtensionPack.glGenRenderbuffersOES(n, renderbuffers); + checkError(); + } + + @Override + public void glGetFramebufferAttachmentParameterivOES(int target, + int attachment, int pname, int[] params, int offset) { + checkThread(); + mgl11ExtensionPack.glGetFramebufferAttachmentParameterivOES(target, attachment, pname, params, offset); + checkError(); + } + + @Override + public void glGetFramebufferAttachmentParameterivOES(int target, + int attachment, int pname, IntBuffer params) { + checkThread(); + mgl11ExtensionPack.glGetFramebufferAttachmentParameterivOES(target, attachment, pname, params); + checkError(); + } + + @Override + public void glGetRenderbufferParameterivOES(int target, int pname, + int[] params, int offset) { + checkThread(); + mgl11ExtensionPack.glGetRenderbufferParameterivOES(target, pname, params, offset); + checkError(); + } + + @Override + public void glGetRenderbufferParameterivOES(int target, int pname, + IntBuffer params) { + checkThread(); + mgl11ExtensionPack.glGetRenderbufferParameterivOES(target, pname, params); + checkError(); + } + + @Override + public void glGetTexGenfv(int coord, int pname, float[] params, int offset) { + checkThread(); + mgl11ExtensionPack.glGetTexGenfv(coord, pname, params, offset); + checkError(); + } + + @Override + public void glGetTexGenfv(int coord, int pname, FloatBuffer params) { + checkThread(); + mgl11ExtensionPack.glGetTexGenfv(coord, pname, params); + checkError(); + } + + @Override + public void glGetTexGeniv(int coord, int pname, int[] params, int offset) { + checkThread(); + mgl11ExtensionPack.glGetTexGeniv(coord, pname, params, offset); + checkError(); + } + + @Override + public void glGetTexGeniv(int coord, int pname, IntBuffer params) { + checkThread(); + mgl11ExtensionPack.glGetTexGeniv(coord, pname, params); + checkError(); + } + + @Override + public void glGetTexGenxv(int coord, int pname, int[] params, int offset) { + checkThread(); + mgl11ExtensionPack.glGetTexGenxv(coord, pname, params, offset); + checkError(); + } + + @Override + public void glGetTexGenxv(int coord, int pname, IntBuffer params) { + checkThread(); + mgl11ExtensionPack.glGetTexGenxv(coord, pname, params); + checkError(); + } + + @Override + public boolean glIsFramebufferOES(int framebuffer) { + checkThread(); + boolean result = mgl11ExtensionPack.glIsFramebufferOES(framebuffer); + checkError(); + return result; + } + + @Override + public boolean glIsRenderbufferOES(int renderbuffer) { + checkThread(); + mgl11ExtensionPack.glIsRenderbufferOES(renderbuffer); + checkError(); + return false; + } + @Override + public void glRenderbufferStorageOES(int target, int internalformat, + int width, int height) { + checkThread(); + mgl11ExtensionPack.glRenderbufferStorageOES(target, internalformat, width, height); + checkError(); + } + + @Override + public void glTexGenf(int coord, int pname, float param) { + checkThread(); + mgl11ExtensionPack.glTexGenf(coord, pname, param); + checkError(); + } + + @Override + public void glTexGenfv(int coord, int pname, float[] params, int offset) { + checkThread(); + mgl11ExtensionPack.glTexGenfv(coord, pname, params, offset); + checkError(); + } + + @Override + public void glTexGenfv(int coord, int pname, FloatBuffer params) { + checkThread(); + mgl11ExtensionPack.glTexGenfv(coord, pname, params); + checkError(); + } + + @Override + public void glTexGeni(int coord, int pname, int param) { + checkThread(); + mgl11ExtensionPack.glTexGeni(coord, pname, param); + checkError(); + } + + @Override + public void glTexGeniv(int coord, int pname, int[] params, int offset) { + checkThread(); + mgl11ExtensionPack.glTexGeniv(coord, pname, params, offset); + checkError(); + } + + @Override + public void glTexGeniv(int coord, int pname, IntBuffer params) { + checkThread(); + mgl11ExtensionPack.glTexGeniv(coord, pname, params); + checkError(); + } + + @Override + public void glTexGenx(int coord, int pname, int param) { + checkThread(); + mgl11ExtensionPack.glTexGenx(coord, pname, param); + checkError(); + } + + @Override + public void glTexGenxv(int coord, int pname, int[] params, int offset) { + checkThread(); + mgl11ExtensionPack.glTexGenxv(coord, pname, params, offset); + checkError(); + } + + @Override + public void glTexGenxv(int coord, int pname, IntBuffer params) { + checkThread(); + mgl11ExtensionPack.glTexGenxv(coord, pname, params); + checkError(); + } } diff --git a/opengl/java/android/opengl/GLLogWrapper.java b/opengl/java/android/opengl/GLLogWrapper.java index 6e97f6775c9b..bff739636b3e 100644 --- a/opengl/java/android/opengl/GLLogWrapper.java +++ b/opengl/java/android/opengl/GLLogWrapper.java @@ -3447,6 +3447,444 @@ class GLLogWrapper extends GLWrapperBase { checkError(); } + @Override + public void glBindFramebufferOES(int target, int framebuffer) { + begin("glBindFramebufferOES"); + arg("target", target); + arg("framebuffer", framebuffer); + end(); + mgl11ExtensionPack.glBindFramebufferOES(target, framebuffer); + checkError(); + } + + @Override + public void glBindRenderbufferOES(int target, int renderbuffer) { + begin("glBindRenderbufferOES"); + arg("target", target); + arg("renderbuffer", renderbuffer); + end(); + mgl11ExtensionPack.glBindRenderbufferOES(target, renderbuffer); + checkError(); + } + + @Override + public void glBlendEquation(int mode) { + begin("glBlendEquation"); + arg("mode", mode); + end(); + mgl11ExtensionPack.glBlendEquation(mode); + checkError(); + } + + @Override + public void glBlendEquationSeparate(int modeRGB, int modeAlpha) { + begin("glBlendEquationSeparate"); + arg("modeRGB", modeRGB); + arg("modeAlpha", modeAlpha); + end(); + mgl11ExtensionPack.glBlendEquationSeparate(modeRGB, modeAlpha); + checkError(); + } + + @Override + public void glBlendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, + int dstAlpha) { + begin("glBlendFuncSeparate"); + arg("srcRGB", srcRGB); + arg("dstRGB", dstRGB); + arg("srcAlpha", srcAlpha); + arg("dstAlpha", dstAlpha); + end(); + mgl11ExtensionPack.glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); + checkError(); + } + + @Override + public int glCheckFramebufferStatusOES(int target) { + begin("glCheckFramebufferStatusOES"); + arg("target", target); + end(); + int result = mgl11ExtensionPack.glCheckFramebufferStatusOES(target); + checkError(); + return result; + } + + @Override + public void glDeleteFramebuffersOES(int n, int[] framebuffers, int offset) { + begin("glDeleteFramebuffersOES"); + arg("n", n); + arg("framebuffers", framebuffers.toString()); + arg("offset", offset); + end(); + mgl11ExtensionPack.glDeleteFramebuffersOES(n, framebuffers, offset); + checkError(); + } + + @Override + public void glDeleteFramebuffersOES(int n, IntBuffer framebuffers) { + begin("glDeleteFramebuffersOES"); + arg("n", n); + arg("framebuffers", framebuffers.toString()); + end(); + mgl11ExtensionPack.glDeleteFramebuffersOES(n, framebuffers); + checkError(); + } + + @Override + public void glDeleteRenderbuffersOES(int n, int[] renderbuffers, int offset) { + begin("glDeleteRenderbuffersOES"); + arg("n", n); + arg("renderbuffers", renderbuffers.toString()); + arg("offset", offset); + end(); + mgl11ExtensionPack.glDeleteRenderbuffersOES(n, renderbuffers, offset); + checkError(); + } + + @Override + public void glDeleteRenderbuffersOES(int n, IntBuffer renderbuffers) { + begin("glDeleteRenderbuffersOES"); + arg("n", n); + arg("renderbuffers", renderbuffers.toString()); + end(); + mgl11ExtensionPack.glDeleteRenderbuffersOES(n, renderbuffers); + checkError(); + } + + @Override + public void glFramebufferRenderbufferOES(int target, int attachment, + int renderbuffertarget, int renderbuffer) { + begin("glFramebufferRenderbufferOES"); + arg("target", target); + arg("attachment", attachment); + arg("renderbuffertarget", renderbuffertarget); + arg("renderbuffer", renderbuffer); + end(); + mgl11ExtensionPack.glFramebufferRenderbufferOES(target, attachment, renderbuffertarget, renderbuffer); + checkError(); + } + + @Override + public void glFramebufferTexture2DOES(int target, int attachment, + int textarget, int texture, int level) { + begin("glFramebufferTexture2DOES"); + arg("target", target); + arg("attachment", attachment); + arg("textarget", textarget); + arg("texture", texture); + arg("level", level); + end(); + mgl11ExtensionPack.glFramebufferTexture2DOES(target, attachment, textarget, texture, level); + checkError(); + } + + @Override + public void glGenerateMipmapOES(int target) { + begin("glGenerateMipmapOES"); + arg("target", target); + end(); + mgl11ExtensionPack.glGenerateMipmapOES(target); + checkError(); + } + + @Override + public void glGenFramebuffersOES(int n, int[] framebuffers, int offset) { + begin("glGenFramebuffersOES"); + arg("n", n); + arg("framebuffers", framebuffers.toString()); + arg("offset", offset); + end(); + mgl11ExtensionPack.glGenFramebuffersOES(n, framebuffers, offset); + checkError(); + } + + @Override + public void glGenFramebuffersOES(int n, IntBuffer framebuffers) { + begin("glGenFramebuffersOES"); + arg("n", n); + arg("framebuffers", framebuffers.toString()); + end(); + mgl11ExtensionPack.glGenFramebuffersOES(n, framebuffers); + checkError(); + } + + @Override + public void glGenRenderbuffersOES(int n, int[] renderbuffers, int offset) { + begin("glGenRenderbuffersOES"); + arg("n", n); + arg("renderbuffers", renderbuffers.toString()); + arg("offset", offset); + end(); + mgl11ExtensionPack.glGenRenderbuffersOES(n, renderbuffers, offset); + checkError(); + } + + @Override + public void glGenRenderbuffersOES(int n, IntBuffer renderbuffers) { + begin("glGenRenderbuffersOES"); + arg("n", n); + arg("renderbuffers", renderbuffers.toString()); + end(); + mgl11ExtensionPack.glGenRenderbuffersOES(n, renderbuffers); + checkError(); + } + + @Override + public void glGetFramebufferAttachmentParameterivOES(int target, + int attachment, int pname, int[] params, int offset) { + begin("glGetFramebufferAttachmentParameterivOES"); + arg("target", target); + arg("attachment", attachment); + arg("pname", pname); + arg("params", params.toString()); + arg("offset", offset); + end(); + mgl11ExtensionPack.glGetFramebufferAttachmentParameterivOES(target, attachment, pname, params, offset); + checkError(); + } + + @Override + public void glGetFramebufferAttachmentParameterivOES(int target, + int attachment, int pname, IntBuffer params) { + begin("glGetFramebufferAttachmentParameterivOES"); + arg("target", target); + arg("attachment", attachment); + arg("pname", pname); + arg("params", params.toString()); + end(); + mgl11ExtensionPack.glGetFramebufferAttachmentParameterivOES(target, attachment, pname, params); + checkError(); + } + + @Override + public void glGetRenderbufferParameterivOES(int target, int pname, + int[] params, int offset) { + begin("glGetRenderbufferParameterivOES"); + arg("target", target); + arg("pname", pname); + arg("params", params.toString()); + arg("offset", offset); + end(); + mgl11ExtensionPack.glGetRenderbufferParameterivOES(target, pname, params, offset); + checkError(); + } + + @Override + public void glGetRenderbufferParameterivOES(int target, int pname, + IntBuffer params) { + begin("glGetRenderbufferParameterivOES"); + arg("target", target); + arg("pname", pname); + arg("params", params.toString()); + end(); + mgl11ExtensionPack.glGetRenderbufferParameterivOES(target, pname, params); + checkError(); + } + + @Override + public void glGetTexGenfv(int coord, int pname, float[] params, int offset) { + begin("glGetTexGenfv"); + arg("coord", coord); + arg("pname", pname); + arg("params", params.toString()); + arg("offset", offset); + end(); + mgl11ExtensionPack.glGetTexGenfv(coord, pname, params, offset); + checkError(); + } + + @Override + public void glGetTexGenfv(int coord, int pname, FloatBuffer params) { + begin("glGetTexGenfv"); + arg("coord", coord); + arg("pname", pname); + arg("params", params.toString()); + end(); + mgl11ExtensionPack.glGetTexGenfv(coord, pname, params); + checkError(); + } + + @Override + public void glGetTexGeniv(int coord, int pname, int[] params, int offset) { + begin("glGetTexGeniv"); + arg("coord", coord); + arg("pname", pname); + arg("params", params.toString()); + arg("offset", offset); + end(); + mgl11ExtensionPack.glGetTexGeniv(coord, pname, params, offset); + checkError(); + } + + @Override + public void glGetTexGeniv(int coord, int pname, IntBuffer params) { + begin("glGetTexGeniv"); + arg("coord", coord); + arg("pname", pname); + arg("params", params.toString()); + end(); + mgl11ExtensionPack.glGetTexGeniv(coord, pname, params); + checkError(); + } + + @Override + public void glGetTexGenxv(int coord, int pname, int[] params, int offset) { + begin("glGetTexGenxv"); + arg("coord", coord); + arg("pname", pname); + arg("params", params.toString()); + arg("offset", offset); + end(); + mgl11ExtensionPack.glGetTexGenxv(coord, pname, params, offset); + checkError(); + } + + @Override + public void glGetTexGenxv(int coord, int pname, IntBuffer params) { + begin("glGetTexGenxv"); + arg("coord", coord); + arg("pname", pname); + arg("params", params.toString()); + end(); + mgl11ExtensionPack.glGetTexGenxv(coord, pname, params); + checkError(); + } + + @Override + public boolean glIsFramebufferOES(int framebuffer) { + begin("glIsFramebufferOES"); + arg("framebuffer", framebuffer); + end(); + boolean result = mgl11ExtensionPack.glIsFramebufferOES(framebuffer); + checkError(); + return result; + } + + @Override + public boolean glIsRenderbufferOES(int renderbuffer) { + begin("glIsRenderbufferOES"); + arg("renderbuffer", renderbuffer); + end(); + mgl11ExtensionPack.glIsRenderbufferOES(renderbuffer); + checkError(); + return false; + } + + @Override + public void glRenderbufferStorageOES(int target, int internalformat, + int width, int height) { + begin("glRenderbufferStorageOES"); + arg("target", target); + arg("internalformat", internalformat); + arg("width", width); + arg("height", height); + end(); + mgl11ExtensionPack.glRenderbufferStorageOES(target, internalformat, width, height); + checkError(); + } + + @Override + public void glTexGenf(int coord, int pname, float param) { + begin("glTexGenf"); + arg("coord", coord); + arg("pname", pname); + arg("param", param); + end(); + mgl11ExtensionPack.glTexGenf(coord, pname, param); + checkError(); + } + + @Override + public void glTexGenfv(int coord, int pname, float[] params, int offset) { + begin("glTexGenfv"); + arg("coord", coord); + arg("pname", pname); + arg("params", params.toString()); + arg("offset", offset); + end(); + mgl11ExtensionPack.glTexGenfv(coord, pname, params, offset); + checkError(); + } + + @Override + public void glTexGenfv(int coord, int pname, FloatBuffer params) { + begin("glTexGenfv"); + arg("coord", coord); + arg("pname", pname); + arg("params", params.toString()); + end(); + mgl11ExtensionPack.glTexGenfv(coord, pname, params); + checkError(); + } + + @Override + public void glTexGeni(int coord, int pname, int param) { + begin("glTexGeni"); + arg("coord", coord); + arg("pname", pname); + arg("param", param); + end(); + mgl11ExtensionPack.glTexGeni(coord, pname, param); + checkError(); + } + + @Override + public void glTexGeniv(int coord, int pname, int[] params, int offset) { + begin("glTexGeniv"); + arg("coord", coord); + arg("pname", pname); + arg("params", params.toString()); + arg("offset", offset); + end(); + mgl11ExtensionPack.glTexGeniv(coord, pname, params, offset); + checkError(); + } + + @Override + public void glTexGeniv(int coord, int pname, IntBuffer params) { + begin("glTexGeniv"); + arg("coord", coord); + arg("pname", pname); + arg("params", params.toString()); + end(); + mgl11ExtensionPack.glTexGeniv(coord, pname, params); + checkError(); + } + + @Override + public void glTexGenx(int coord, int pname, int param) { + begin("glTexGenx"); + arg("coord", coord); + arg("pname", pname); + arg("param", param); + end(); + mgl11ExtensionPack.glTexGenx(coord, pname, param); + checkError(); + } + + @Override + public void glTexGenxv(int coord, int pname, int[] params, int offset) { + begin("glTexGenxv"); + arg("coord", coord); + arg("pname", pname); + arg("params", params.toString()); + arg("offset", offset); + end(); + mgl11ExtensionPack.glTexGenxv(coord, pname, params, offset); + checkError(); + } + + @Override + public void glTexGenxv(int coord, int pname, IntBuffer params) { + begin("glTexGenxv"); + arg("coord", coord); + arg("pname", pname); + arg("params", params.toString()); + end(); + mgl11ExtensionPack.glTexGenxv(coord, pname, params); + checkError(); + } + private class PointerInfo { /** * The number of coordinates per vertex. 1..4 diff --git a/opengl/java/android/opengl/GLUtils.java b/opengl/java/android/opengl/GLUtils.java index e150c1967fc2..f30a4cd47871 100644 --- a/opengl/java/android/opengl/GLUtils.java +++ b/opengl/java/android/opengl/GLUtils.java @@ -47,6 +47,9 @@ public final class GLUtils { if (bitmap == null) { throw new NullPointerException("getInternalFormat can't be used with a null Bitmap"); } + if (bitmap.isRecycled()) { + throw new IllegalArgumentException("bitmap is recycled"); + } int result = native_getInternalFormat(bitmap); if (result < 0) { throw new IllegalArgumentException("Unknown internalformat"); @@ -66,6 +69,9 @@ public final class GLUtils { if (bitmap == null) { throw new NullPointerException("getType can't be used with a null Bitmap"); } + if (bitmap.isRecycled()) { + throw new IllegalArgumentException("bitmap is recycled"); + } int result = native_getType(bitmap); if (result < 0) { throw new IllegalArgumentException("Unknown type"); @@ -100,6 +106,9 @@ public final class GLUtils { if (bitmap == null) { throw new NullPointerException("texImage2D can't be used with a null Bitmap"); } + if (bitmap.isRecycled()) { + throw new IllegalArgumentException("bitmap is recycled"); + } if (native_texImage2D(target, level, internalformat, bitmap, -1, border)!=0) { throw new IllegalArgumentException("invalid Bitmap format"); } @@ -123,6 +132,9 @@ public final class GLUtils { if (bitmap == null) { throw new NullPointerException("texImage2D can't be used with a null Bitmap"); } + if (bitmap.isRecycled()) { + throw new IllegalArgumentException("bitmap is recycled"); + } if (native_texImage2D(target, level, internalformat, bitmap, type, border)!=0) { throw new IllegalArgumentException("invalid Bitmap format"); } @@ -142,6 +154,9 @@ public final class GLUtils { if (bitmap == null) { throw new NullPointerException("texImage2D can't be used with a null Bitmap"); } + if (bitmap.isRecycled()) { + throw new IllegalArgumentException("bitmap is recycled"); + } if (native_texImage2D(target, level, -1, bitmap, -1, border)!=0) { throw new IllegalArgumentException("invalid Bitmap format"); } @@ -174,6 +189,9 @@ public final class GLUtils { if (bitmap == null) { throw new NullPointerException("texSubImage2D can't be used with a null Bitmap"); } + if (bitmap.isRecycled()) { + throw new IllegalArgumentException("bitmap is recycled"); + } int type = getType(bitmap); if (native_texSubImage2D(target, level, xoffset, yoffset, bitmap, -1, type)!=0) { throw new IllegalArgumentException("invalid Bitmap format"); @@ -196,6 +214,9 @@ public final class GLUtils { if (bitmap == null) { throw new NullPointerException("texSubImage2D can't be used with a null Bitmap"); } + if (bitmap.isRecycled()) { + throw new IllegalArgumentException("bitmap is recycled"); + } if (native_texSubImage2D(target, level, xoffset, yoffset, bitmap, format, type)!=0) { throw new IllegalArgumentException("invalid Bitmap format"); } diff --git a/opengl/java/android/opengl/GLWrapperBase.java b/opengl/java/android/opengl/GLWrapperBase.java index b0f83f7b2f14..cf252f9cc5d6 100644 --- a/opengl/java/android/opengl/GLWrapperBase.java +++ b/opengl/java/android/opengl/GLWrapperBase.java @@ -28,7 +28,7 @@ import javax.microedition.khronos.opengles.GL11ExtensionPack; * some convenient instance variables and default implementations. */ abstract class GLWrapperBase - implements GL, GL10, GL10Ext, GL11, GL11Ext { + implements GL, GL10, GL10Ext, GL11, GL11Ext, GL11ExtensionPack { public GLWrapperBase(GL gl) { mgl = (GL10) gl; if (gl instanceof GL10Ext) { diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index 8977fbf5ec9a..3d5a4d16f6e1 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -22,7 +22,7 @@ #include <sys/ioctl.h> -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS #include <linux/android_pmem.h> #endif @@ -389,10 +389,9 @@ static tls_t* getTLS() } static inline void clearError() { - if (gEGLThreadLocalStorageKey != -1) { - tls_t* tls = getTLS(); - tls->error = EGL_SUCCESS; - } + // This must clear the error from all the underlying EGL implementations as + // well as the EGL wrapper layer. + eglGetError(); } template<typename T> diff --git a/packages/SystemUI/res/drawable-mdpi/pocket_drag_pattern.png b/packages/SystemUI/res/drawable-mdpi/pocket_drag_pattern.png Binary files differnew file mode 100644 index 000000000000..abde010d4c8c --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/pocket_drag_pattern.png diff --git a/packages/SystemUI/res/drawable/pocket_drag_bg.xml b/packages/SystemUI/res/drawable/pocket_drag_bg.xml new file mode 100644 index 000000000000..573a7029b6b4 --- /dev/null +++ b/packages/SystemUI/res/drawable/pocket_drag_bg.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<bitmap + xmlns:android="http://schemas.android.com/apk/res/android" + android:tileMode="repeat" + android:src="@drawable/pocket_drag_pattern" + /> diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_notification_area.xml b/packages/SystemUI/res/layout-xlarge/status_bar_notification_area.xml index 6e3b0d7cc949..f53b29e36beb 100644 --- a/packages/SystemUI/res/layout-xlarge/status_bar_notification_area.xml +++ b/packages/SystemUI/res/layout-xlarge/status_bar_notification_area.xml @@ -41,6 +41,7 @@ android:src="@drawable/ic_sysbar_ime_default" android:visibility="gone" /> + <com.android.systemui.statusbar.tablet.NotificationIconArea android:id="@+id/notificationIcons" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml index 3fdfdbbd95ca..d7e16337d47f 100644 --- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml +++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml @@ -40,6 +40,9 @@ android:layout_alignParentTop="true" android:layout_marginLeft="123dip" android:layout_marginTop="16dip" + android:maxWidth="64dip" + android:maxHeight="64dip" + android:adjustViewBounds="true" /> <View android:id="@+id/recents_callout_line" @@ -55,26 +58,33 @@ /> <TextView android:id="@+id/app_label" - android:layout_width="113dip" + android:layout_width="97dip" android:layout_height="wrap_content" android:textSize="18dip" - android:fadingEdge="none" - android:fadingEdgeLength="0dp" + android:fadingEdge="horizontal" + android:fadingEdgeLength="10dip" android:scrollHorizontally="true" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginLeft="16dip" android:layout_marginTop="32dip" + android:singleLine="true" + android:ellipsize="marquee" /> <TextView android:id="@+id/app_description" - android:layout_width="wrap_content" + android:layout_width="97dip" android:layout_height="wrap_content" android:textSize="18dip" + android:fadingEdge="horizontal" + android:fadingEdgeLength="10dip" + android:scrollHorizontally="true" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginLeft="16dip" android:layout_marginTop="61dip" + android:singleLine="true" + android:ellipsize="marquee" /> </RelativeLayout> diff --git a/packages/SystemUI/res/values-es-rUS-xlarge-land/strings.xml b/packages/SystemUI/res/values-es-rUS-xlarge-land/strings.xml new file mode 100644 index 000000000000..78a4c180fffb --- /dev/null +++ b/packages/SystemUI/res/values-es-rUS-xlarge-land/strings.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- XL --> + <string name="toast_rotation_locked" msgid="2686639138967158852">"La pantalla está bloqueada en orientación paisaje."</string> +</resources> diff --git a/packages/SystemUI/res/values-es-rUS-xlarge-port/strings.xml b/packages/SystemUI/res/values-es-rUS-xlarge-port/strings.xml new file mode 100644 index 000000000000..9daef6a93164 --- /dev/null +++ b/packages/SystemUI/res/values-es-rUS-xlarge-port/strings.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- XL --> + <string name="toast_rotation_locked" msgid="4297721709987511908">"La pantalla está bloqueada en orientación retrato."</string> +</resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 42868db00788..ef37b4e35ca8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -33,6 +33,11 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Binder; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; import android.os.RemoteException; import android.provider.Settings; import android.provider.Telephony; @@ -50,6 +55,7 @@ import com.android.internal.telephony.IccCard; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.cdma.EriInfo; import com.android.server.am.BatteryStatsService; +import com.android.internal.util.AsyncChannel; import com.android.systemui.R; @@ -82,6 +88,7 @@ public class NetworkController extends BroadcastReceiver { // wifi final WifiManager mWifiManager; + AsyncChannel mWifiChannel; boolean mWifiEnabled, mWifiConnected; int mWifiLevel; String mWifiSsid; @@ -140,6 +147,14 @@ public class NetworkController extends BroadcastReceiver { // wifi mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + HandlerThread handlerThread = new HandlerThread("WifiServiceThread"); + handlerThread.start(); + Handler handler = new WifiHandler(handlerThread.getLooper()); + mWifiChannel = new AsyncChannel(); + Messenger wifiMessenger = mWifiManager.getMessenger(); + if (wifiMessenger != null) { + mWifiChannel.connect(mContext, handler, wifiMessenger); + } // broadcasts IntentFilter filter = new IntentFilter(); @@ -584,6 +599,44 @@ public class NetworkController extends BroadcastReceiver { // ===== Wifi =================================================================== + class WifiHandler extends Handler { + + WifiHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: + if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { + mWifiChannel.sendMessage(Message.obtain(this, + AsyncChannel.CMD_CHANNEL_FULL_CONNECTION)); + } else { + Slog.e(TAG, "Failed to connect to wifi"); + } + break; + case WifiManager.DATA_ACTIVITY_NOTIFICATION: + int dataActivity = msg.arg1; + /* TODO: update icons based on data activity */ + switch (dataActivity) { + case WifiManager.DATA_ACTIVITY_IN: + break; + case WifiManager.DATA_ACTIVITY_OUT: + break; + case WifiManager.DATA_ACTIVITY_INOUT: + break; + case WifiManager.DATA_ACTIVITY_NONE: + break; + } + break; + default: + //Ignore + break; + } + } + } + private void updateWifiState(Intent intent) { final String action = intent.getAction(); if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java index b1e74ad15e6a..ce0848b69ea0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java @@ -90,8 +90,8 @@ public class InputMethodsPanel extends LinearLayout implements StatusBarPanel, if (imi2 == null) return 0; if (imi1 == null) return 1; if (mPackageManager != null) { - CharSequence imiId1 = imi1.loadLabel(mPackageManager); - CharSequence imiId2 = imi2.loadLabel(mPackageManager); + CharSequence imiId1 = imi1.loadLabel(mPackageManager) + "/" + imi1.getId(); + CharSequence imiId2 = imi2.loadLabel(mPackageManager) + "/" + imi2.getId(); if (imiId1 != null && imiId2 != null) { return imiId1.toString().compareTo(imiId2.toString()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/PanelBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/PanelBackgroundView.java index 5eafdc1f118a..9ac933f3721a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/PanelBackgroundView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/PanelBackgroundView.java @@ -14,28 +14,25 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.statusbar.tablet; import android.content.Context; -import android.content.res.TypedArray; +import android.graphics.Canvas; import android.util.AttributeSet; -import android.util.Slog; import android.view.View; -import android.graphics.BitmapFactory; -import android.graphics.Bitmap; -import android.graphics.Paint; -import android.graphics.Canvas; public class PanelBackgroundView extends View { + /* private Bitmap mTexture; private Paint mPaint; private int mTextureWidth; private int mTextureHeight; - + */ + public PanelBackgroundView(Context context, AttributeSet attrs) { super(context, attrs); /* - mTexture = BitmapFactory.decodeResource(getResources(), + mTexture = BitmapFactory.decodeResource(getResources(), com.android.internal.R.drawable.status_bar_background); mTextureWidth = mTexture.getWidth(); mTextureHeight = mTexture.getHeight(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java index 86c3e7597884..e0d558f2580d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java @@ -374,8 +374,9 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O if (title != null && title.length() > 0 && icon != null) { if (DEBUG) Log.v(TAG, "creating activity desc for id=" + id + ", label=" + title); ActivityDescription item = new ActivityDescription( - recentInfo.thumbnail, icon, title, - recentInfo.description, intent, id, index, info.packageName); + am.getTaskThumbnail(recentInfo.persistentId), + icon, title, recentInfo.description, intent, id, + index, info.packageName); activityDescriptions.add(item); ++index; } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java index a67f9157459f..ddb43b8b6e0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java @@ -16,26 +16,28 @@ package com.android.systemui.statusbar.tablet; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; -import android.content.Context; -import android.util.Slog; -import android.view.View; -import android.util.AttributeSet; -import android.widget.ImageView; -import android.widget.TextView; -import android.view.DragEvent; -import android.view.MotionEvent; import android.content.ClipData; import android.content.ClipDescription; -import android.graphics.Paint; +import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PixelFormat; import android.graphics.Point; +import android.util.AttributeSet; +import android.util.Slog; +import android.view.DragEvent; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; import android.view.WindowManager; -import android.widget.FrameLayout; import android.view.WindowManagerImpl; -import android.graphics.PixelFormat; -import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; import com.android.systemui.R; @@ -45,87 +47,149 @@ public class ShirtPocket extends ImageView { private ClipData mClipping = null; - private View mWindow = null; private ImageView mPreviewIcon; - private TextView mDescription; - private TextView mAltText; - public ShirtPocket(Context context, AttributeSet attrs) { - super(context, attrs); - } + public static class DropZone extends View { + ShirtPocket mPocket; + public DropZone(Context context, AttributeSet attrs) { + super(context, attrs); + } + public void setPocket(ShirtPocket p) { + mPocket = p; + } - // TODO: "pin area" panel, dragging things out - ObjectAnimator mAnimHide, mAnimShow; - - protected void onAttachedToWindow() { - // Drag API notes: we must be visible to receive drag events - setVisibility(View.VISIBLE); + public void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mPocket.holding()) { + show(false); + } else { + hide(false); + } + } - refresh(); + // Drag API notes: we must be visible to receive drag events + private void show(boolean animate) { + setTranslationY(0f); + if (animate) { + setAlpha(0f); + ObjectAnimator.ofFloat(this, "alpha", 0f, 1f).start(); + } else { + setAlpha(1f); + } + } - setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - if (mClipping != null) { - if (mWindow.getVisibility() == View.VISIBLE) hideWindow(); - else showWindow(); + private void hide(boolean animate) { + AnimatorListenerAdapter onEnd = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator _a) { + DropZone.this.setTranslationY(getHeight() + 2); + DropZone.this.setAlpha(0f); } + }; + if (animate) { + Animator a = ObjectAnimator.ofFloat(this, "alpha", getAlpha(), 0f); + a.addListener(onEnd); + a.start(); + } else { + onEnd.onAnimationEnd(null); } - }); - } + } - private void refresh() { - setClickable(mClipping != null); - // XXX: TODO - } - - private void showWindow() { - getHandler().post(new Runnable() { - public void run() { - mWindow.setVisibility(View.VISIBLE); - refresh(); + @Override + public boolean onDragEvent(DragEvent event) { + if (DEBUG) Slog.d(TAG, "onDragEvent: " + event); + switch (event.getAction()) { + // We want to appear whenever a potential drag takes off from anywhere in the UI. + case DragEvent.ACTION_DRAG_STARTED: + show(true); + break; + case DragEvent.ACTION_DRAG_ENTERED: + if (DEBUG) Slog.d(TAG, "entered!"); + // XXX: TODO + break; + case DragEvent.ACTION_DRAG_EXITED: + if (DEBUG) Slog.d(TAG, "exited!"); + break; + case DragEvent.ACTION_DROP: + if (DEBUG) Slog.d(TAG, "dropped!"); + mPocket.stash(event.getClipData()); + break; + case DragEvent.ACTION_DRAG_ENDED: + hide(true); + break; } - }); + return true; // we want everything, thank you + } } - private void hideWindow() { - getHandler().post(new Runnable() { - public void run() { - mWindow.setVisibility(View.GONE); - refresh(); - } - }); + public ShirtPocket(Context context, AttributeSet attrs) { + super(context, attrs); } + + // TODO: "pin area" panel, dragging things out + ObjectAnimator mAnimHide, mAnimShow; - private void hideWindowInJustASec() { - getHandler().postDelayed(new Runnable() { - public void run() { - mWindow.setVisibility(View.GONE); - refresh(); - } - }, - 250); + protected void onAttachedToWindow() { + } + + public boolean holding() { + return (mClipping != null); } private void stash(ClipData clipping) { mClipping = clipping; if (mClipping != null) { + setVisibility(View.VISIBLE); Bitmap icon = mClipping.getIcon(); - mDescription.setText(mClipping.getDescription().getLabel()); +// mDescription.setText(mClipping.getDescription().getLabel()); if (icon != null) { - mPreviewIcon.setImageBitmap(icon); - mPreviewIcon.setVisibility(View.VISIBLE); - mAltText.setVisibility(View.GONE); + setImageBitmap(icon); } else { - mPreviewIcon.setVisibility(View.GONE); - mAltText.setVisibility(View.VISIBLE); if (mClipping.getItemCount() > 0) { // TODO: figure out how to visualize every kind of ClipData! - mAltText.setText(mClipping.getItemAt(0).coerceToText(getContext())); + //mAltText.setText(mClipping.getItemAt(0).coerceToText(getContext())); } } + } else { + setVisibility(View.GONE); } } + @Override + public boolean onTouchEvent(MotionEvent ev) { + final int action = ev.getAction(); + if (action == MotionEvent.ACTION_DOWN) { + final ClipData clip = mClipping; + if (clip != null) { + final Bitmap icon = clip.getIcon(); + DragShadowBuilder shadow; + if (icon != null) { + shadow = new DragShadowBuilder(this) { + public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) { + shadowSize.set(icon.getWidth(), icon.getHeight()); + shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2); + } + public void onDrawShadow(Canvas canvas) { + canvas.drawBitmap(icon, 0, 0, new Paint()); + } + }; + } else { + // uhhh, what now? + shadow = new DragShadowBuilder(this); + } + + startDrag(clip, shadow, null, 0); + + // TODO: only discard the clipping if it was accepted + stash(null); + + return true; + } + } + return false; + } + + /* private boolean isInViewContentArea(View v, int x, int y) { final int l = v.getPaddingLeft(); final int r = v.getWidth() - v.getPaddingRight(); @@ -167,38 +231,12 @@ public class ShirtPocket extends ImageView { // TODO: only discard the clipping if it was accepted stash(null); - hideWindowInJustASec(); // will refresh the icon - return true; } } return false; } }; - - public boolean onDragEvent(DragEvent event) { - if (DEBUG) Slog.d(TAG, "onDragEvent: " + event); - switch (event.getAction()) { - // We want to appear whenever a potential drag takes off from anywhere in the UI. - case DragEvent.ACTION_DRAG_STARTED: - // XXX: TODO - break; - case DragEvent.ACTION_DRAG_ENTERED: - if (DEBUG) Slog.d(TAG, "entered!"); - // XXX: TODO - break; - case DragEvent.ACTION_DRAG_EXITED: - if (DEBUG) Slog.d(TAG, "exited!"); - setVisibility(mClipping == null ? View.GONE : View.VISIBLE); - break; - case DragEvent.ACTION_DROP: - if (DEBUG) Slog.d(TAG, "dropped!"); - stash(event.getClipData()); - break; - case DragEvent.ACTION_DRAG_ENDED: - break; - } - return true; // we want everything, thank you - } + */ } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java index a072aedd08b6..a8f4262aff79 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java @@ -158,7 +158,9 @@ public class TabletTicker private void advance() { // Out with the old... if (mCurrentView != null) { - mWindow.removeView(mCurrentView); + if (mWindow != null) { + mWindow.removeView(mCurrentView); + } mCurrentView = null; mCurrentKey = null; mCurrentNotification = null; diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java index 5e33f0535c49..1f06dcc4c6f2 100644 --- a/policy/src/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java @@ -221,7 +221,8 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac final AlertDialog.Builder ab = new AlertDialog.Builder(mContext); ab.setAdapter(mAdapter, this) - .setInverseBackgroundForced(true); + .setInverseBackgroundForced(true) + .setTitle(R.string.global_actions); final AlertDialog dialog = ab.create(); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); @@ -248,7 +249,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac } else { mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); } - mDialog.setTitle(R.string.global_actions); } diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java index e775dac88c89..5ed67a975fa7 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java @@ -26,6 +26,7 @@ import static android.os.BatteryManager.BATTERY_STATUS_CHARGING; import static android.os.BatteryManager.BATTERY_STATUS_FULL; import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; import android.media.AudioManager; +import android.os.BatteryManager; import android.os.Handler; import android.os.Message; import android.os.SystemClock; @@ -70,12 +71,12 @@ public class KeyguardUpdateMonitor { private boolean mKeyguardBypassEnabled; - private boolean mDevicePluggedIn; - private boolean mDeviceProvisioned; private int mBatteryLevel; + private int mBatteryStatus; + private CharSequence mTelephonyPlmn; private CharSequence mTelephonySpn; @@ -203,7 +204,7 @@ public class KeyguardUpdateMonitor { // take a guess to start mSimState = IccCard.State.READY; - mDevicePluggedIn = true; + mBatteryStatus = BATTERY_STATUS_FULL; mBatteryLevel = 100; mTelephonyPlmn = getDefaultPlmn(); @@ -283,13 +284,12 @@ public class KeyguardUpdateMonitor { /** * Handle {@link #MSG_BATTERY_UPDATE} */ - private void handleBatteryUpdate(int pluggedInStatus, int batteryLevel) { + private void handleBatteryUpdate(int batteryStatus, int batteryLevel) { if (DEBUG) Log.d(TAG, "handleBatteryUpdate"); - final boolean pluggedIn = isPluggedIn(pluggedInStatus); - - if (isBatteryUpdateInteresting(pluggedIn, batteryLevel)) { + if (isBatteryUpdateInteresting(batteryStatus, batteryLevel)) { + mBatteryStatus = batteryStatus; mBatteryLevel = batteryLevel; - mDevicePluggedIn = pluggedIn; + final boolean pluggedIn = isPluggedIn(batteryStatus);; for (int i = 0; i < mInfoCallbacks.size(); i++) { mInfoCallbacks.get(i).onRefreshBatteryInfo( shouldShowBatteryInfo(), pluggedIn, batteryLevel); @@ -336,26 +336,34 @@ public class KeyguardUpdateMonitor { return status == BATTERY_STATUS_CHARGING || status == BATTERY_STATUS_FULL; } - private boolean isBatteryUpdateInteresting(boolean pluggedIn, int batteryLevel) { + private boolean isBatteryUpdateInteresting(int batteryStatus, int batteryLevel) { // change in plug is always interesting - if (mDevicePluggedIn != pluggedIn) { + final boolean isPluggedIn = isPluggedIn(batteryStatus); + final boolean wasPluggedIn = isPluggedIn(mBatteryStatus); + final boolean stateChangedWhilePluggedIn = + wasPluggedIn == true && isPluggedIn == true && (mBatteryStatus != batteryStatus); + if (wasPluggedIn != isPluggedIn || stateChangedWhilePluggedIn) { return true; } // change in battery level while plugged in - if (pluggedIn && mBatteryLevel != batteryLevel) { + if (isPluggedIn && mBatteryLevel != batteryLevel) { return true; } - if (!pluggedIn) { + if (!isPluggedIn) { // not plugged in and below threshold - if (batteryLevel < LOW_BATTERY_THRESHOLD && batteryLevel != mBatteryLevel) { + if (isBatteryLow(batteryLevel) && batteryLevel != mBatteryLevel) { return true; } } return false; } + private boolean isBatteryLow(int batteryLevel) { + return batteryLevel < LOW_BATTERY_THRESHOLD; + } + /** * @param intent The intent with action {@link Telephony.Intents#SPN_STRINGS_UPDATED_ACTION} * @return The string to use for the plmn, or null if it should not be shown. @@ -485,7 +493,12 @@ public class KeyguardUpdateMonitor { } public boolean isDevicePluggedIn() { - return mDevicePluggedIn; + return isPluggedIn(mBatteryStatus); + } + + public boolean isDeviceCharged() { + return mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL + || mBatteryLevel >= 100; // in case a particular device doesn't flag it } public int getBatteryLevel() { @@ -493,7 +506,7 @@ public class KeyguardUpdateMonitor { } public boolean shouldShowBatteryInfo() { - return mDevicePluggedIn || mBatteryLevel < LOW_BATTERY_THRESHOLD; + return isPluggedIn(mBatteryStatus) || isBatteryLow(mBatteryLevel); } public CharSequence getTelephonyPlmn() { diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java index 780996171a7e..b32a72970151 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java @@ -89,7 +89,7 @@ import android.view.WindowManagerPolicy; * This class is created by the initialization routine of the {@link WindowManagerPolicy}, * and runs on its thread. The keyguard UI is created from that thread in the * constructor of this class. The apis may be called from other threads, including the - * {@link com.android.server.InputManager}'s and {@link android.view.WindowManager}'s. + * {@link com.android.server.wm.InputManager}'s and {@link android.view.WindowManager}'s. * Therefore, methods on this class are synchronized, and any action that is pointed * directly to the keyguard UI is posted to a {@link Handler} to ensure it is taken on the UI * thread of the keyguard. diff --git a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java index 6c6c2cc843d7..018fe0cddc8b 100644 --- a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java +++ b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java @@ -361,10 +361,12 @@ class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient /** {@inheritDoc} */ public void cleanUp() { + if (DEBUG) Log.v(TAG, "Cleanup() called on " + this); mUpdateMonitor.removeCallback(this); mLockPatternUtils = null; mUpdateMonitor = null; mCallback = null; + mLockPatternView.setOnPatternListener(null); } @Override @@ -406,6 +408,7 @@ class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient mCallback.keyguardDone(true); mCallback.reportSuccessfulUnlockAttempt(); } else { + boolean reportFailedAttempt = false; if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS); } @@ -413,9 +416,10 @@ class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) { mTotalFailedPatternAttempts++; mFailedPatternAttemptsSinceLastTimeout++; - mCallback.reportFailedUnlockAttempt(); + reportFailedAttempt = true; } - if (mFailedPatternAttemptsSinceLastTimeout >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) { + if (mFailedPatternAttemptsSinceLastTimeout + >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) { long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); handleAttemptLockout(deadline); } else { @@ -427,6 +431,12 @@ class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS); } + + // Because the following can result in cleanUp() being called on this screen, + // member variables reset in cleanUp() shouldn't be accessed after this call. + if (reportFailedAttempt) { + mCallback.reportFailedUnlockAttempt(); + } } } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 8afb53e1aaca..79b5ced9d018 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -424,9 +424,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public final void openPanel(int featureId, KeyEvent event) { if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null && mActionBar.isOverflowReserved()) { - // Invalidate the options menu, we want a prepare event that the app can respond to. - invalidatePanelMenu(FEATURE_OPTIONS_PANEL); - mActionBar.showOverflowMenu(); + if (mActionBar.getVisibility() == View.VISIBLE) { + // Invalidate the options menu, we want a prepare event that the app can respond to. + invalidatePanelMenu(FEATURE_OPTIONS_PANEL); + mActionBar.showOverflowMenu(); + } } else { openPanel(getPanelState(featureId, true), event); } @@ -696,14 +698,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { final PanelFeatureState st = getPanelState(featureId, true); if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null && mActionBar.isOverflowReserved()) { - if (!mActionBar.isOverflowMenuShowing()) { - final Callback cb = getCallback(); - if (cb != null && - cb.onPreparePanel(featureId, st.createdPanelView, st.menu)) { - playSoundEffect = mActionBar.showOverflowMenu(); + if (mActionBar.getVisibility() == View.VISIBLE) { + if (!mActionBar.isOverflowMenuShowing()) { + final Callback cb = getCallback(); + if (cb != null && + cb.onPreparePanel(featureId, st.createdPanelView, st.menu)) { + playSoundEffect = mActionBar.showOverflowMenu(); + } + } else { + playSoundEffect = mActionBar.hideOverflowMenu(); } - } else { - playSoundEffect = mActionBar.hideOverflowMenu(); } } else { if (st.isOpen || st.isHandled) { @@ -911,7 +915,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mActionBar != null) { final Callback cb = getCallback(); if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) { - if (cb != null) { + if (cb != null && mActionBar.getVisibility() == View.VISIBLE) { final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); if (cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) { cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu); @@ -1275,6 +1279,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return mDecor.superDispatchTrackballEvent(event); } + @Override + public boolean superDispatchGenericMotionEvent(MotionEvent event) { + return mDecor.superDispatchGenericMotionEvent(event); + } + /** * A key was pressed down and not handled by anything else in the window. * @@ -1688,6 +1697,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { .dispatchTrackballEvent(ev); } + @Override + public boolean dispatchGenericMotionEvent(MotionEvent ev) { + final Callback cb = getCallback(); + return cb != null && mFeatureId < 0 ? cb.dispatchGenericMotionEvent(ev) : super + .dispatchGenericMotionEvent(ev); + } + public boolean superDispatchKeyEvent(KeyEvent event) { return super.dispatchKeyEvent(event); } @@ -1704,6 +1720,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return super.dispatchTrackballEvent(event); } + public boolean superDispatchGenericMotionEvent(MotionEvent event) { + return super.dispatchGenericMotionEvent(event); + } + @Override public boolean onTouchEvent(MotionEvent event) { return onInterceptTouchEvent(event); diff --git a/policy/src/com/android/internal/policy/impl/StatusView.java b/policy/src/com/android/internal/policy/impl/StatusView.java index da7bbb8928d3..2aff4a8eeaa7 100644 --- a/policy/src/com/android/internal/policy/impl/StatusView.java +++ b/policy/src/com/android/internal/policy/impl/StatusView.java @@ -51,6 +51,7 @@ class StatusView { private LockPatternUtils mLockPatternUtils; private int mHelpMessageId; private int mHelpIconId; + private KeyguardUpdateMonitor mUpdateMonitor; private View findViewById(int id) { return mView.findViewById(id); @@ -97,6 +98,7 @@ class StatusView { mHasDate = (mDate != null); mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year); mLockPatternUtils = lockPatternUtils; + mUpdateMonitor = updateMonitor; refreshTimeAndDateDisplay(); @@ -186,7 +188,7 @@ class StatusView { // Battery status if (mPluggedIn) { // Charging or charged - if (mBatteryLevel >= 100) { + if (mUpdateMonitor.isDeviceCharged()) { mStatus1.setText(getContext().getString(R.string.lockscreen_charged)); } else { mStatus1.setText(getContext().getString(R.string.lockscreen_plugged_in, diff --git a/preloaded-classes b/preloaded-classes index 378085342054..1eabe147d0f2 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -556,9 +556,6 @@ android.provider.Settings$NameValueTable android.provider.Settings$Secure android.provider.Settings$System android.renderscript.RenderScript -android.security.Md5MessageDigest -android.security.MessageDigest -android.security.Sha1MessageDigest android.server.BluetoothA2dpService android.server.BluetoothEventLoop android.server.BluetoothService @@ -1907,6 +1904,7 @@ com.android.org.bouncycastle.crypto.Digest com.android.org.bouncycastle.crypto.ExtendedDigest com.android.org.bouncycastle.crypto.Mac com.android.org.bouncycastle.crypto.digests.OpenSSLDigest +com.android.org.bouncycastle.crypto.digests.OpenSSLDigest$MD5 com.android.org.bouncycastle.crypto.digests.OpenSSLDigest$SHA1 com.android.org.bouncycastle.crypto.macs.HMac com.android.org.bouncycastle.jce.ProviderConfigurationPermission diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 22ecc5444aab..69a4adca68df 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -120,12 +120,4 @@ ifeq ($(TARGET_SIMULATOR),true) endif endif -ifeq ($(BOARD_USE_LVMX),true) - LOCAL_CFLAGS += -DLVMX - LOCAL_C_INCLUDES += vendor/nxp - LOCAL_STATIC_LIBRARIES += liblifevibes - LOCAL_SHARED_LIBRARIES += liblvmxservice -# LOCAL_SHARED_LIBRARIES += liblvmxipc -endif - include $(BUILD_SHARED_LIBRARY) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 4ec16c1c110f..704da72a8a6d 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -47,10 +47,6 @@ #include "A2dpAudioInterface.h" #endif -#ifdef LVMX -#include "lifevibes.h" -#endif - #include <media/EffectsFactoryApi.h> #include <media/EffectVisualizerApi.h> @@ -149,10 +145,6 @@ AudioFlinger::AudioFlinger() } else { LOGE("Couldn't even initialize the stubbed audio hardware!"); } -#ifdef LVMX - LifeVibes::init(); - mLifeVibesClientPid = -1; -#endif } AudioFlinger::~AudioFlinger() @@ -485,9 +477,6 @@ status_t AudioFlinger::setMode(int mode) mMode = mode; for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) mPlaybackThreads.valueAt(i)->setMode(mode); -#ifdef LVMX - LifeVibes::setMode(mode); -#endif } return ret; @@ -635,39 +624,11 @@ status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs) return PERMISSION_DENIED; } -#ifdef LVMX - AudioParameter param = AudioParameter(keyValuePairs); - LifeVibes::setParameters(ioHandle,keyValuePairs); - String8 key = String8(AudioParameter::keyRouting); - int device; - if (NO_ERROR != param.getInt(key, device)) { - device = -1; - } - - key = String8(LifevibesTag); - String8 value; - int musicEnabled = -1; - if (NO_ERROR == param.get(key, value)) { - if (value == LifevibesEnable) { - mLifeVibesClientPid = IPCThreadState::self()->getCallingPid(); - musicEnabled = 1; - } else if (value == LifevibesDisable) { - mLifeVibesClientPid = -1; - musicEnabled = 0; - } - } -#endif - // ioHandle == 0 means the parameters are global to the audio hardware interface if (ioHandle == 0) { AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_SET_PARAMETER; result = mAudioHardware->setParameters(keyValuePairs); -#ifdef LVMX - if (musicEnabled != -1) { - LifeVibes::enableMusic((bool) musicEnabled); - } -#endif mHardwareStatus = AUDIO_HW_IDLE; return result; } @@ -684,11 +645,6 @@ status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs) } if (thread != NULL) { result = thread->setParameters(keyValuePairs); -#ifdef LVMX - if ((NO_ERROR == result) && (device != -1)) { - LifeVibes::setDevice(LifeVibes::threadIdToAudioOutputType(thread->id()), device); - } -#endif return result; } return BAD_VALUE; @@ -802,13 +758,6 @@ void AudioFlinger::removeNotificationClient(pid_t pid) if (index >= 0) { sp <NotificationClient> client = mNotificationClients.valueFor(pid); LOGV("removeNotificationClient() %p, pid %d", client.get(), pid); -#ifdef LVMX - if (pid == mLifeVibesClientPid) { - LOGV("Disabling lifevibes"); - LifeVibes::enableMusic(false); - mLifeVibesClientPid = -1; - } -#endif mNotificationClients.removeItem(pid); } } @@ -1214,24 +1163,12 @@ uint32_t AudioFlinger::PlaybackThread::latency() const status_t AudioFlinger::PlaybackThread::setMasterVolume(float value) { -#ifdef LVMX - int audioOutputType = LifeVibes::getMixerType(mId, mType); - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { - LifeVibes::setMasterVolume(audioOutputType, value); - } -#endif mMasterVolume = value; return NO_ERROR; } status_t AudioFlinger::PlaybackThread::setMasterMute(bool muted) { -#ifdef LVMX - int audioOutputType = LifeVibes::getMixerType(mId, mType); - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { - LifeVibes::setMasterMute(audioOutputType, muted); - } -#endif mMasterMute = muted; return NO_ERROR; } @@ -1248,24 +1185,12 @@ bool AudioFlinger::PlaybackThread::masterMute() const status_t AudioFlinger::PlaybackThread::setStreamVolume(int stream, float value) { -#ifdef LVMX - int audioOutputType = LifeVibes::getMixerType(mId, mType); - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { - LifeVibes::setStreamVolume(audioOutputType, stream, value); - } -#endif mStreamTypes[stream].volume = value; return NO_ERROR; } status_t AudioFlinger::PlaybackThread::setStreamMute(int stream, bool muted) { -#ifdef LVMX - int audioOutputType = LifeVibes::getMixerType(mId, mType); - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { - LifeVibes::setStreamMute(audioOutputType, stream, muted); - } -#endif mStreamTypes[stream].mute = muted; return NO_ERROR; } @@ -1593,12 +1518,6 @@ bool AudioFlinger::MixerThread::threadLoop() } // enable changes in effect chain unlockEffectChains(effectChains); -#ifdef LVMX - int audioOutputType = LifeVibes::getMixerType(mId, mType); - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { - LifeVibes::process(audioOutputType, mMixBuffer, mixBufferSize); - } -#endif mLastWriteTime = systemTime(); mInWrite = true; mBytesWritten += mixBufferSize; @@ -1661,24 +1580,6 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track if (masterMute) { masterVolume = 0; } -#ifdef LVMX - bool tracksConnectedChanged = false; - bool stateChanged = false; - - int audioOutputType = LifeVibes::getMixerType(mId, mType); - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) - { - int activeTypes = 0; - for (size_t i=0 ; i<count ; i++) { - sp<Track> t = activeTracks[i].promote(); - if (t == 0) continue; - Track* const track = t.get(); - int iTracktype=track->type(); - activeTypes |= 1<<track->type(); - } - LifeVibes::computeVolumes(audioOutputType, activeTypes, tracksConnectedChanged, stateChanged, masterVolume, masterMute); - } -#endif // Delegate master volume control to effect in output mix effect chain if needed sp<EffectChain> chain = getEffectChain_l(AudioSystem::SESSION_OUTPUT_MIX); if (chain != 0) { @@ -1746,17 +1647,6 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track // read original volumes with volume control float typeVolume = mStreamTypes[track->type()].volume; -#ifdef LVMX - bool streamMute=false; - // read the volume from the LivesVibes audio engine. - if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) - { - LifeVibes::getStreamVolumes(audioOutputType, track->type(), &typeVolume, &streamMute); - if (streamMute) { - typeVolume = 0; - } - } -#endif float v = masterVolume * typeVolume; vl = (uint32_t)(v * cblk->volume[0]) << 12; vr = (uint32_t)(v * cblk->volume[1]) << 12; @@ -1789,14 +1679,6 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track if (va > MAX_GAIN_INT) va = MAX_GAIN_INT; aux = int16_t(va); -#ifdef LVMX - if ( tracksConnectedChanged || stateChanged ) - { - // only do the ramp when the volume is changed by the user / application - param = AudioMixer::VOLUME; - } -#endif - // XXX: these things DON'T need to be done each time mAudioMixer->setBufferProvider(track); mAudioMixer->enable(AudioMixer::MIXING); @@ -4292,18 +4174,6 @@ int AudioFlinger::openOutput(uint32_t *pDevices, } else { thread = new MixerThread(this, output, id, *pDevices); LOGV("openOutput() created mixer output: ID %d thread %p", id, thread); - -#ifdef LVMX - unsigned bitsPerSample = - (format == AudioSystem::PCM_16_BIT) ? 16 : - ((format == AudioSystem::PCM_8_BIT) ? 8 : 0); - unsigned channelCount = (channels == AudioSystem::CHANNEL_OUT_STEREO) ? 2 : 1; - int audioOutputType = LifeVibes::threadIdToAudioOutputType(thread->id()); - - LifeVibes::init_aot(audioOutputType, samplingRate, bitsPerSample, channelCount); - LifeVibes::setDevice(audioOutputType, *pDevices); -#endif - } mPlaybackThreads.add(id, thread); diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 81f2eb4db9b9..ec3d202cd5c7 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -1181,9 +1181,6 @@ private: DefaultKeyedVector< pid_t, sp<NotificationClient> > mNotificationClients; volatile int32_t mNextUniqueId; -#ifdef LVMX - int mLifeVibesClientPid; -#endif uint32_t mMode; }; diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp index afa9acc9ee3d..3082d45145a7 100644 --- a/services/audioflinger/AudioPolicyManagerBase.cpp +++ b/services/audioflinger/AudioPolicyManagerBase.cpp @@ -19,6 +19,7 @@ #include <utils/Log.h> #include <hardware_legacy/AudioPolicyManagerBase.h> #include <media/mediarecorder.h> +#include <math.h> namespace android { @@ -609,7 +610,7 @@ status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output, // store time at which the stream was stopped - see isStreamActive() outputDesc->mStopTime[stream] = systemTime(); - setOutputDevice(output, getNewDevice(output)); + setOutputDevice(output, getNewDevice(output), false, outputDesc->mLatency*2); #ifdef WITH_A2DP if (mA2dpOutput != 0 && !a2dpUsedForSonification() && @@ -1030,6 +1031,8 @@ AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clien mForceUse[i] = AudioSystem::FORCE_NONE; } + initializeVolumeCurves(); + // devices available by default are speaker, ear piece and microphone mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE | AudioSystem::DEVICE_OUT_SPEAKER; @@ -1540,6 +1543,20 @@ uint32_t AudioPolicyManagerBase::getStrategyForStream(AudioSystem::stream_type s return (uint32_t)getStrategy(stream); } +uint32_t AudioPolicyManagerBase::getDevicesForStream(AudioSystem::stream_type stream) { + uint32_t devices; + // By checking the range of stream before calling getStrategy, we avoid + // getStrategy's behavior for invalid streams. getStrategy would do a LOGE + // and then return STRATEGY_MEDIA, but we want to return the empty set. + if (stream < (AudioSystem::stream_type) 0 || stream >= AudioSystem::NUM_STREAM_TYPES) { + devices = 0; + } else { + AudioPolicyManagerBase::routing_strategy strategy = getStrategy(stream); + devices = getDeviceForStrategy(strategy, true); + } + return devices; +} + AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy( AudioSystem::stream_type stream) { // stream to strategy mapping @@ -1605,12 +1622,6 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, if (device) break; device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET; if (device) break; - device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; - if (device) break; - device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET; - if (device) break; - device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; - if (device) break; #ifdef WITH_A2DP // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP if (!isInCall()) { @@ -1620,6 +1631,12 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, if (device) break; } #endif + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; + if (device) break; + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE; if (device == 0) { LOGE("getDeviceForStrategy() earpiece device not found"); @@ -1627,12 +1644,6 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, break; case AudioSystem::FORCE_SPEAKER: - device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; - if (device) break; - device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET; - if (device) break; - device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; - if (device) break; #ifdef WITH_A2DP // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to // A2DP speaker when forcing to speaker output @@ -1641,6 +1652,12 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, if (device) break; } #endif + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; + if (device) break; + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER; if (device == 0) { LOGE("getDeviceForStrategy() speaker device not found"); @@ -1669,20 +1686,9 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, if (device2 == 0) { device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET; } - if (device2 == 0) { - device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; - } - if (device2 == 0) { - device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET; - } - if (device2 == 0) { - device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; - } #ifdef WITH_A2DP - if (mA2dpOutput != 0) { - if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) { - break; - } + if ((mA2dpOutput != 0) && + (strategy != STRATEGY_SONIFICATION || a2dpUsedForSonification())) { if (device2 == 0) { device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP; } @@ -1695,6 +1701,15 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, } #endif if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; + } + if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET; + } + if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET; + } + if (device2 == 0) { device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER; } @@ -1821,6 +1836,62 @@ audio_io_handle_t AudioPolicyManagerBase::getActiveInput() return 0; } +float AudioPolicyManagerBase::volIndexToAmpl(uint32_t device, const StreamDescriptor& streamDesc, + int indexInUi) { + // the volume index in the UI is relative to the min and max volume indices for this stream type + int nbSteps = 1 + streamDesc.mVolIndex[StreamDescriptor::VOLMAX] - + streamDesc.mVolIndex[StreamDescriptor::VOLMIN]; + int volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) / + (streamDesc.mIndexMax - streamDesc.mIndexMin); + + // find what part of the curve this index volume belongs to, or if it's out of bounds + int segment = 0; + if (volIdx < streamDesc.mVolIndex[StreamDescriptor::VOLMIN]) { // out of bounds + return 0.0f; + } else if (volIdx < streamDesc.mVolIndex[StreamDescriptor::VOLKNEE1]) { + segment = 0; + } else if (volIdx < streamDesc.mVolIndex[StreamDescriptor::VOLKNEE2]) { + segment = 1; + } else if (volIdx <= streamDesc.mVolIndex[StreamDescriptor::VOLMAX]) { + segment = 2; + } else { // out of bounds + return 1.0f; + } + + // linear interpolation in the attenuation table in dB + float decibels = streamDesc.mVolDbAtt[segment] + + ((float)(volIdx - streamDesc.mVolIndex[segment])) * + ( (streamDesc.mVolDbAtt[segment+1] - streamDesc.mVolDbAtt[segment]) / + ((float)(streamDesc.mVolIndex[segment+1] - streamDesc.mVolIndex[segment])) ); + + float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 ) + + LOGV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f", + streamDesc.mVolIndex[segment], volIdx, streamDesc.mVolIndex[segment+1], + streamDesc.mVolDbAtt[segment], decibels, streamDesc.mVolDbAtt[segment+1], + amplification); + + return amplification; +} + +void AudioPolicyManagerBase::initializeVolumeCurves() { + // initialize the volume curves to a (-49.5 - 0 dB) attenuation in 0.5dB steps + for (int i=0 ; i< AudioSystem::NUM_STREAM_TYPES ; i++) { + mStreams[i].mVolIndex[StreamDescriptor::VOLMIN] = 1; + mStreams[i].mVolDbAtt[StreamDescriptor::VOLMIN] = -49.5f; + mStreams[i].mVolIndex[StreamDescriptor::VOLKNEE1] = 33; + mStreams[i].mVolDbAtt[StreamDescriptor::VOLKNEE1] = -33.5f; + mStreams[i].mVolIndex[StreamDescriptor::VOLKNEE2] = 66; + mStreams[i].mVolDbAtt[StreamDescriptor::VOLKNEE2] = -17.0f; + // here we use 100 steps to avoid rounding errors + // when computing the volume in volIndexToAmpl() + mStreams[i].mVolIndex[StreamDescriptor::VOLMAX] = 100; + mStreams[i].mVolDbAtt[StreamDescriptor::VOLMAX] = 0.0f; + } + + // TODO add modifications for music to have finer steps below knee1 and above knee2 +} + float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device) { float volume = 1.0; @@ -1831,8 +1902,7 @@ float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_hand device = outputDesc->device(); } - int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin); - volume = AudioSystem::linearToLog(volInt); + volume = volIndexToAmpl(device, streamDesc, index); // if a headset is connected, apply the following rules to ring tones and notifications // to avoid sound level bursts in user's ears: @@ -1843,9 +1913,7 @@ float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_hand (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP | AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | AudioSystem::DEVICE_OUT_WIRED_HEADSET | - AudioSystem::DEVICE_OUT_WIRED_HEADPHONE | - AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET | - AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET)) && + AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) && ((getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) || (stream == AudioSystem::SYSTEM)) && streamDesc.mCanBeMuted) { diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp index b04672d7d6fe..b614c483632f 100644 --- a/services/audioflinger/AudioPolicyService.cpp +++ b/services/audioflinger/AudioPolicyService.cpp @@ -365,6 +365,14 @@ uint32_t AudioPolicyService::getStrategyForStream(AudioSystem::stream_type strea return mpPolicyManager->getStrategyForStream(stream); } +uint32_t AudioPolicyService::getDevicesForStream(AudioSystem::stream_type stream) +{ + if (mpPolicyManager == NULL) { + return 0; + } + return mpPolicyManager->getDevicesForStream(stream); +} + audio_io_handle_t AudioPolicyService::getOutputForEffect(effect_descriptor_t *desc) { if (mpPolicyManager == NULL) { @@ -488,13 +496,6 @@ status_t AudioPolicyService::onTransact( // ---------------------------------------------------------------------------- -void AudioPolicyService::instantiate() { - defaultServiceManager()->addService( - String16("media.audio_policy"), new AudioPolicyService()); -} - - -// ---------------------------------------------------------------------------- // AudioPolicyClientInterface implementation // ---------------------------------------------------------------------------- diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h index 54af1f1e0265..faad893ea968 100644 --- a/services/audioflinger/AudioPolicyService.h +++ b/services/audioflinger/AudioPolicyService.h @@ -21,6 +21,7 @@ #include <hardware_legacy/AudioPolicyInterface.h> #include <media/ToneGenerator.h> #include <utils/Vector.h> +#include <binder/BinderService.h> namespace android { @@ -28,12 +29,17 @@ class String8; // ---------------------------------------------------------------------------- -class AudioPolicyService: public BnAudioPolicyService, public AudioPolicyClientInterface, +class AudioPolicyService : + public BinderService<AudioPolicyService>, + public BnAudioPolicyService, + public AudioPolicyClientInterface, public IBinder::DeathRecipient { + friend class BinderService<AudioPolicyService>; public: - static void instantiate(); + // for BinderService + static const char *getServiceName() { return "media.audio_policy"; } virtual status_t dump(int fd, const Vector<String16>& args); @@ -80,6 +86,7 @@ public: virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index); virtual uint32_t getStrategyForStream(AudioSystem::stream_type stream); + virtual uint32_t getDevicesForStream(AudioSystem::stream_type stream); virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc); virtual status_t registerEffect(effect_descriptor_t *desc, @@ -241,11 +248,3 @@ private: }; // namespace android #endif // ANDROID_AUDIOPOLICYSERVICE_H - - - - - - - - diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 41dbe2fb1980..38a896fa4029 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -624,7 +624,11 @@ static const int32_t GAMEPAD_KEYCODES[] = { AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1, AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2, AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR, - AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE + AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE, + AKEYCODE_BUTTON_1, AKEYCODE_BUTTON_2, AKEYCODE_BUTTON_3, AKEYCODE_BUTTON_4, + AKEYCODE_BUTTON_5, AKEYCODE_BUTTON_6, AKEYCODE_BUTTON_7, AKEYCODE_BUTTON_8, + AKEYCODE_BUTTON_9, AKEYCODE_BUTTON_10, AKEYCODE_BUTTON_11, AKEYCODE_BUTTON_12, + AKEYCODE_BUTTON_13, AKEYCODE_BUTTON_14, AKEYCODE_BUTTON_15, AKEYCODE_BUTTON_16, }; int EventHub::openDevice(const char *devicePath) { @@ -739,9 +743,9 @@ int EventHub::openDevice(const char *devicePath) { //} // See if this is a keyboard. Ignore everything in the button range except for - // gamepads which are also considered keyboards. + // joystick and gamepad buttons which are also considered keyboards. if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC)) - || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD), + || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_JOYSTICK), sizeof_bit_array(BTN_DIGI)) || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK), sizeof_bit_array(KEY_MAX + 1))) { @@ -856,6 +860,18 @@ int EventHub::openDevice(const char *devicePath) { } } + // See if this device is a joystick. + // Ignore touchscreens because they use the same absolute axes for other purposes. + if (device->classes & INPUT_DEVICE_CLASS_GAMEPAD + && !(device->classes & INPUT_DEVICE_CLASS_TOUCH)) { + if (test_bit(ABS_X, abs_bitmask) + || test_bit(ABS_Y, abs_bitmask) + || test_bit(ABS_HAT0X, abs_bitmask) + || test_bit(ABS_HAT0Y, abs_bitmask)) { + device->classes |= INPUT_DEVICE_CLASS_JOYSTICK; + } + } + // If the device isn't recognized as something we handle, don't monitor it. if (device->classes == 0) { LOGV("Dropping device: id=%d, path='%s', name='%s'", diff --git a/services/input/EventHub.h b/services/input/EventHub.h index 0ee0b9b853bc..52993f705f2e 100644 --- a/services/input/EventHub.h +++ b/services/input/EventHub.h @@ -85,7 +85,7 @@ struct RawAbsoluteAxisInfo { int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8 int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise - inline int32_t getRange() { return maxValue - minValue; } + inline int32_t getRange() const { return maxValue - minValue; } inline void clear() { valid = false; @@ -100,7 +100,7 @@ struct RawAbsoluteAxisInfo { * Input device classes. */ enum { - /* The input device is a keyboard. */ + /* The input device is a keyboard or has buttons. */ INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001, /* The input device is an alpha-numeric keyboard (not just a dial pad). */ @@ -123,6 +123,9 @@ enum { /* The input device has switches. */ INPUT_DEVICE_CLASS_SWITCH = 0x00000080, + + /* The input device is a joystick (implies gamepad, has joystick absolute axes). */ + INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100, }; /* diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index cbfdd755df09..ae11fb1e025e 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -482,8 +482,10 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && mInputTargetWaitApplication != NULL) { - int32_t x = int32_t(motionEntry->firstSample.pointerCoords[0].x); - int32_t y = int32_t(motionEntry->firstSample.pointerCoords[0].y); + int32_t x = int32_t(motionEntry->firstSample.pointerCoords[0]. + getAxisValue(AMOTION_EVENT_AXIS_X)); + int32_t y = int32_t(motionEntry->firstSample.pointerCoords[0]. + getAxisValue(AMOTION_EVENT_AXIS_Y)); const InputWindow* touchedWindow = findTouchedWindowAtLocked(x, y); if (touchedWindow && touchedWindow->inputWindowHandle != NULL @@ -888,11 +890,15 @@ void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const M "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " "orientation=%f", i, entry->pointerIds[i], - sample->pointerCoords[i].x, sample->pointerCoords[i].y, - sample->pointerCoords[i].pressure, sample->pointerCoords[i].size, - sample->pointerCoords[i].touchMajor, sample->pointerCoords[i].touchMinor, - sample->pointerCoords[i].toolMajor, sample->pointerCoords[i].toolMinor, - sample->pointerCoords[i].orientation); + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); } // Keep in mind that due to batching, it is possible for the number of samples actually @@ -1188,8 +1194,10 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, /* Case 1: New splittable pointer going down. */ int32_t pointerIndex = getMotionEventActionPointerIndex(action); - int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].x); - int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex].y); + int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex]. + getAxisValue(AMOTION_EVENT_AXIS_X)); + int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex]. + getAxisValue(AMOTION_EVENT_AXIS_Y)); const InputWindow* newTouchedWindow = NULL; const InputWindow* topErrorWindow = NULL; @@ -2275,11 +2283,16 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f, " "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " "orientation=%f", - i, pointerIds[i], pointerCoords[i].x, pointerCoords[i].y, - pointerCoords[i].pressure, pointerCoords[i].size, - pointerCoords[i].touchMajor, pointerCoords[i].touchMinor, - pointerCoords[i].toolMajor, pointerCoords[i].toolMinor, - pointerCoords[i].orientation); + i, pointerIds[i], + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); } #endif if (! validateMotionEvent(action, pointerCount, pointerIds)) { diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 46d374d7203e..8f38cb259fef 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -250,6 +250,9 @@ InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, ui if (classes & INPUT_DEVICE_CLASS_DPAD) { keyboardSources |= AINPUT_SOURCE_DPAD; } + if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { + keyboardSources |= AINPUT_SOURCE_GAMEPAD; + } if (keyboardSources != 0) { device->addMapper(new KeyboardInputMapper(device, keyboardSources, keyboardType)); @@ -267,6 +270,11 @@ InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, ui device->addMapper(new SingleTouchInputMapper(device)); } + // Joystick-like devices. + if (classes & INPUT_DEVICE_CLASS_JOYSTICK) { + device->addMapper(new JoystickInputMapper(device)); + } + return device; } @@ -605,19 +613,11 @@ void InputDevice::configure() { mSources = 0; - for (size_t i = 0; i < mMappers.size(); i++) { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { InputMapper* mapper = mMappers[i]; mapper->configure(); - - uint32_t sources = mapper->getSources(); - if (sources) { - mSources |= sources; - } else { - // The input mapper does not provide any sources. Remove it from the list. - mMappers.removeAt(i); - delete mapper; - i -= 1; - } + mSources |= mapper->getSources(); } } @@ -741,6 +741,16 @@ int32_t InputMapper::getMetaState() { return 0; } +void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump, + const RawAbsoluteAxisInfo& axis, const char* name) { + if (axis.valid) { + dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n", + name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); + } else { + dump.appendFormat(INDENT4 "%s: unknown range\n", name); + } +} + // --- SwitchInputMapper --- @@ -883,7 +893,7 @@ void KeyboardInputMapper::process(const RawEvent* rawEvent) { bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { return scanCode < BTN_MOUSE || scanCode >= KEY_OK - || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI); + || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); } void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, @@ -1082,7 +1092,7 @@ void CursorInputMapper::configure() { // Configure device mode. switch (mParameters.mode) { case Parameters::MODE_POINTER: - mSources = 0; // AINPUT_SOURCE_MOUSE; disable mouse support + mSources = AINPUT_SOURCE_MOUSE; mXPrecision = 1.0f; mYPrecision = 1.0f; mXScale = 1.0f; @@ -1275,24 +1285,23 @@ void CursorInputMapper::sync(nsecs_t when) { } } + pointerCoords.clear(); + if (mPointerController != NULL) { mPointerController->move(deltaX, deltaY); if (downChanged) { mPointerController->setButtonState(mLocked.down ? POINTER_BUTTON_1 : 0); } - mPointerController->getPosition(&pointerCoords.x, &pointerCoords.y); + float x, y; + mPointerController->getPosition(&x, &y); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); } else { - pointerCoords.x = deltaX; - pointerCoords.y = deltaY; + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); } - pointerCoords.pressure = mLocked.down ? 1.0f : 0.0f; - pointerCoords.size = 0; - pointerCoords.touchMajor = 0; - pointerCoords.touchMinor = 0; - pointerCoords.toolMajor = 0; - pointerCoords.toolMinor = 0; - pointerCoords.orientation = 0; + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, mLocked.down ? 1.0f : 0.0f); } // release lock int32_t metaState = mContext->getGlobalMetaState(); @@ -1519,25 +1528,16 @@ void TouchInputMapper::configureRawAxes() { mRawAxes.orientation.clear(); } -static void dumpAxisInfo(String8& dump, RawAbsoluteAxisInfo axis, const char* name) { - if (axis.valid) { - dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n", - name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); - } else { - dump.appendFormat(INDENT4 "%s: unknown range\n", name); - } -} - void TouchInputMapper::dumpRawAxes(String8& dump) { dump.append(INDENT3 "Raw Axes:\n"); - dumpAxisInfo(dump, mRawAxes.x, "X"); - dumpAxisInfo(dump, mRawAxes.y, "Y"); - dumpAxisInfo(dump, mRawAxes.pressure, "Pressure"); - dumpAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor"); - dumpAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor"); - dumpAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor"); - dumpAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor"); - dumpAxisInfo(dump, mRawAxes.orientation, "Orientation"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.x, "X"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.y, "Y"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.pressure, "Pressure"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.orientation, "Orientation"); } bool TouchInputMapper::configureSurfaceLocked() { @@ -2640,9 +2640,11 @@ void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, int32_t c2 = signExtendNybble(in.orientation & 0x0f); if (c1 != 0 || c2 != 0) { orientation = atan2f(c1, c2) * 0.5f; - float minorAxisScale = (16.0f - pythag(c1, c2)) / 16.0f; - toolMinor *= minorAxisScale; - touchMinor *= minorAxisScale; + float scale = 1.0f + pythag(c1, c2) / 16.0f; + touchMajor *= scale; + touchMinor /= scale; + toolMajor *= scale; + toolMinor /= scale; } else { orientation = 0; } @@ -2683,15 +2685,16 @@ void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, // Write output coords. PointerCoords& out = pointerCoords[outIndex]; - out.x = x; - out.y = y; - out.pressure = pressure; - out.size = size; - out.touchMajor = touchMajor; - out.touchMinor = touchMinor; - out.toolMajor = toolMajor; - out.toolMinor = toolMinor; - out.orientation = orientation; + out.clear(); + out.setAxisValue(AMOTION_EVENT_AXIS_X, x); + out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); + out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); pointerIds[outIndex] = int32_t(id); @@ -2703,14 +2706,17 @@ void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, // Check edge flags by looking only at the first pointer since the flags are // global to the event. if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) { - if (pointerCoords[0].x <= 0) { + float x = pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X); + float y = pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y); + + if (x <= 0) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT; - } else if (pointerCoords[0].x >= mLocked.orientedSurfaceWidth) { + } else if (x >= mLocked.orientedSurfaceWidth) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT; } - if (pointerCoords[0].y <= 0) { + if (y <= 0) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP; - } else if (pointerCoords[0].y >= mLocked.orientedSurfaceHeight) { + } else if (y >= mLocked.orientedSurfaceHeight) { motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM; } } @@ -3705,4 +3711,193 @@ void MultiTouchInputMapper::configureRawAxes() { } +// --- JoystickInputMapper --- + +JoystickInputMapper::JoystickInputMapper(InputDevice* device) : + InputMapper(device) { + initialize(); +} + +JoystickInputMapper::~JoystickInputMapper() { +} + +uint32_t JoystickInputMapper::getSources() { + return AINPUT_SOURCE_JOYSTICK; +} + +void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mAxes.x.valid) { + info->addMotionRange(AINPUT_MOTION_RANGE_X, + mAxes.x.min, mAxes.x.max, mAxes.x.flat, mAxes.x.fuzz); + } + if (mAxes.y.valid) { + info->addMotionRange(AINPUT_MOTION_RANGE_Y, + mAxes.y.min, mAxes.y.max, mAxes.y.flat, mAxes.y.fuzz); + } +} + +void JoystickInputMapper::dump(String8& dump) { + dump.append(INDENT2 "Joystick Input Mapper:\n"); + + dump.append(INDENT3 "Raw Axes:\n"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.x, "X"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.y, "Y"); + + dump.append(INDENT3 "Normalized Axes:\n"); + dumpNormalizedAxis(dump, mAxes.x, "X"); + dumpNormalizedAxis(dump, mAxes.y, "Y"); + dumpNormalizedAxis(dump, mAxes.hat0X, "Hat0X"); + dumpNormalizedAxis(dump, mAxes.hat0Y, "Hat0Y"); +} + +void JoystickInputMapper::dumpNormalizedAxis(String8& dump, + const NormalizedAxis& axis, const char* name) { + if (axis.valid) { + dump.appendFormat(INDENT4 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, " + "scale=%0.3f, center=%0.3f, precision=%0.3f, value=%0.3f\n", + name, axis.min, axis.max, axis.flat, axis.fuzz, + axis.scale, axis.center, axis.precision, axis.value); + } else { + dump.appendFormat(INDENT4 "%s: unknown range\n", name); + } +} + +void JoystickInputMapper::configure() { + InputMapper::configure(); + + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mRawAxes.x); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mRawAxes.y); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_HAT0X, & mRawAxes.hat0X); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_HAT0Y, & mRawAxes.hat0Y); + + mAxes.x.configure(mRawAxes.x); + mAxes.y.configure(mRawAxes.y); + mAxes.hat0X.configure(mRawAxes.hat0X); + mAxes.hat0Y.configure(mRawAxes.hat0Y); +} + +void JoystickInputMapper::initialize() { + mAccumulator.clear(); + + mAxes.x.resetState(); + mAxes.y.resetState(); + mAxes.hat0X.resetState(); + mAxes.hat0Y.resetState(); +} + +void JoystickInputMapper::reset() { + // Recenter all axes. + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + mAccumulator.clear(); + mAccumulator.fields = Accumulator::FIELD_ALL; + sync(when); + + // Reinitialize state. + initialize(); + + InputMapper::reset(); +} + +void JoystickInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_ABS: + switch (rawEvent->scanCode) { + case ABS_X: + mAccumulator.fields |= Accumulator::FIELD_ABS_X; + mAccumulator.absX = rawEvent->value; + break; + case ABS_Y: + mAccumulator.fields |= Accumulator::FIELD_ABS_Y; + mAccumulator.absY = rawEvent->value; + break; + case ABS_HAT0X: + mAccumulator.fields |= Accumulator::FIELD_ABS_HAT0X; + mAccumulator.absHat0X = rawEvent->value; + break; + case ABS_HAT0Y: + mAccumulator.fields |= Accumulator::FIELD_ABS_HAT0Y; + mAccumulator.absHat0Y = rawEvent->value; + break; + } + break; + + case EV_SYN: + switch (rawEvent->scanCode) { + case SYN_REPORT: + sync(rawEvent->when); + break; + } + break; + } +} + +void JoystickInputMapper::sync(nsecs_t when) { + uint32_t fields = mAccumulator.fields; + if (fields == 0) { + return; // no new state changes, so nothing to do + } + + int32_t metaState = mContext->getGlobalMetaState(); + + bool motionAxisChanged = false; + if (fields & Accumulator::FIELD_ABS_X) { + if (mAxes.x.updateValue(mAccumulator.absX)) { + motionAxisChanged = true; + } + } + + if (fields & Accumulator::FIELD_ABS_Y) { + if (mAxes.y.updateValue(mAccumulator.absY)) { + motionAxisChanged = true; + } + } + + if (motionAxisChanged) { + PointerCoords pointerCoords; + pointerCoords.clear(); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, mAxes.x.value); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, mAxes.y.value); + + int32_t pointerId = 0; + getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0, + AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + 1, &pointerId, &pointerCoords, mAxes.x.precision, mAxes.y.precision, 0); + } + + if (fields & Accumulator::FIELD_ABS_HAT0X) { + if (mAxes.hat0X.updateValueAndDirection(mAccumulator.absHat0X)) { + notifyDirectionalAxis(mAxes.hat0X, when, metaState, + AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT); + } + } + + if (fields & Accumulator::FIELD_ABS_HAT0Y) { + if (mAxes.hat0Y.updateValueAndDirection(mAccumulator.absHat0Y)) { + notifyDirectionalAxis(mAxes.hat0Y, when, metaState, + AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN); + } + } + + mAccumulator.clear(); +} + +void JoystickInputMapper::notifyDirectionalAxis(DirectionalAxis& axis, + nsecs_t when, int32_t metaState, int32_t lowKeyCode, int32_t highKeyCode) { + if (axis.lastKeyCode) { + getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0, + AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM, + axis.lastKeyCode, 0, metaState, when); + axis.lastKeyCode = 0; + } + if (axis.direction) { + axis.lastKeyCode = axis.direction > 0 ? highKeyCode : lowKeyCode; + getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0, + AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM, + axis.lastKeyCode, 0, metaState, when); + } +} + + } // namespace android diff --git a/services/input/InputReader.h b/services/input/InputReader.h index 7619682a0116..27cb8e1e685c 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -354,6 +354,9 @@ public: protected: InputDevice* mDevice; InputReaderContext* mContext; + + static void dumpRawAbsoluteAxisInfo(String8& dump, + const RawAbsoluteAxisInfo& axis, const char* name); }; @@ -968,6 +971,139 @@ private: void sync(nsecs_t when); }; + +class JoystickInputMapper : public InputMapper { +public: + JoystickInputMapper(InputDevice* device); + virtual ~JoystickInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(String8& dump); + virtual void configure(); + virtual void reset(); + virtual void process(const RawEvent* rawEvent); + +private: + struct RawAxes { + RawAbsoluteAxisInfo x; + RawAbsoluteAxisInfo y; + RawAbsoluteAxisInfo hat0X; + RawAbsoluteAxisInfo hat0Y; + } mRawAxes; + + struct NormalizedAxis { + bool valid; + + static const float min = -1.0f; + static const float max = -1.0f; + + float scale; // scale factor + float center; // center offset after scaling + float precision; // precision + float flat; // size of flat region + float fuzz; // error tolerance + + float value; // most recent value + + NormalizedAxis() : valid(false), scale(0), center(0), precision(0), + flat(0), fuzz(0), value(0) { + } + + void configure(const RawAbsoluteAxisInfo& rawAxis) { + if (rawAxis.valid && rawAxis.getRange() != 0) { + valid = true; + scale = 2.0f / rawAxis.getRange(); + precision = rawAxis.getRange(); + flat = rawAxis.flat * scale; + fuzz = rawAxis.fuzz * scale; + center = float(rawAxis.minValue + rawAxis.maxValue) / rawAxis.getRange(); + } + } + + void resetState() { + value = 0; + } + + bool updateValue(int32_t rawValue) { + float newValue = rawValue * scale - center; + if (value == newValue) { + return false; + } + value = newValue; + return true; + } + }; + + struct DirectionalAxis : NormalizedAxis { + int32_t direction; // most recent direction vector: value is one of -1, 0, 1. + + int32_t lastKeyCode; // most recent key code produced + + DirectionalAxis() : lastKeyCode(0) { + } + + void resetState() { + NormalizedAxis::resetState(); + direction = 0; + lastKeyCode = 0; + } + + bool updateValueAndDirection(int32_t rawValue) { + if (!updateValue(rawValue)) { + return false; + } + if (value > flat) { + direction = 1; + } else if (value < -flat) { + direction = -1; + } else { + direction = 0; + } + return true; + } + }; + + struct Axes { + NormalizedAxis x; + NormalizedAxis y; + DirectionalAxis hat0X; + DirectionalAxis hat0Y; + } mAxes; + + struct Accumulator { + enum { + FIELD_ABS_X = 1, + FIELD_ABS_Y = 2, + FIELD_ABS_HAT0X = 4, + FIELD_ABS_HAT0Y = 8, + + FIELD_ALL = FIELD_ABS_X | FIELD_ABS_Y | FIELD_ABS_HAT0X | FIELD_ABS_HAT0Y, + }; + + uint32_t fields; + + int32_t absX; + int32_t absY; + int32_t absHat0X; + int32_t absHat0Y; + + inline void clear() { + fields = 0; + } + } mAccumulator; + + void initialize(); + + void sync(nsecs_t when); + + void notifyDirectionalAxis(DirectionalAxis& axis, + nsecs_t when, int32_t metaState, int32_t lowKeyCode, int32_t highKeyCode); + + static void dumpNormalizedAxis(String8& dump, + const NormalizedAxis& axis, const char* name); +}; + } // namespace android #endif // _UI_INPUT_READER_H diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index 25030d81c1d6..41d67ed27d4f 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -878,7 +878,8 @@ public: InstrumentedInputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputDispatcherInterface>& dispatcher) : - InputReader(eventHub, policy, dispatcher) { + InputReader(eventHub, policy, dispatcher), + mNextDevice(NULL) { } virtual ~InstrumentedInputReader() { @@ -1472,15 +1473,15 @@ protected: float x, float y, float pressure, float size, float touchMajor, float touchMinor, float toolMajor, float toolMinor, float orientation) { - ASSERT_NEAR(x, coords.x, 1); - ASSERT_NEAR(y, coords.y, 1); - ASSERT_NEAR(pressure, coords.pressure, EPSILON); - ASSERT_NEAR(size, coords.size, EPSILON); - ASSERT_NEAR(touchMajor, coords.touchMajor, 1); - ASSERT_NEAR(touchMinor, coords.touchMinor, 1); - ASSERT_NEAR(toolMajor, coords.toolMajor, 1); - ASSERT_NEAR(toolMinor, coords.toolMinor, 1); - ASSERT_NEAR(orientation, coords.orientation, EPSILON); + ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), 1); + ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON); + ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON); + ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), 1); + ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), 1); + ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), 1); + ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), 1); + ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON); } }; @@ -2077,7 +2078,6 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) { process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NEAR(0.0f, args.pointerCoords[0].x, EPSILON); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); } @@ -2892,8 +2892,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotate processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(50, args.pointerCoords[0].x, 1); - ASSERT_NEAR(75, args.pointerCoords[0].y, 1); + ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); @@ -2914,8 +2914,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(50, args.pointerCoords[0].x, 1); - ASSERT_NEAR(75, args.pointerCoords[0].y, 1); + ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); @@ -2927,8 +2927,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(75, args.pointerCoords[0].x, 1); - ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].y, 1); + ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); @@ -2940,8 +2940,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].x, 1); - ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].y, 1); + ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); @@ -2953,8 +2953,8 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].x, 1); - ASSERT_NEAR(50, args.pointerCoords[0].y, 1); + ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); + ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); processUp(mapper); processSync(mapper); diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index 5a36417ba9db..8c07e155847a 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -63,7 +63,10 @@ class AlarmManagerService extends IAlarmManager.Stub { private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP; private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME; private static final int TIME_CHANGED_MASK = 1 << 16; - + + // Alignment quantum for inexact repeating alarms + private static final long QUANTUM = AlarmManager.INTERVAL_FIFTEEN_MINUTES; + private static final String TAG = "AlarmManager"; private static final String ClockReceiver_TAG = "ClockReceiver"; private static final boolean localLOGV = false; @@ -83,17 +86,6 @@ class AlarmManagerService extends IAlarmManager.Stub { private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>(); private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder(); - // slots corresponding with the inexact-repeat interval buckets, - // ordered from shortest to longest - private static final long sInexactSlotIntervals[] = { - AlarmManager.INTERVAL_FIFTEEN_MINUTES, - AlarmManager.INTERVAL_HALF_HOUR, - AlarmManager.INTERVAL_HOUR, - AlarmManager.INTERVAL_HALF_DAY, - AlarmManager.INTERVAL_DAY - }; - private long mInexactDeliveryTimes[] = { 0, 0, 0, 0, 0}; - private int mDescriptor; private int mBroadcastRefCount = 0; private PowerManager.WakeLock mWakeLock; @@ -199,58 +191,40 @@ class AlarmManagerService extends IAlarmManager.Stub { return; } - // find the slot in the delivery-times array that we will use - int intervalSlot; - for (intervalSlot = 0; intervalSlot < sInexactSlotIntervals.length; intervalSlot++) { - if (sInexactSlotIntervals[intervalSlot] == interval) { - break; - } + if (interval <= 0) { + Slog.w(TAG, "setInexactRepeating ignored because interval " + interval + + " is invalid"); + return; } - - // Non-bucket intervals just fall back to the less-efficient - // unbucketed recurring alarm implementation - if (intervalSlot >= sInexactSlotIntervals.length) { + + // If the requested interval isn't a multiple of 15 minutes, just treat it as exact + if (interval % QUANTUM != 0) { + if (localLOGV) Slog.v(TAG, "Interval " + interval + " not a quantum multiple"); setRepeating(type, triggerAtTime, interval, operation); return; } - // Align bucketed alarm deliveries by trying to match - // the shortest-interval bucket already scheduled - long bucketTime = 0; - for (int slot = 0; slot < mInexactDeliveryTimes.length; slot++) { - if (mInexactDeliveryTimes[slot] > 0) { - bucketTime = mInexactDeliveryTimes[slot]; - break; - } - } - - if (bucketTime == 0) { - // If nothing is scheduled yet, just start at the requested time - bucketTime = triggerAtTime; - } else { - // Align the new alarm with the existing bucketed sequence. To achieve - // alignment, we slide the start time around by min{interval, slot interval} - long adjustment = (interval <= sInexactSlotIntervals[intervalSlot]) - ? interval : sInexactSlotIntervals[intervalSlot]; - - // The bucket may have started in the past; adjust - while (bucketTime < triggerAtTime) { - bucketTime += adjustment; - } + // Translate times into the ELAPSED timebase for alignment purposes so that + // alignment never tries to match against wall clock times. + final boolean isRtc = (type == AlarmManager.RTC || type == AlarmManager.RTC_WAKEUP); + final long skew = (isRtc) + ? System.currentTimeMillis() - SystemClock.elapsedRealtime() + : 0; - // Or the bucket may be set to start more than an interval beyond - // our requested trigger time; pull it back to meet our needs - while (bucketTime > triggerAtTime + adjustment) { - bucketTime -= adjustment; - } + // Slip forward to the next ELAPSED-timebase quantum after the stated time. If + // we're *at* a quantum point, leave it alone. + final long adjustedTriggerTime; + long offset = (triggerAtTime - skew) % QUANTUM; + if (offset != 0) { + adjustedTriggerTime = triggerAtTime - offset + QUANTUM; + } else { + adjustedTriggerTime = triggerAtTime; } - // Remember where this bucket started (reducing the amount of later - // fixup required) and set the alarm with the new, bucketed start time. - if (localLOGV) Slog.v(TAG, "setInexactRepeating: interval=" + interval - + " bucketTime=" + bucketTime); - mInexactDeliveryTimes[intervalSlot] = bucketTime; - setRepeating(type, bucketTime, interval, operation); + // Set the alarm based on the quantum-aligned start time + if (localLOGV) Slog.v(TAG, "setInexactRepeating: type=" + type + " interval=" + interval + + " trigger=" + adjustedTriggerTime + " orig=" + triggerAtTime); + setRepeating(type, adjustedTriggerTime, interval, operation); } public void setTime(long millis) { diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index e689654110b6..8e39a63fe372 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -1414,13 +1414,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { LinkProperties p = nt.getLinkProperties(); if (p == null) return; String interfaceName = p.getInterfaceName(); - InetAddress defaultGatewayAddr = p.getGateway(); + if (TextUtils.isEmpty(interfaceName)) return; + for (InetAddress gateway : p.getGateways()) { - if ((interfaceName != null) && (defaultGatewayAddr != null )) { - if (!NetworkUtils.addDefaultRoute(interfaceName, defaultGatewayAddr) && DBG) { + if (!NetworkUtils.addDefaultRoute(interfaceName, gateway) && DBG) { NetworkInfo networkInfo = nt.getNetworkInfo(); log("addDefaultRoute for " + networkInfo.getTypeName() + - " (" + interfaceName + "), GatewayAddr=" + defaultGatewayAddr); + " (" + interfaceName + "), GatewayAddr=" + gateway.getHostAddress()); } } } @@ -2142,7 +2142,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (proxy == null) proxy = new ProxyProperties("", 0, ""); log("sending Proxy Broadcast for " + proxy); Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING | + Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy); mContext.sendStickyBroadcast(intent); } diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index b2d534b64370..df2cd1b9572a 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -79,9 +79,9 @@ import java.util.Set; * Implementation of the device policy APIs. */ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { - private static final int REQUEST_EXPIRE_PASSWORD = 5571; + private static final String TAG = "DevicePolicyManagerService"; - static final String TAG = "DevicePolicyManagerService"; + private static final int REQUEST_EXPIRE_PASSWORD = 5571; private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * 86400 * 1000; // 5 days, in ms diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 6636fb732747..44b859098e05 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -26,7 +26,7 @@ import com.android.internal.view.IInputMethodManager; import com.android.internal.view.IInputMethodSession; import com.android.internal.view.InputBindResult; -import com.android.server.StatusBarManagerService; +import com.android.server.EventLogTags; import org.xmlpull.v1.XmlPullParserException; @@ -330,6 +330,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.DEFAULT_INPUT_METHOD), false, this); resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.ENABLED_INPUT_METHODS), false, this); + resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this); } @@ -594,13 +596,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (imi == null && mCurMethodId != null) { imi = mMethodMap.get(mCurMethodId); } - final List<InputMethodSubtype> enabledSubtypes = + List<InputMethodSubtype> enabledSubtypes = mSettings.getEnabledInputMethodSubtypeListLocked(imi); - if (!allowsImplicitlySelectedSubtypes || enabledSubtypes.size() > 0) { - return enabledSubtypes; - } else { - return getApplicableSubtypesLocked(mRes, getSubtypes(imi)); + if (allowsImplicitlySelectedSubtypes && enabledSubtypes.isEmpty()) { + enabledSubtypes = getApplicableSubtypesLocked(mRes, getSubtypes(imi)); } + return InputMethodSubtype.sort(mContext, 0, imi, enabledSubtypes); } public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi, @@ -1948,14 +1949,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private boolean canAddToLastInputMethod(InputMethodSubtype subtype) { if (subtype == null) return true; - String[] extraValues = subtype.getExtraValue().split(","); - final int N = extraValues.length; - for (int i = 0; i < N; ++i) { - if (SUBTYPE_EXTRAVALUE_EXCLUDE_FROM_LAST_IME.equals(extraValues[i])) { - return false; - } - } - return true; + return !subtype.containsExtraValueKey(SUBTYPE_EXTRAVALUE_EXCLUDE_FROM_LAST_IME); } private void saveCurrentInputMethodAndSubtypeToHistory() { diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/Installer.java index 85eca6062a41..08d1b82f2520 100644 --- a/services/java/com/android/server/Installer.java +++ b/services/java/com/android/server/Installer.java @@ -166,17 +166,11 @@ class Installer { } } - public int install(String name, boolean useEncryptedFilesystem, int uid, int gid) { + public int install(String name, int uid, int gid) { StringBuilder builder = new StringBuilder("install"); builder.append(' '); builder.append(name); builder.append(' '); - if (useEncryptedFilesystem) { - builder.append('1'); - } else { - builder.append('0'); - } - builder.append(' '); builder.append(uid); builder.append(' '); builder.append(gid); @@ -209,57 +203,33 @@ class Installer { return execute(builder.toString()); } - public int remove(String name, boolean useEncryptedFilesystem) { + public int remove(String name) { StringBuilder builder = new StringBuilder("remove"); builder.append(' '); builder.append(name); - builder.append(' '); - if (useEncryptedFilesystem) { - builder.append('1'); - } else { - builder.append('0'); - } return execute(builder.toString()); } - public int rename(String oldname, String newname, boolean useEncryptedFilesystem) { + public int rename(String oldname, String newname) { StringBuilder builder = new StringBuilder("rename"); builder.append(' '); builder.append(oldname); builder.append(' '); builder.append(newname); - builder.append(' '); - if (useEncryptedFilesystem) { - builder.append('1'); - } else { - builder.append('0'); - } return execute(builder.toString()); } - public int deleteCacheFiles(String name, boolean useEncryptedFilesystem) { + public int deleteCacheFiles(String name) { StringBuilder builder = new StringBuilder("rmcache"); builder.append(' '); builder.append(name); - builder.append(' '); - if (useEncryptedFilesystem) { - builder.append('1'); - } else { - builder.append('0'); - } return execute(builder.toString()); } - public int clearUserData(String name, boolean useEncryptedFilesystem) { + public int clearUserData(String name) { StringBuilder builder = new StringBuilder("rmuserdata"); builder.append(' '); builder.append(name); - builder.append(' '); - if (useEncryptedFilesystem) { - builder.append('1'); - } else { - builder.append('0'); - } return execute(builder.toString()); } @@ -292,8 +262,8 @@ class Installer { return execute(builder.toString()); } - public int getSizeInfo(String pkgName, String apkPath, - String fwdLockApkPath, PackageStats pStats, boolean useEncryptedFilesystem) { + public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath, + PackageStats pStats) { StringBuilder builder = new StringBuilder("getsize"); builder.append(' '); builder.append(pkgName); @@ -301,12 +271,6 @@ class Installer { builder.append(apkPath); builder.append(' '); builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); - builder.append(' '); - if (useEncryptedFilesystem) { - builder.append('1'); - } else { - builder.append('0'); - } String s = transaction(builder.toString()); String res[] = s.split(" "); diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java index a8b28403ee1a..e9ee12ca2b8b 100644 --- a/services/java/com/android/server/IntentResolver.java +++ b/services/java/com/android/server/IntentResolver.java @@ -27,6 +27,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import android.net.Uri; +import android.util.FastImmutableArraySet; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Slog; @@ -207,10 +209,11 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { final boolean debug = localLOGV || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); + FastImmutableArraySet<String> categories = getFastIntentCategories(intent); final String scheme = intent.getScheme(); int N = listCut.size(); for (int i = 0; i < N; ++i) { - buildResolveList(intent, debug, defaultOnly, + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, listCut.get(i), resultList); } sortResults(resultList); @@ -286,20 +289,21 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { if (debug) Slog.v(TAG, "Action list: " + firstTypeCut); } + FastImmutableArraySet<String> categories = getFastIntentCategories(intent); if (firstTypeCut != null) { - buildResolveList(intent, debug, defaultOnly, + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, firstTypeCut, finalList); } if (secondTypeCut != null) { - buildResolveList(intent, debug, defaultOnly, + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, secondTypeCut, finalList); } if (thirdTypeCut != null) { - buildResolveList(intent, debug, defaultOnly, + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, thirdTypeCut, finalList); } if (schemeCut != null) { - buildResolveList(intent, debug, defaultOnly, + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, schemeCut, finalList); } sortResults(finalList); @@ -478,9 +482,19 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { return false; } - private void buildResolveList(Intent intent, boolean debug, boolean defaultOnly, + private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) { + final Set<String> categories = intent.getCategories(); + if (categories == null) { + return null; + } + return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()])); + } + + private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories, + boolean debug, boolean defaultOnly, String resolvedType, String scheme, List<F> src, List<R> dest) { - Set<String> categories = intent.getCategories(); + final String action = intent.getAction(); + final Uri data = intent.getData(); final int N = src != null ? src.size() : 0; boolean hasNonDefaults = false; @@ -498,8 +512,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { continue; } - match = filter.match( - intent.getAction(), resolvedType, scheme, intent.getData(), categories, TAG); + match = filter.match(action, resolvedType, scheme, data, categories, TAG); if (match >= 0) { if (debug) Slog.v(TAG, " Filter matched! match=0x" + Integer.toHexString(match)); diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 152605f0a4ae..eaf68b0ed8cf 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -26,6 +26,8 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.net.InterfaceConfiguration; import android.net.INetworkManagementEventObserver; +import android.net.LinkAddress; +import android.net.NetworkUtils; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.os.INetworkManagementService; @@ -246,7 +248,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { } Slog.d(TAG, String.format("rsp <%s>", rsp)); - // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3] + // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz [flag1 flag2 flag3] StringTokenizer st = new StringTokenizer(rsp); InterfaceConfiguration cfg; @@ -265,18 +267,21 @@ class NetworkManagementService extends INetworkManagementService.Stub { cfg = new InterfaceConfiguration(); cfg.hwAddr = st.nextToken(" "); + InetAddress addr = null; + int prefixLength = 0; try { - cfg.addr = InetAddress.getByName(st.nextToken(" ")); + addr = InetAddress.getByName(st.nextToken(" ")); } catch (UnknownHostException uhe) { Slog.e(TAG, "Failed to parse ipaddr", uhe); } try { - cfg.mask = InetAddress.getByName(st.nextToken(" ")); - } catch (UnknownHostException uhe) { - Slog.e(TAG, "Failed to parse netmask", uhe); + prefixLength = Integer.parseInt(st.nextToken(" ")); + } catch (NumberFormatException nfe) { + Slog.e(TAG, "Failed to parse prefixLength", nfe); } + cfg.addr = new LinkAddress(addr, prefixLength); cfg.interfaceFlags = st.nextToken("]").trim() +"]"; } catch (NoSuchElementException nsee) { throw new IllegalStateException( @@ -288,8 +293,13 @@ class NetworkManagementService extends INetworkManagementService.Stub { public void setInterfaceConfig( String iface, InterfaceConfiguration cfg) throws IllegalStateException { - String cmd = String.format("interface setcfg %s %s %s %s", iface, - cfg.addr.getHostAddress(), cfg.mask.getHostAddress(), + LinkAddress linkAddr = cfg.addr; + if (linkAddr == null || linkAddr.getAddress() == null) { + throw new IllegalStateException("Null LinkAddress given"); + } + String cmd = String.format("interface setcfg %s %s %d %s", iface, + linkAddr.getAddress().getHostAddress(), + linkAddr.getNetworkPrefixLength(), cfg.interfaceFlags); try { mConnector.doCommand(cmd); @@ -600,11 +610,10 @@ class NetworkManagementService extends INetworkManagementService.Stub { * argv7 - Preamble * argv8 - Max SCB */ - String str = String.format("softap set " + wlanIface + " " + softapIface + - " %s %s %s", convertQuotedString(wifiConfig.SSID), - wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? - "wpa2-psk" : "open", - convertQuotedString(wifiConfig.preSharedKey)); + String str = String.format("softap set " + wlanIface + " " + softapIface + + " %s %s %s", convertQuotedString(wifiConfig.SSID), + getSecurityType(wifiConfig), + convertQuotedString(wifiConfig.preSharedKey)); mConnector.doCommand(str); } mConnector.doCommand(String.format("softap startap")); @@ -621,6 +630,17 @@ class NetworkManagementService extends INetworkManagementService.Stub { return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"'; } + private String getSecurityType(WifiConfiguration wifiConfig) { + switch (wifiConfig.getAuthType()) { + case KeyMgmt.WPA_PSK: + return "wpa-psk"; + case KeyMgmt.WPA2_PSK: + return "wpa2-psk"; + default: + return "open"; + } + } + public void stopAccessPoint() throws IllegalStateException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); @@ -646,7 +666,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { } else { String str = String.format("softap set " + wlanIface + " " + softapIface + " %s %s %s", convertQuotedString(wifiConfig.SSID), - wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? "wpa2-psk" : "open", + getSecurityType(wifiConfig), convertQuotedString(wifiConfig.preSharedKey)); mConnector.doCommand(str); } diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 9ee71e840186..e54e2152f1b6 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -216,10 +216,6 @@ class PackageManagerService extends IPackageManager.Stub { // This is where all application persistent data goes. final File mAppDataDir; - // If Encrypted File System feature is enabled, all application persistent data - // should go here instead. - final File mSecureAppDataDir; - // This is the object monitoring the framework dir. final FileObserver mFrameworkInstallObserver; @@ -785,7 +781,6 @@ class PackageManagerService extends IPackageManager.Stub { File dataDir = Environment.getDataDirectory(); mAppDataDir = new File(dataDir, "data"); - mSecureAppDataDir = new File(dataDir, "secure/data"); mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); if (mInstaller == null) { @@ -795,7 +790,6 @@ class PackageManagerService extends IPackageManager.Stub { File miscDir = new File(dataDir, "misc"); miscDir.mkdirs(); mAppDataDir.mkdirs(); - mSecureAppDataDir.mkdirs(); mDrmAppPrivateInstallDir.mkdirs(); } @@ -964,9 +958,7 @@ class PackageManagerService extends IPackageManager.Stub { + " no longer exists; wiping its data"; reportSettingsProblem(Log.WARN, msg); if (mInstaller != null) { - // XXX how to set useEncryptedFSDir for packages that - // are not encrypted? - mInstaller.remove(ps.name, true); + mInstaller.remove(ps.name); } } } @@ -1050,8 +1042,7 @@ class PackageManagerService extends IPackageManager.Stub { void cleanupInstallFailedPackage(PackageSetting ps) { Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name); if (mInstaller != null) { - boolean useSecureFS = false; - int retCode = mInstaller.remove(ps.name, useSecureFS); + int retCode = mInstaller.remove(ps.name); if (retCode < 0) { Slog.w(TAG, "Couldn't remove app data directory for package: " + ps.name + ", retcode=" + retCode); @@ -2796,13 +2787,7 @@ class PackageManagerService extends IPackageManager.Stub { } private File getDataPathForPackage(PackageParser.Package pkg) { - boolean useEncryptedFSDir = false; - File dataPath; - if (useEncryptedFSDir) { - dataPath = new File(mSecureAppDataDir, pkg.packageName); - } else { - dataPath = new File(mAppDataDir, pkg.packageName); - } + final File dataPath = new File(mAppDataDir, pkg.packageName); return dataPath; } @@ -3132,7 +3117,6 @@ class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.dataDir = dataPath.getPath(); } else { // This is a normal package, need to make its data directory. - boolean useEncryptedFSDir = false; dataPath = getDataPathForPackage(pkg); boolean uidError = false; @@ -3149,7 +3133,7 @@ class PackageManagerService extends IPackageManager.Stub { // If this is a system app, we can at least delete its // current data so the application will still work. if (mInstaller != null) { - int ret = mInstaller.remove(pkgName, useEncryptedFSDir); + int ret = mInstaller.remove(pkgName); if (ret >= 0) { // Old data gone! String msg = "System package " + pkg.packageName @@ -3160,7 +3144,7 @@ class PackageManagerService extends IPackageManager.Stub { recovered = true; // And now re-install the app. - ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid, + ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.uid); if (ret == -1) { // Ack should not happen! @@ -3201,7 +3185,7 @@ class PackageManagerService extends IPackageManager.Stub { Log.v(TAG, "Want this data dir: " + dataPath); //invoke installer to do the actual installation if (mInstaller != null) { - int ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid, + int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.uid); if(ret < 0) { // Error from installer @@ -6293,9 +6277,8 @@ class PackageManagerService extends IPackageManager.Stub { deletedPs = mSettings.mPackages.get(packageName); } if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { - boolean useEncryptedFSDir = false; if (mInstaller != null) { - int retCode = mInstaller.remove(packageName, useEncryptedFSDir); + int retCode = mInstaller.remove(packageName); if (retCode < 0) { Slog.w(TAG, "Couldn't remove app data or cache directory for package: " + packageName + ", retcode=" + retCode); @@ -6535,7 +6518,6 @@ class PackageManagerService extends IPackageManager.Stub { p = ps.pkg; } } - boolean useEncryptedFSDir = false; if (!dataOnly) { //need to check this only for fully installed applications @@ -6550,7 +6532,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (mInstaller != null) { - int retCode = mInstaller.clearUserData(packageName, useEncryptedFSDir); + int retCode = mInstaller.clearUserData(packageName); if (retCode < 0) { Slog.w(TAG, "Couldn't remove cache files for package: " + packageName); @@ -6601,9 +6583,8 @@ class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); return false; } - boolean useEncryptedFSDir = false; if (mInstaller != null) { - int retCode = mInstaller.deleteCacheFiles(packageName, useEncryptedFSDir); + int retCode = mInstaller.deleteCacheFiles(packageName); if (retCode < 0) { Slog.w(TAG, "Couldn't remove cache files for package: " + packageName); @@ -6663,10 +6644,8 @@ class PackageManagerService extends IPackageManager.Stub { } publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null; } - boolean useEncryptedFSDir = false; if (mInstaller != null) { - int res = mInstaller.getSizeInfo(packageName, p.mPath, - publicSrcDir, pStats, useEncryptedFSDir); + int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, pStats); if (res < 0) { return false; } else { diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index caf637600c11..d80a2cd41c2d 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -40,7 +40,6 @@ import android.hardware.SensorManager; import android.os.BatteryManager; import android.os.BatteryStats; import android.os.Binder; -import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -50,7 +49,6 @@ import android.os.Power; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.WorkSource; import android.provider.Settings.SettingNotFoundException; @@ -69,14 +67,13 @@ import static android.provider.Settings.System.WINDOW_ANIMATION_SCALE; import static android.provider.Settings.System.TRANSITION_ANIMATION_SCALE; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.Observable; import java.util.Observer; -class PowerManagerService extends IPowerManager.Stub +public class PowerManagerService extends IPowerManager.Stub implements LocalPowerManager, Watchdog.Monitor { private static final String TAG = "PowerManagerService"; @@ -2689,7 +2686,7 @@ class PowerManagerService extends IPowerManager.Stub } } - void setPolicy(WindowManagerPolicy p) { + public void setPolicy(WindowManagerPolicy p) { synchronized (mLocks) { mPolicy = p; mLocks.notifyAll(); diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java index 5ada77bba9a2..8df817789af2 100644 --- a/services/java/com/android/server/StatusBarManagerService.java +++ b/services/java/com/android/server/StatusBarManagerService.java @@ -39,6 +39,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; import com.android.internal.statusbar.StatusBarNotification; +import com.android.server.wm.WindowManagerService; import java.io.FileDescriptor; import java.io.PrintWriter; diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java index a1f43b4bdc81..80b0174b22c3 100644 --- a/services/java/com/android/server/SystemBackupAgent.java +++ b/services/java/com/android/server/SystemBackupAgent.java @@ -16,6 +16,7 @@ package com.android.server; + import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.BackupAgentHelper; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 33e9908c76c8..52c47e172ae9 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -17,6 +17,7 @@ package com.android.server; import com.android.server.am.ActivityManagerService; +import com.android.server.wm.WindowManagerService; import com.android.internal.app.ShutdownThread; import com.android.internal.os.BinderInternal; import com.android.internal.os.SamplingProfilerIntegration; @@ -405,6 +406,7 @@ class ServerThread extends Thread { Slog.i(TAG, "USB Observer"); // Listen for USB changes usb = new UsbService(context); + ServiceManager.addService(Context.USB_SERVICE, usb); } catch (Throwable e) { Slog.e(TAG, "Failure starting UsbService", e); } diff --git a/services/java/com/android/server/UsbService.java b/services/java/com/android/server/UsbService.java index 8ef03d436ad2..af4c425c24e7 100644 --- a/services/java/com/android/server/UsbService.java +++ b/services/java/com/android/server/UsbService.java @@ -19,10 +19,19 @@ package com.android.server; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.hardware.IUsbManager; +import android.hardware.UsbAccessory; +import android.hardware.UsbConstants; +import android.hardware.UsbDevice; +import android.hardware.UsbEndpoint; +import android.hardware.UsbInterface; import android.hardware.UsbManager; import android.net.Uri; +import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.os.Parcelable; +import android.os.ParcelFileDescriptor; import android.os.UEventObserver; import android.provider.Settings; import android.util.Log; @@ -32,11 +41,12 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.util.ArrayList; +import java.util.HashMap; /** * <p>UsbService monitors for changes to USB state. */ -class UsbService { +class UsbService extends IUsbManager.Stub { private static final String TAG = UsbService.class.getSimpleName(); private static final boolean LOG = false; @@ -68,14 +78,64 @@ class UsbService { private int mLastConnected = -1; private int mLastConfiguration = -1; - // lists of enabled and disabled USB functions + // lists of enabled and disabled USB functions (for USB device mode) + // synchronize on mEnabledFunctions when using either of these lists private final ArrayList<String> mEnabledFunctions = new ArrayList<String>(); private final ArrayList<String> mDisabledFunctions = new ArrayList<String>(); + // contains all connected USB devices (for USB host mode) + private final HashMap<String,UsbDevice> mDevices = new HashMap<String,UsbDevice>(); + + // USB busses to exclude from USB host support + private final String[] mHostBlacklist; + private boolean mSystemReady; + private UsbAccessory mCurrentAccessory; private final Context mContext; + private final void functionEnabled(String function, boolean enabled) { + synchronized (mEnabledFunctions) { + if (enabled) { + if (!mEnabledFunctions.contains(function)) { + mEnabledFunctions.add(function); + } + mDisabledFunctions.remove(function); + } else { + if (!mDisabledFunctions.contains(function)) { + mDisabledFunctions.add(function); + } + mEnabledFunctions.remove(function); + } + } + + if (enabled && UsbManager.USB_FUNCTION_ACCESSORY.equals(function)) { + String[] strings = nativeGetAccessoryStrings(); + if (strings != null) { + Log.d(TAG, "entering USB accessory mode"); + mCurrentAccessory = new UsbAccessory(strings); + Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED); + intent.putExtra(UsbManager.EXTRA_ACCESSORY, mCurrentAccessory); + // add strings as separate extras to allow filtering + if (strings[0] != null) { + intent.putExtra(UsbManager.EXTRA_ACCESSORY_MANUFACTURER, strings[0]); + } + if (strings[1] != null) { + intent.putExtra(UsbManager.EXTRA_ACCESSORY_PRODUCT, strings[1]); + } + if (strings[2] != null) { + intent.putExtra(UsbManager.EXTRA_ACCESSORY_TYPE, strings[2]); + } + if (strings[3] != null) { + intent.putExtra(UsbManager.EXTRA_ACCESSORY_VERSION, strings[3]); + } + mContext.sendBroadcast(intent); + } else { + Log.e(TAG, "nativeGetAccessoryStrings failed"); + } + } + } + private final UEventObserver mUEventObserver = new UEventObserver() { @Override public void onUEvent(UEventObserver.UEvent event) { @@ -113,17 +173,7 @@ class UsbService { // Note: we do not broadcast a change when a function is enabled or disabled. // We just record the state change for the next broadcast. boolean enabled = "1".equals(enabledStr); - if (enabled) { - if (!mEnabledFunctions.contains(function)) { - mEnabledFunctions.add(function); - } - mDisabledFunctions.remove(function); - } else { - if (!mDisabledFunctions.contains(function)) { - mDisabledFunctions.add(function); - } - mEnabledFunctions.remove(function); - } + functionEnabled(function, enabled); } } } @@ -132,6 +182,9 @@ class UsbService { public UsbService(Context context) { mContext = context; + mHostBlacklist = context.getResources().getStringArray( + com.android.internal.R.array.config_usbHostBlacklist); + init(); // set initial status if (mConfiguration >= 0) { @@ -165,18 +218,20 @@ class UsbService { return; try { - File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles(); - for (int i = 0; i < files.length; i++) { - File file = new File(files[i], "enable"); - FileReader reader = new FileReader(file); - int len = reader.read(buffer, 0, 1024); - reader.close(); - int value = Integer.valueOf((new String(buffer, 0, len)).trim()); - String functionName = files[i].getName(); - if (value == 1) { - mEnabledFunctions.add(functionName); - } else { - mDisabledFunctions.add(functionName); + synchronized (mEnabledFunctions) { + File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles(); + for (int i = 0; i < files.length; i++) { + File file = new File(files[i], "enable"); + FileReader reader = new FileReader(file); + int len = reader.read(buffer, 0, 1024); + reader.close(); + int value = Integer.valueOf((new String(buffer, 0, len)).trim()); + String functionName = files[i].getName(); + if (value == 1) { + mEnabledFunctions.add(functionName); + } else { + mDisabledFunctions.add(functionName); + } } } } catch (FileNotFoundException e) { @@ -186,8 +241,125 @@ class UsbService { } } + private boolean isBlackListed(String deviceName) { + int count = mHostBlacklist.length; + for (int i = 0; i < count; i++) { + if (deviceName.startsWith(mHostBlacklist[i])) { + return true; + } + } + return false; + } + + private boolean isBlackListed(int clazz, int subClass, int protocol) { + // blacklist hubs + if (clazz == UsbConstants.USB_CLASS_HUB) return true; + + // blacklist HID boot devices (mouse and keyboard) + if (clazz == UsbConstants.USB_CLASS_HID && + subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) { + return true; + } + + return false; + } + + // called from JNI in monitorUsbHostBus() + private void usbDeviceAdded(String deviceName, int vendorID, int productID, + int deviceClass, int deviceSubclass, int deviceProtocol, + /* array of quintuples containing id, class, subclass, protocol + and number of endpoints for each interface */ + int[] interfaceValues, + /* array of quadruples containing address, attributes, max packet size + and interval for each endpoint */ + int[] endpointValues) { + + if (isBlackListed(deviceName) || + isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) { + return; + } + + synchronized (mDevices) { + if (mDevices.get(deviceName) != null) { + Log.w(TAG, "device already on mDevices list: " + deviceName); + return; + } + + int numInterfaces = interfaceValues.length / 5; + Parcelable[] interfaces = new UsbInterface[numInterfaces]; + try { + // repackage interfaceValues as an array of UsbInterface + int intf, endp, ival = 0, eval = 0; + for (intf = 0; intf < numInterfaces; intf++) { + int interfaceId = interfaceValues[ival++]; + int interfaceClass = interfaceValues[ival++]; + int interfaceSubclass = interfaceValues[ival++]; + int interfaceProtocol = interfaceValues[ival++]; + int numEndpoints = interfaceValues[ival++]; + + Parcelable[] endpoints = new UsbEndpoint[numEndpoints]; + for (endp = 0; endp < numEndpoints; endp++) { + int address = endpointValues[eval++]; + int attributes = endpointValues[eval++]; + int maxPacketSize = endpointValues[eval++]; + int interval = endpointValues[eval++]; + endpoints[endp] = new UsbEndpoint(address, attributes, + maxPacketSize, interval); + } + + // don't allow if any interfaces are blacklisted + if (isBlackListed(interfaceClass, interfaceSubclass, interfaceProtocol)) { + return; + } + interfaces[intf] = new UsbInterface(interfaceId, interfaceClass, + interfaceSubclass, interfaceProtocol, endpoints); + } + } catch (Exception e) { + // beware of index out of bound exceptions, which might happen if + // a device does not set bNumEndpoints correctly + Log.e(TAG, "error parsing USB descriptors", e); + return; + } + + UsbDevice device = new UsbDevice(deviceName, vendorID, productID, + deviceClass, deviceSubclass, deviceProtocol, interfaces); + mDevices.put(deviceName, device); + + Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); + intent.putExtra(UsbManager.EXTRA_DEVICE_NAME, deviceName); + intent.putExtra(UsbManager.EXTRA_VENDOR_ID, vendorID); + intent.putExtra(UsbManager.EXTRA_PRODUCT_ID, productID); + intent.putExtra(UsbManager.EXTRA_DEVICE_CLASS, deviceClass); + intent.putExtra(UsbManager.EXTRA_DEVICE_SUBCLASS, deviceSubclass); + intent.putExtra(UsbManager.EXTRA_DEVICE_PROTOCOL, deviceProtocol); + intent.putExtra(UsbManager.EXTRA_DEVICE, device); + Log.d(TAG, "usbDeviceAdded, sending " + intent); + mContext.sendBroadcast(intent); + } + } + + // called from JNI in monitorUsbHostBus() + private void usbDeviceRemoved(String deviceName) { + synchronized (mDevices) { + UsbDevice device = mDevices.remove(deviceName); + if (device != null) { + Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED); + intent.putExtra(UsbManager.EXTRA_DEVICE_NAME, deviceName); + Log.d(TAG, "usbDeviceRemoved, sending " + intent); + mContext.sendBroadcast(intent); + } + } + } + private void initHostSupport() { - // temporarily disabled + // Create a thread to call into native code to wait for USB host events. + // This thread will call us back on usbDeviceAdded and usbDeviceRemoved. + Runnable runnable = new Runnable() { + public void run() { + monitorUsbHostBus(); + } + }; + new Thread(null, runnable, "UsbService host thread").start(); } void systemReady() { @@ -208,14 +380,49 @@ class UsbService { mHandler.sendEmptyMessageDelayed(MSG_UPDATE, delayed ? UPDATE_DELAY : 0); } + /* Returns a list of all currently attached USB devices */ + public void getDeviceList(Bundle devices) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null); + synchronized (mDevices) { + for (String name : mDevices.keySet()) { + devices.putParcelable(name, mDevices.get(name)); + } + } + } + + public ParcelFileDescriptor openDevice(String deviceName) { + if (isBlackListed(deviceName)) { + throw new SecurityException("USB device is on a restricted bus"); + } + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null); + if (mDevices.get(deviceName) == null) { + // if it is not in mDevices, it either does not exist or is blacklisted + throw new IllegalArgumentException( + "device " + deviceName + " does not exist or is restricted"); + } + return nativeOpenDevice(deviceName); + } + + public UsbAccessory getCurrentAccessory() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null); + return mCurrentAccessory; + } + + public ParcelFileDescriptor openAccessory() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null); + return nativeOpenAccessory(); + } + private final Handler mHandler = new Handler() { private void addEnabledFunctions(Intent intent) { + synchronized (mEnabledFunctions) { // include state of all USB functions in our extras - for (int i = 0; i < mEnabledFunctions.size(); i++) { - intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED); - } - for (int i = 0; i < mDisabledFunctions.size(); i++) { - intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED); + for (int i = 0; i < mEnabledFunctions.size(); i++) { + intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED); + } + for (int i = 0; i < mDisabledFunctions.size(); i++) { + intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED); + } } } @@ -225,6 +432,26 @@ class UsbService { case MSG_UPDATE: synchronized (this) { if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) { + if (mConnected == 0 && mCurrentAccessory != null) { + // turn off accessory mode when we are disconnected + if (UsbManager.setFunctionEnabled( + UsbManager.USB_FUNCTION_ACCESSORY, false)) { + Log.d(TAG, "exited USB accessory mode"); + + Intent intent = new Intent( + UsbManager.ACTION_USB_ACCESSORY_DETACHED); + intent.putExtra(UsbManager.EXTRA_ACCESSORY, mCurrentAccessory); + mContext.sendBroadcast(intent); + mCurrentAccessory = null; + + // this will cause an immediate reset of the USB bus, + // so there is no point in sending the + // function disabled broadcast. + return; + } else { + Log.e(TAG, "could not disable USB_FUNCTION_ACCESSORY"); + } + } final ContentResolver cr = mContext.getContentResolver(); if (Settings.Secure.getInt(cr, @@ -249,4 +476,9 @@ class UsbService { } } }; + + private native void monitorUsbHostBus(); + private native ParcelFileDescriptor nativeOpenDevice(String deviceName); + private native String[] nativeGetAccessoryStrings(); + private native ParcelFileDescriptor nativeOpenAccessory(); } diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index 997e75039072..b1a6a9a5a42c 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -70,8 +70,6 @@ import com.android.internal.content.PackageMonitor; import com.android.internal.service.wallpaper.ImageWallpaper; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; -import com.android.server.DevicePolicyManagerService.ActiveAdmin; -import com.android.server.DevicePolicyManagerService.MyPackageMonitor; class WallpaperManagerService extends IWallpaperManager.Stub { static final String TAG = "WallpaperService"; diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index d2f2ec701910..cc25e8d09996 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -39,24 +39,26 @@ import android.net.wifi.WifiConfiguration.KeyMgmt; import android.net.wifi.WpsConfiguration; import android.net.wifi.WpsResult; import android.net.ConnectivityManager; -import android.net.InterfaceConfiguration; import android.net.DhcpInfo; import android.net.NetworkInfo; import android.net.NetworkInfo.State; +import android.net.NetworkInfo.DetailedState; +import android.net.TrafficStats; import android.os.Binder; import android.os.Handler; +import android.os.Messenger; import android.os.HandlerThread; import android.os.IBinder; import android.os.INetworkManagementService; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.os.WorkSource; import android.provider.Settings; import android.text.TextUtils; import android.util.Slog; -import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -113,8 +115,19 @@ public class WifiService extends IWifiManager.Stub { private final IBatteryStats mBatteryStats; - ConnectivityManager mCm; - private String[] mWifiRegexs; + private boolean mEnableTrafficStatsPoll = false; + private int mTrafficStatsPollToken = 0; + private long mTxPkts; + private long mRxPkts; + /* Tracks last reported data activity */ + private int mDataActivity; + private String mInterfaceName; + + /** + * Interval in milliseconds between polling for traffic + * statistics + */ + private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000; /** * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a @@ -128,6 +141,9 @@ public class WifiService extends IWifiManager.Stub { private static final String ACTION_DEVICE_IDLE = "com.android.server.WifiManager.action.DEVICE_IDLE"; + private static final int CMD_ENABLE_TRAFFIC_STATS_POLL = 1; + private static final int CMD_TRAFFIC_STATS_POLL = 2; + private boolean mIsReceiverRegistered = false; @@ -185,18 +201,20 @@ public class WifiService extends IWifiManager.Stub { /** * Asynchronous channel to WifiStateMachine */ - private AsyncChannel mChannel; + private AsyncChannel mWifiStateMachineChannel; + + /** + * Clients receiving asynchronous messages + */ + private List<AsyncChannel> mClients = new ArrayList<AsyncChannel>(); /** - * TODO: Possibly change WifiService into an AsyncService. + * Handles client connections */ - private class WifiServiceHandler extends Handler { - private AsyncChannel mWshChannel; + private class AsyncServiceHandler extends Handler { - WifiServiceHandler(android.os.Looper looper, Context context) { + AsyncServiceHandler(android.os.Looper looper) { super(looper); - mWshChannel = new AsyncChannel(); - mWshChannel.connect(context, this, mWifiStateMachine.getHandler()); } @Override @@ -204,11 +222,33 @@ public class WifiService extends IWifiManager.Stub { switch (msg.what) { case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - mChannel = mWshChannel; + Slog.d(TAG, "New client listening to asynchronous messages"); + mClients.add((AsyncChannel) msg.obj); } else { - Slog.d(TAG, "WifiServicehandler.handleMessage could not connect error=" + - msg.arg1); - mChannel = null; + Slog.e(TAG, "Client connection failure, error=" + msg.arg1); + } + break; + } + case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { + AsyncChannel ac = new AsyncChannel(); + ac.connect(mContext, this, msg.replyTo); + break; + } + case CMD_ENABLE_TRAFFIC_STATS_POLL: { + mEnableTrafficStatsPoll = (msg.arg1 == 1); + mTrafficStatsPollToken++; + if (mEnableTrafficStatsPoll) { + notifyOnDataActivity(); + sendMessageDelayed(Message.obtain(this, CMD_TRAFFIC_STATS_POLL, + mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS); + } + break; + } + case CMD_TRAFFIC_STATS_POLL: { + if (msg.arg1 == mTrafficStatsPollToken) { + notifyOnDataActivity(); + sendMessageDelayed(Message.obtain(this, CMD_TRAFFIC_STATS_POLL, + mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS); } break; } @@ -219,7 +259,40 @@ public class WifiService extends IWifiManager.Stub { } } } - WifiServiceHandler mHandler; + private AsyncServiceHandler mAsyncServiceHandler; + + /** + * Handles interaction with WifiStateMachine + */ + private class WifiStateMachineHandler extends Handler { + private AsyncChannel mWsmChannel; + + WifiStateMachineHandler(android.os.Looper looper) { + super(looper); + mWsmChannel = new AsyncChannel(); + mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler()); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { + if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { + mWifiStateMachineChannel = mWsmChannel; + } else { + Slog.e(TAG, "WifiStateMachine connection failure, error=" + msg.arg1); + mWifiStateMachineChannel = null; + } + break; + } + default: { + Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg); + break; + } + } + } + } + WifiStateMachineHandler mWifiStateMachineHandler; /** * Temporary for computing UIDS that are responsible for starting WIFI. @@ -229,7 +302,10 @@ public class WifiService extends IWifiManager.Stub { WifiService(Context context) { mContext = context; - mWifiStateMachine = new WifiStateMachine(mContext); + + mInterfaceName = SystemProperties.get("wifi.interface", "wlan0"); + + mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName); mWifiStateMachine.enableRssiPolling(true); mBatteryStats = BatteryStatsService.getService(); @@ -237,10 +313,6 @@ public class WifiService extends IWifiManager.Stub { Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null); mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0); - HandlerThread wifiThread = new HandlerThread("WifiService"); - wifiThread.start(); - mHandler = new WifiServiceHandler(wifiThread.getLooper(), context); - mContext.registerReceiver( new BroadcastReceiver() { @Override @@ -256,20 +328,6 @@ public class WifiService extends IWifiManager.Stub { }, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); - mContext.registerReceiver( - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - - ArrayList<String> available = intent.getStringArrayListExtra( - ConnectivityManager.EXTRA_AVAILABLE_TETHER); - ArrayList<String> active = intent.getStringArrayListExtra( - ConnectivityManager.EXTRA_ACTIVE_TETHER); - updateTetherState(available, active); - - } - },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); - IntentFilter filter = new IntentFilter(); filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); @@ -290,6 +348,7 @@ public class WifiService extends IWifiManager.Stub { switch(mNetworkInfo.getDetailedState()) { case CONNECTED: case DISCONNECTED: + evaluateTrafficStatsPolling(); resetNotification(); break; } @@ -300,6 +359,11 @@ public class WifiService extends IWifiManager.Stub { } }, filter); + HandlerThread wifiThread = new HandlerThread("WifiService"); + wifiThread.start(); + mAsyncServiceHandler = new AsyncServiceHandler(wifiThread.getLooper()); + mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper()); + // Setting is in seconds NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l; @@ -322,52 +386,6 @@ public class WifiService extends IWifiManager.Stub { setWifiEnabled(wifiEnabled); } - private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) { - - boolean wifiTethered = false; - boolean wifiAvailable = false; - - IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); - INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); - - if (mCm == null) { - mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - } - - mWifiRegexs = mCm.getTetherableWifiRegexs(); - - for (String intf : available) { - for (String regex : mWifiRegexs) { - if (intf.matches(regex)) { - - InterfaceConfiguration ifcg = null; - try { - ifcg = service.getInterfaceConfig(intf); - if (ifcg != null) { - /* IP/netmask: 192.168.43.1/255.255.255.0 */ - ifcg.addr = InetAddress.getByName("192.168.43.1"); - ifcg.mask = InetAddress.getByName("255.255.255.0"); - ifcg.interfaceFlags = "[up]"; - - service.setInterfaceConfig(intf, ifcg); - } - } catch (Exception e) { - Slog.e(TAG, "Error configuring interface " + intf + ", :" + e); - setWifiApEnabled(null, false); - return; - } - - if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { - Slog.e(TAG, "Error tethering on " + intf); - setWifiApEnabled(null, false); - return; - } - break; - } - } - } - } - private boolean testAndClearWifiSavedState() { final ContentResolver cr = mContext.getContentResolver(); int wifiSavedState = 0; @@ -402,10 +420,10 @@ public class WifiService extends IWifiManager.Stub { */ public boolean pingSupplicant() { enforceAccessPermission(); - if (mChannel != null) { - return mWifiStateMachine.syncPingSupplicant(mChannel); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncPingSupplicant(mWifiStateMachineChannel); } else { - Slog.e(TAG, "mChannel is not initialized"); + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return false; } } @@ -566,15 +584,12 @@ public class WifiService extends IWifiManager.Stub { public synchronized void setWifiApConfiguration(WifiConfiguration wifiConfig) { enforceChangePermission(); final ContentResolver cr = mContext.getContentResolver(); - boolean isWpa; if (wifiConfig == null) return; + int authType = wifiConfig.getAuthType(); Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_SSID, wifiConfig.SSID); - isWpa = wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK); - Settings.Secure.putInt(cr, - Settings.Secure.WIFI_AP_SECURITY, - isWpa ? KeyMgmt.WPA_PSK : KeyMgmt.NONE); - if (isWpa) + Settings.Secure.putInt(cr, Settings.Secure.WIFI_AP_SECURITY, authType); + if (authType != KeyMgmt.NONE) Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_PASSWD, wifiConfig.preSharedKey); } @@ -618,10 +633,10 @@ public class WifiService extends IWifiManager.Stub { */ public int addOrUpdateNetwork(WifiConfiguration config) { enforceChangePermission(); - if (mChannel != null) { - return mWifiStateMachine.syncAddOrUpdateNetwork(mChannel, config); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncAddOrUpdateNetwork(mWifiStateMachineChannel, config); } else { - Slog.e(TAG, "mChannel is not initialized"); + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return -1; } } @@ -634,10 +649,10 @@ public class WifiService extends IWifiManager.Stub { */ public boolean removeNetwork(int netId) { enforceChangePermission(); - if (mChannel != null) { - return mWifiStateMachine.syncRemoveNetwork(mChannel, netId); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncRemoveNetwork(mWifiStateMachineChannel, netId); } else { - Slog.e(TAG, "mChannel is not initialized"); + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return false; } } @@ -651,10 +666,11 @@ public class WifiService extends IWifiManager.Stub { */ public boolean enableNetwork(int netId, boolean disableOthers) { enforceChangePermission(); - if (mChannel != null) { - return mWifiStateMachine.syncEnableNetwork(mChannel, netId, disableOthers); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, netId, + disableOthers); } else { - Slog.e(TAG, "mChannel is not initialized"); + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return false; } } @@ -667,10 +683,10 @@ public class WifiService extends IWifiManager.Stub { */ public boolean disableNetwork(int netId) { enforceChangePermission(); - if (mChannel != null) { - return mWifiStateMachine.syncDisableNetwork(mChannel, netId); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncDisableNetwork(mWifiStateMachineChannel, netId); } else { - Slog.e(TAG, "mChannel is not initialized"); + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return false; } } @@ -707,10 +723,10 @@ public class WifiService extends IWifiManager.Stub { public boolean saveConfiguration() { boolean result = true; enforceChangePermission(); - if (mChannel != null) { - return mWifiStateMachine.syncSaveConfig(mChannel); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncSaveConfig(mWifiStateMachineChannel); } else { - Slog.e(TAG, "mChannel is not initialized"); + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return false; } } @@ -844,14 +860,23 @@ public class WifiService extends IWifiManager.Stub { public WpsResult startWps(WpsConfiguration config) { enforceChangePermission(); - if (mChannel != null) { - return mWifiStateMachine.startWps(mChannel, config); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.startWps(mWifiStateMachineChannel, config); } else { - Slog.e(TAG, "mChannel is not initialized"); + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return new WpsResult(WpsResult.Status.FAILURE); } } + /** + * Get a reference to handler. This is used by a client to establish + * an AsyncChannel communication with WifiService + */ + public Messenger getMessenger() { + enforceAccessPermission(); + return new Messenger(mAsyncServiceHandler); + } + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -873,6 +898,7 @@ public class WifiService extends IWifiManager.Stub { // Once the screen is on, we are not keeping WIFI running // because of any locks so clear that tracking immediately. reportStartWorkSource(); + evaluateTrafficStatsPolling(); mWifiStateMachine.enableRssiPolling(true); mWifiStateMachine.enableAllNetworks(); updateWifiState(); @@ -881,6 +907,7 @@ public class WifiService extends IWifiManager.Stub { Slog.d(TAG, "ACTION_SCREEN_OFF"); } mScreenOff = true; + evaluateTrafficStatsPolling(); mWifiStateMachine.enableRssiPolling(false); /* * Set a timer to put Wi-Fi to sleep, but only if the screen is off @@ -1484,6 +1511,48 @@ public class WifiService extends IWifiManager.Stub { } } + /** + * Evaluate if traffic stats polling is needed based on + * connection and screen on status + */ + private void evaluateTrafficStatsPolling() { + Message msg; + if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED && !mScreenOff) { + msg = Message.obtain(mAsyncServiceHandler, CMD_ENABLE_TRAFFIC_STATS_POLL, 1, 0); + } else { + msg = Message.obtain(mAsyncServiceHandler, CMD_ENABLE_TRAFFIC_STATS_POLL, 0, 0); + } + msg.sendToTarget(); + } + + private void notifyOnDataActivity() { + long sent, received; + long preTxPkts = mTxPkts, preRxPkts = mRxPkts; + int dataActivity = WifiManager.DATA_ACTIVITY_NONE; + + mTxPkts = TrafficStats.getTxPackets(mInterfaceName); + mRxPkts = TrafficStats.getRxPackets(mInterfaceName); + + if (preTxPkts > 0 || preRxPkts > 0) { + sent = mTxPkts - preTxPkts; + received = mRxPkts - preRxPkts; + if (sent > 0) { + dataActivity |= WifiManager.DATA_ACTIVITY_OUT; + } + if (received > 0) { + dataActivity |= WifiManager.DATA_ACTIVITY_IN; + } + + if (dataActivity != mDataActivity && !mScreenOff) { + mDataActivity = dataActivity; + for (AsyncChannel client : mClients) { + client.sendMessage(WifiManager.DATA_ACTIVITY_NOTIFICATION, mDataActivity); + } + } + } + } + + private void checkAndSetNotification() { // If we shouldn't place a notification on available networks, then // don't bother doing any of the following diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java index 46d6befc0c0e..94531bbb8e2c 100644 --- a/services/java/com/android/server/WifiWatchdogService.java +++ b/services/java/com/android/server/WifiWatchdogService.java @@ -22,8 +22,9 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; +import android.net.ConnectivityManager; +import android.net.LinkProperties; import android.net.NetworkInfo; -import android.net.DhcpInfo; import android.net.wifi.ScanResult; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; @@ -42,6 +43,7 @@ import java.net.InetAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; +import java.util.Collection; import java.util.List; import java.util.Random; @@ -77,7 +79,8 @@ public class WifiWatchdogService { private Context mContext; private ContentResolver mContentResolver; private WifiManager mWifiManager; - + private ConnectivityManager mConnectivityManager; + /** * The main watchdog thread. */ @@ -310,19 +313,26 @@ public class WifiWatchdogService { } /** - * Gets the DNS of the current AP. + * Gets the first DNS of the current AP. * - * @return The DNS of the current AP. + * @return The first DNS of the current AP. */ - private int getDns() { - DhcpInfo addressInfo = mWifiManager.getDhcpInfo(); - if (addressInfo != null) { - return addressInfo.dns1; - } else { - return -1; + private InetAddress getDns() { + if (mConnectivityManager == null) { + mConnectivityManager = (ConnectivityManager)mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); } + + LinkProperties linkProperties = mConnectivityManager.getLinkProperties( + ConnectivityManager.TYPE_WIFI); + if (linkProperties == null) return null; + + Collection<InetAddress> dnses = linkProperties.getDnses(); + if (dnses == null || dnses.size() == 0) return null; + + return dnses.iterator().next(); } - + /** * Checks whether the DNS can be reached using multiple attempts according * to the current setting values. @@ -330,29 +340,28 @@ public class WifiWatchdogService { * @return Whether the DNS is reachable */ private boolean checkDnsConnectivity() { - int dns = getDns(); - if (dns == -1) { + InetAddress dns = getDns(); + if (dns == null) { if (V) { myLogV("checkDnsConnectivity: Invalid DNS, returning false"); } return false; } - + if (V) { - myLogV("checkDnsConnectivity: Checking 0x" + - Integer.toHexString(Integer.reverseBytes(dns)) + " for connectivity"); + myLogV("checkDnsConnectivity: Checking " + dns.getHostAddress() + " for connectivity"); } int numInitialIgnoredPings = getInitialIgnoredPingCount(); int numPings = getPingCount(); int pingDelay = getPingDelayMs(); int acceptableLoss = getAcceptablePacketLossPercentage(); - + /** See {@link Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT} */ int ignoredPingCounter = 0; int pingCounter = 0; int successCounter = 0; - + // No connectivity check needed if (numPings == 0) { return true; @@ -371,20 +380,20 @@ public class WifiWatchdogService { pingCounter++; successCounter++; } - + if (V) { Slog.v(TAG, (dnsAlive ? " +" : " Ignored: -")); } if (shouldCancel()) return false; - + try { Thread.sleep(pingDelay); } catch (InterruptedException e) { Slog.w(TAG, "Interrupted while pausing between pings", e); } } - + // Do the pings that we use to measure packet loss for (; pingCounter < numPings; pingCounter++) { if (shouldCancel()) return false; @@ -401,40 +410,41 @@ public class WifiWatchdogService { } if (shouldCancel()) return false; - + try { Thread.sleep(pingDelay); } catch (InterruptedException e) { Slog.w(TAG, "Interrupted while pausing between pings", e); } } - + int packetLossPercentage = 100 * (numPings - successCounter) / numPings; if (D) { Slog.d(TAG, packetLossPercentage + "% packet loss (acceptable is " + acceptableLoss + "%)"); } - + return !shouldCancel() && (packetLossPercentage <= acceptableLoss); } private boolean backgroundCheckDnsConnectivity() { - int dns = getDns(); - if (false && V) { - myLogV("backgroundCheckDnsConnectivity: Background checking " + dns + - " for connectivity"); - } - - if (dns == -1) { + InetAddress dns = getDns(); + + if (dns == null) { if (V) { myLogV("backgroundCheckDnsConnectivity: DNS is empty, returning false"); } return false; } - + + if (false && V) { + myLogV("backgroundCheckDnsConnectivity: Background checking " + + dns.getHostAddress() + " for connectivity"); + } + return DnsPinger.isDnsReachable(dns, getBackgroundCheckTimeoutMs()); } - + /** * Signals the current action to cancel. */ @@ -1207,43 +1217,37 @@ public class WifiWatchdogService { /** Used to generate IDs */ private static Random sRandom = new Random(); - - static boolean isDnsReachable(int dns, int timeout) { + + static boolean isDnsReachable(InetAddress dnsAddress, int timeout) { DatagramSocket socket = null; try { socket = new DatagramSocket(); - + // Set some socket properties socket.setSoTimeout(timeout); - + byte[] buf = new byte[DNS_QUERY_BASE_SIZE]; fillQuery(buf); - + // Send the DNS query - byte parts[] = new byte[4]; - parts[0] = (byte)(dns & 0xff); - parts[1] = (byte)((dns >> 8) & 0xff); - parts[2] = (byte)((dns >> 16) & 0xff); - parts[3] = (byte)((dns >> 24) & 0xff); - InetAddress dnsAddress = InetAddress.getByAddress(parts); DatagramPacket packet = new DatagramPacket(buf, buf.length, dnsAddress, DNS_PORT); socket.send(packet); - + // Wait for reply (blocks for the above timeout) DatagramPacket replyPacket = new DatagramPacket(buf, buf.length); socket.receive(replyPacket); // If a timeout occurred, an exception would have been thrown. We got a reply! return true; - + } catch (SocketException e) { if (V) { Slog.v(TAG, "DnsPinger.isReachable received SocketException", e); } return false; - + } catch (UnknownHostException e) { if (V) { Slog.v(TAG, "DnsPinger.isReachable is unable to resolve the DNS host", e); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index c24fe8ee0da6..399c19ae7f57 100755..100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -24,8 +24,8 @@ import com.android.server.ProcessMap; import com.android.server.ProcessStats; import com.android.server.SystemServer; import com.android.server.Watchdog; -import com.android.server.WindowManagerService; import com.android.server.am.ActivityStack.ActivityState; +import com.android.server.wm.WindowManagerService; import dalvik.system.Zygote; @@ -4969,11 +4969,6 @@ public final class ActivityManagerService extends ActivityManagerNative enforceCallingPermission(android.Manifest.permission.GET_TASKS, "getRecentTasks()"); - final boolean canReadFb = (flags&ActivityManager.TASKS_GET_THUMBNAILS) != 0 - && checkCallingPermission( - android.Manifest.permission.READ_FRAME_BUFFER) - == PackageManager.PERMISSION_GRANTED; - IPackageManager pm = AppGlobals.getPackageManager(); ActivityRecord resumed = mMainStack.mResumedActivity; @@ -4991,17 +4986,10 @@ public final class ActivityManagerService extends ActivityManagerNative ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); rti.id = tr.numActivities > 0 ? tr.taskId : -1; + rti.persistentId = tr.taskId; rti.baseIntent = new Intent( tr.intent != null ? tr.intent : tr.affinityIntent); rti.origActivity = tr.origActivity; - - if (canReadFb) { - if (resumed != null && resumed.task == tr) { - rti.thumbnail = resumed.stack.screenshotActivities(resumed); - } else { - rti.thumbnail = tr.lastThumbnail; - } - } rti.description = tr.lastDescription; if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0) { @@ -5030,6 +5018,26 @@ public final class ActivityManagerService extends ActivityManagerNative } } + public Bitmap getTaskThumbnail(int id) { + synchronized (this) { + enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, + "getTaskThumbnail()"); + ActivityRecord resumed = mMainStack.mResumedActivity; + final int N = mRecentTasks.size(); + for (int i=0; i<N; i++) { + TaskRecord tr = mRecentTasks.get(i); + if (tr.taskId == id) { + if (resumed != null && resumed.task == tr) { + return resumed.stack.screenshotActivities(resumed); + } else { + return tr.lastThumbnail; + } + } + } + } + return null; + } + private final int findAffinityTaskTopLocked(int startIndex, String affinity) { int j; TaskRecord startTask = ((ActivityRecord)mMainStack.mHistory.get(startIndex)).task; @@ -5085,6 +5093,9 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<N; i++) { TaskRecord tr = mRecentTasks.get(i); if (tr.taskId == task) { + if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { + mMainStack.mUserLeaving = true; + } if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) { // Caller wants the home activity moved with it. To accomplish this, // we'll just move the home task to the top first. @@ -5097,6 +5108,9 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i); if (hr.task.taskId == task) { + if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { + mMainStack.mUserLeaving = true; + } if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) { // Caller wants the home activity moved with it. To accomplish this, // we'll just move the home task to the top first. @@ -9637,7 +9651,7 @@ public final class ActivityManagerService extends ActivityManagerNative // r.record is null if findServiceLocked() failed the caller permission check if (r.record == null) { throw new SecurityException( - "Permission Denial: Accessing service " + "Permission Denial: Accessing service " + r.record.name + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + r.permission); diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index 26c7e711c393..f24f96c54509 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -31,6 +31,7 @@ import android.net.ConnectivityManager; import android.net.InterfaceConfiguration; import android.net.IConnectivityManager; import android.net.INetworkManagementEventObserver; +import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkInfo; import android.os.Binder; @@ -89,7 +90,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { private BroadcastReceiver mStateReceiver; private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129"; - private static final String USB_NETMASK = "255.255.255.0"; + private static final int USB_PREFIX_LENGTH = 24; // USB is 192.168.42.1 and 255.255.255.0 // Wifi is 192.168.43.1 and 255.255.255.0 @@ -566,8 +567,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { try { ifcg = service.getInterfaceConfig(iface); if (ifcg != null) { - ifcg.addr = InetAddress.getByName(USB_NEAR_IFACE_ADDR); - ifcg.mask = InetAddress.getByName(USB_NETMASK); + InetAddress addr = InetAddress.getByName(USB_NEAR_IFACE_ADDR); + ifcg.addr = new LinkAddress(addr, USB_PREFIX_LENGTH); if (enabled) { ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); } else { @@ -1188,8 +1189,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub { try { service.startTethering(mDhcpRange); } catch (Exception e) { - transitionTo(mStartTetheringErrorState); - return false; + try { + service.stopTethering(); + service.startTethering(mDhcpRange); + } catch (Exception ee) { + transitionTo(mStartTetheringErrorState); + return false; + } } try { service.setDnsForwarders(mDnsServers); diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java new file mode 100644 index 000000000000..d3d9df4359d5 --- /dev/null +++ b/services/java/com/android/server/wm/AppWindowToken.java @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; + +import com.android.server.wm.WindowManagerService.H; + +import android.content.pm.ActivityInfo; +import android.os.Message; +import android.os.RemoteException; +import android.util.Slog; +import android.view.IApplicationToken; +import android.view.View; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.Transformation; + +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Version of WindowToken that is specifically for a particular application (or + * really activity) that is displaying windows. + */ +class AppWindowToken extends WindowToken { + // Non-null only for application tokens. + final IApplicationToken appToken; + + // All of the windows and child windows that are included in this + // application token. Note this list is NOT sorted! + final ArrayList<WindowState> allAppWindows = new ArrayList<WindowState>(); + + int groupId = -1; + boolean appFullscreen; + int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + + // The input dispatching timeout for this application token in nanoseconds. + long inputDispatchingTimeoutNanos; + + // These are used for determining when all windows associated with + // an activity have been drawn, so they can be made visible together + // at the same time. + int lastTransactionSequence; + int numInterestingWindows; + int numDrawnWindows; + boolean inPendingTransaction; + boolean allDrawn; + + // Is this token going to be hidden in a little while? If so, it + // won't be taken into account for setting the screen orientation. + boolean willBeHidden; + + // Is this window's surface needed? This is almost like hidden, except + // it will sometimes be true a little earlier: when the token has + // been shown, but is still waiting for its app transition to execute + // before making its windows shown. + boolean hiddenRequested; + + // Have we told the window clients to hide themselves? + boolean clientHidden; + + // Last visibility state we reported to the app token. + boolean reportedVisible; + + // Set to true when the token has been removed from the window mgr. + boolean removed; + + // Have we been asked to have this token keep the screen frozen? + boolean freezingScreen; + + boolean animating; + Animation animation; + boolean hasTransformation; + final Transformation transformation = new Transformation(); + + // Offset to the window of all layers in the token, for use by + // AppWindowToken animations. + int animLayerAdjustment; + + // Information about an application starting window if displayed. + StartingData startingData; + WindowState startingWindow; + View startingView; + boolean startingDisplayed; + boolean startingMoved; + boolean firstWindowDrawn; + + // Input application handle used by the input dispatcher. + InputApplicationHandle mInputApplicationHandle; + + AppWindowToken(WindowManagerService _service, IApplicationToken _token) { + super(_service, _token.asBinder(), + WindowManager.LayoutParams.TYPE_APPLICATION, true); + appWindowToken = this; + appToken = _token; + mInputApplicationHandle = new InputApplicationHandle(this); + lastTransactionSequence = service.mTransactionSequence-1; + } + + public void setAnimation(Animation anim) { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Setting animation in " + this + ": " + anim); + animation = anim; + animating = false; + anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION); + anim.scaleCurrentDuration(service.mTransitionAnimationScale); + int zorder = anim.getZAdjustment(); + int adj = 0; + if (zorder == Animation.ZORDER_TOP) { + adj = WindowManagerService.TYPE_LAYER_OFFSET; + } else if (zorder == Animation.ZORDER_BOTTOM) { + adj = -WindowManagerService.TYPE_LAYER_OFFSET; + } + + if (animLayerAdjustment != adj) { + animLayerAdjustment = adj; + updateLayers(); + } + } + + public void setDummyAnimation() { + if (animation == null) { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Setting dummy animation in " + this); + animation = WindowManagerService.sDummyAnimation; + } + } + + public void clearAnimation() { + if (animation != null) { + animation = null; + animating = true; + } + } + + void updateLayers() { + final int N = allAppWindows.size(); + final int adj = animLayerAdjustment; + for (int i=0; i<N; i++) { + WindowState w = allAppWindows.get(i); + w.mAnimLayer = w.mLayer + adj; + if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": " + + w.mAnimLayer); + if (w == service.mInputMethodTarget && !service.mInputMethodTargetWaitingAnim) { + service.setInputMethodAnimLayerAdjustment(adj); + } + if (w == service.mWallpaperTarget && service.mLowerWallpaperTarget == null) { + service.setWallpaperAnimLayerAdjustmentLocked(adj); + } + } + } + + void sendAppVisibilityToClients() { + final int N = allAppWindows.size(); + for (int i=0; i<N; i++) { + WindowState win = allAppWindows.get(i); + if (win == startingWindow && clientHidden) { + // Don't hide the starting window. + continue; + } + try { + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, + "Setting visibility of " + win + ": " + (!clientHidden)); + win.mClient.dispatchAppVisibility(!clientHidden); + } catch (RemoteException e) { + } + } + } + + void showAllWindowsLocked() { + final int NW = allAppWindows.size(); + for (int i=0; i<NW; i++) { + WindowState w = allAppWindows.get(i); + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, + "performing show on: " + w); + w.performShowLocked(); + } + } + + // This must be called while inside a transaction. + boolean stepAnimationLocked(long currentTime, int dw, int dh) { + if (!service.mDisplayFrozen && service.mPolicy.isScreenOn()) { + // We will run animations as long as the display isn't frozen. + + if (animation == WindowManagerService.sDummyAnimation) { + // This guy is going to animate, but not yet. For now count + // it as not animating for purposes of scheduling transactions; + // when it is really time to animate, this will be set to + // a real animation and the next call will execute normally. + return false; + } + + if ((allDrawn || animating || startingDisplayed) && animation != null) { + if (!animating) { + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Starting animation in " + this + + " @ " + currentTime + ": dw=" + dw + " dh=" + dh + + " scale=" + service.mTransitionAnimationScale + + " allDrawn=" + allDrawn + " animating=" + animating); + animation.initialize(dw, dh, dw, dh); + animation.setStartTime(currentTime); + animating = true; + } + transformation.clear(); + final boolean more = animation.getTransformation( + currentTime, transformation); + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Stepped animation in " + this + + ": more=" + more + ", xform=" + transformation); + if (more) { + // we're done! + hasTransformation = true; + return true; + } + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Finished animation in " + this + + " @ " + currentTime); + animation = null; + } + } else if (animation != null) { + // If the display is frozen, and there is a pending animation, + // clear it and make sure we run the cleanup code. + animating = true; + animation = null; + } + + hasTransformation = false; + + if (!animating) { + return false; + } + + clearAnimation(); + animating = false; + if (animLayerAdjustment != 0) { + animLayerAdjustment = 0; + updateLayers(); + } + if (service.mInputMethodTarget != null && service.mInputMethodTarget.mAppToken == this) { + service.moveInputMethodWindowsIfNeededLocked(true); + } + + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Animation done in " + this + + ": reportedVisible=" + reportedVisible); + + transformation.clear(); + + final int N = windows.size(); + for (int i=0; i<N; i++) { + windows.get(i).finishExit(); + } + updateReportedVisibilityLocked(); + + return false; + } + + void updateReportedVisibilityLocked() { + if (appToken == null) { + return; + } + + int numInteresting = 0; + int numVisible = 0; + boolean nowGone = true; + + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Update reported visibility: " + this); + final int N = allAppWindows.size(); + for (int i=0; i<N; i++) { + WindowState win = allAppWindows.get(i); + if (win == startingWindow || win.mAppFreezing + || win.mViewVisibility != View.VISIBLE + || win.mAttrs.type == TYPE_APPLICATION_STARTING + || win.mDestroying) { + continue; + } + if (WindowManagerService.DEBUG_VISIBILITY) { + Slog.v(WindowManagerService.TAG, "Win " + win + ": isDrawn=" + + win.isDrawnLw() + + ", isAnimating=" + win.isAnimating()); + if (!win.isDrawnLw()) { + Slog.v(WindowManagerService.TAG, "Not displayed: s=" + win.mSurface + + " pv=" + win.mPolicyVisibility + + " dp=" + win.mDrawPending + + " cdp=" + win.mCommitDrawPending + + " ah=" + win.mAttachedHidden + + " th=" + + (win.mAppToken != null + ? win.mAppToken.hiddenRequested : false) + + " a=" + win.mAnimating); + } + } + numInteresting++; + if (win.isDrawnLw()) { + if (!win.isAnimating()) { + numVisible++; + } + nowGone = false; + } else if (win.isAnimating()) { + nowGone = false; + } + } + + boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting; + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "VIS " + this + ": interesting=" + + numInteresting + " visible=" + numVisible); + if (nowVisible != reportedVisible) { + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v( + WindowManagerService.TAG, "Visibility changed in " + this + + ": vis=" + nowVisible); + reportedVisible = nowVisible; + Message m = service.mH.obtainMessage( + H.REPORT_APPLICATION_TOKEN_WINDOWS, + nowVisible ? 1 : 0, + nowGone ? 1 : 0, + this); + service.mH.sendMessage(m); + } + } + + WindowState findMainWindow() { + int j = windows.size(); + while (j > 0) { + j--; + WindowState win = windows.get(j); + if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION + || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) { + return win; + } + } + return null; + } + + void dump(PrintWriter pw, String prefix) { + super.dump(pw, prefix); + if (appToken != null) { + pw.print(prefix); pw.println("app=true"); + } + if (allAppWindows.size() > 0) { + pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows); + } + pw.print(prefix); pw.print("groupId="); pw.print(groupId); + pw.print(" appFullscreen="); pw.print(appFullscreen); + pw.print(" requestedOrientation="); pw.println(requestedOrientation); + pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested); + pw.print(" clientHidden="); pw.print(clientHidden); + pw.print(" willBeHidden="); pw.print(willBeHidden); + pw.print(" reportedVisible="); pw.println(reportedVisible); + if (paused || freezingScreen) { + pw.print(prefix); pw.print("paused="); pw.print(paused); + pw.print(" freezingScreen="); pw.println(freezingScreen); + } + if (numInterestingWindows != 0 || numDrawnWindows != 0 + || inPendingTransaction || allDrawn) { + pw.print(prefix); pw.print("numInterestingWindows="); + pw.print(numInterestingWindows); + pw.print(" numDrawnWindows="); pw.print(numDrawnWindows); + pw.print(" inPendingTransaction="); pw.print(inPendingTransaction); + pw.print(" allDrawn="); pw.println(allDrawn); + } + if (animating || animation != null) { + pw.print(prefix); pw.print("animating="); pw.print(animating); + pw.print(" animation="); pw.println(animation); + } + if (hasTransformation) { + pw.print(prefix); pw.print("XForm: "); + transformation.printShortString(pw); + pw.println(); + } + if (animLayerAdjustment != 0) { + pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment); + } + if (startingData != null || removed || firstWindowDrawn) { + pw.print(prefix); pw.print("startingData="); pw.print(startingData); + pw.print(" removed="); pw.print(removed); + pw.print(" firstWindowDrawn="); pw.println(firstWindowDrawn); + } + if (startingWindow != null || startingView != null + || startingDisplayed || startingMoved) { + pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow); + pw.print(" startingView="); pw.print(startingView); + pw.print(" startingDisplayed="); pw.print(startingDisplayed); + pw.print(" startingMoved"); pw.println(startingMoved); + } + } + + @Override + public String toString() { + if (stringName == null) { + StringBuilder sb = new StringBuilder(); + sb.append("AppWindowToken{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" token="); sb.append(token); sb.append('}'); + stringName = sb.toString(); + } + return stringName; + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java new file mode 100644 index 000000000000..1fcb8697174a --- /dev/null +++ b/services/java/com/android/server/wm/DimAnimator.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.util.Slog; +import android.util.TypedValue; +import android.view.Surface; +import android.view.SurfaceSession; + +import java.io.PrintWriter; + +/** + * DimAnimator class that controls the dim animation. This holds the surface and + * all state used for dim animation. + */ +class DimAnimator { + Surface mDimSurface; + boolean mDimShown = false; + float mDimCurrentAlpha; + float mDimTargetAlpha; + float mDimDeltaPerMs; + long mLastDimAnimTime; + + int mLastDimWidth, mLastDimHeight; + + DimAnimator (SurfaceSession session) { + if (mDimSurface == null) { + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + + mDimSurface + ": CREATE"); + try { + mDimSurface = new Surface(session, 0, + "DimSurface", + -1, 16, 16, PixelFormat.OPAQUE, + Surface.FX_SURFACE_DIM); + mDimSurface.setAlpha(0.0f); + } catch (Exception e) { + Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e); + } + } + } + + /** + * Show the dim surface. + */ + void show(int dw, int dh) { + if (!mDimShown) { + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" + + dw + "x" + dh + ")"); + mDimShown = true; + try { + mLastDimWidth = dw; + mLastDimHeight = dh; + mDimSurface.setPosition(0, 0); + mDimSurface.setSize(dw, dh); + mDimSurface.show(); + } catch (RuntimeException e) { + Slog.w(WindowManagerService.TAG, "Failure showing dim surface", e); + } + } else if (mLastDimWidth != dw || mLastDimHeight != dh) { + mLastDimWidth = dw; + mLastDimHeight = dh; + mDimSurface.setSize(dw, dh); + } + } + + /** + * Set's the dim surface's layer and update dim parameters that will be used in + * {@link updateSurface} after all windows are examined. + */ + void updateParameters(Resources res, WindowState w, long currentTime) { + mDimSurface.setLayer(w.mAnimLayer-1); + + final float target = w.mExiting ? 0 : w.mAttrs.dimAmount; + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface + + ": layer=" + (w.mAnimLayer-1) + " target=" + target); + if (mDimTargetAlpha != target) { + // If the desired dim level has changed, then + // start an animation to it. + mLastDimAnimTime = currentTime; + long duration = (w.mAnimating && w.mAnimation != null) + ? w.mAnimation.computeDurationHint() + : WindowManagerService.DEFAULT_DIM_DURATION; + if (target > mDimTargetAlpha) { + TypedValue tv = new TypedValue(); + res.getValue(com.android.internal.R.fraction.config_dimBehindFadeDuration, + tv, true); + if (tv.type == TypedValue.TYPE_FRACTION) { + duration = (long)tv.getFraction((float)duration, (float)duration); + } else if (tv.type >= TypedValue.TYPE_FIRST_INT + && tv.type <= TypedValue.TYPE_LAST_INT) { + duration = tv.data; + } + } + if (duration < 1) { + // Don't divide by zero + duration = 1; + } + mDimTargetAlpha = target; + mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha) / duration; + } + } + + /** + * Updating the surface's alpha. Returns true if the animation continues, or returns + * false when the animation is finished and the dim surface is hidden. + */ + boolean updateSurface(boolean dimming, long currentTime, boolean displayFrozen) { + if (!dimming) { + if (mDimTargetAlpha != 0) { + mLastDimAnimTime = currentTime; + mDimTargetAlpha = 0; + mDimDeltaPerMs = (-mDimCurrentAlpha) / WindowManagerService.DEFAULT_DIM_DURATION; + } + } + + boolean animating = false; + if (mLastDimAnimTime != 0) { + mDimCurrentAlpha += mDimDeltaPerMs + * (currentTime-mLastDimAnimTime); + boolean more = true; + if (displayFrozen) { + // If the display is frozen, there is no reason to animate. + more = false; + } else if (mDimDeltaPerMs > 0) { + if (mDimCurrentAlpha > mDimTargetAlpha) { + more = false; + } + } else if (mDimDeltaPerMs < 0) { + if (mDimCurrentAlpha < mDimTargetAlpha) { + more = false; + } + } else { + more = false; + } + + // Do we need to continue animating? + if (more) { + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + + mDimSurface + ": alpha=" + mDimCurrentAlpha); + mLastDimAnimTime = currentTime; + mDimSurface.setAlpha(mDimCurrentAlpha); + animating = true; + } else { + mDimCurrentAlpha = mDimTargetAlpha; + mLastDimAnimTime = 0; + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + + mDimSurface + ": final alpha=" + mDimCurrentAlpha); + mDimSurface.setAlpha(mDimCurrentAlpha); + if (!dimming) { + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface + + ": HIDE"); + try { + mDimSurface.hide(); + } catch (RuntimeException e) { + Slog.w(WindowManagerService.TAG, "Illegal argument exception hiding dim surface"); + } + mDimShown = false; + } + } + } + return animating; + } + + public void printTo(PrintWriter pw) { + pw.print(" mDimShown="); pw.print(mDimShown); + pw.print(" current="); pw.print(mDimCurrentAlpha); + pw.print(" target="); pw.print(mDimTargetAlpha); + pw.print(" delta="); pw.print(mDimDeltaPerMs); + pw.print(" lastAnimTime="); pw.println(mLastDimAnimTime); + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java new file mode 100644 index 000000000000..c8f8ff3ca03e --- /dev/null +++ b/services/java/com/android/server/wm/DragState.java @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import com.android.server.wm.WindowManagerService.H; + +import android.content.ClipData; +import android.content.ClipDescription; +import android.graphics.Region; +import android.os.IBinder; +import android.os.Message; +import android.os.Process; +import android.os.RemoteException; +import android.util.Slog; +import android.view.DragEvent; +import android.view.InputChannel; +import android.view.InputQueue; +import android.view.Surface; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowManagerPolicy; + +import java.util.ArrayList; + +/** + * Drag/drop state + */ +class DragState { + final WindowManagerService mService; + IBinder mToken; + Surface mSurface; + int mFlags; + IBinder mLocalWin; + ClipData mData; + ClipDescription mDataDescription; + boolean mDragResult; + float mCurrentX, mCurrentY; + float mThumbOffsetX, mThumbOffsetY; + InputChannel mServerChannel, mClientChannel; + WindowState mTargetWindow; + ArrayList<WindowState> mNotifiedWindows; + boolean mDragInProgress; + + private final Region mTmpRegion = new Region(); + + DragState(WindowManagerService service, IBinder token, Surface surface, + int flags, IBinder localWin) { + mService = service; + mToken = token; + mSurface = surface; + mFlags = flags; + mLocalWin = localWin; + mNotifiedWindows = new ArrayList<WindowState>(); + } + + void reset() { + if (mSurface != null) { + mSurface.destroy(); + } + mSurface = null; + mFlags = 0; + mLocalWin = null; + mToken = null; + mData = null; + mThumbOffsetX = mThumbOffsetY = 0; + mNotifiedWindows = null; + } + + void register() { + if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "registering drag input channel"); + if (mClientChannel != null) { + Slog.e(WindowManagerService.TAG, "Duplicate register of drag input channel"); + } else { + InputChannel[] channels = InputChannel.openInputChannelPair("drag"); + mServerChannel = channels[0]; + mClientChannel = channels[1]; + mService.mInputManager.registerInputChannel(mServerChannel, null); + InputQueue.registerInputChannel(mClientChannel, mService.mDragInputHandler, + mService.mH.getLooper().getQueue()); + } + } + + void unregister() { + if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "unregistering drag input channel"); + if (mClientChannel == null) { + Slog.e(WindowManagerService.TAG, "Unregister of nonexistent drag input channel"); + } else { + mService.mInputManager.unregisterInputChannel(mServerChannel); + InputQueue.unregisterInputChannel(mClientChannel); + mClientChannel.dispose(); + mServerChannel.dispose(); + mClientChannel = null; + mServerChannel = null; + } + } + + int getDragLayerLw() { + return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG) + * WindowManagerService.TYPE_LAYER_MULTIPLIER + + WindowManagerService.TYPE_LAYER_OFFSET; + } + + /* call out to each visible window/session informing it about the drag + */ + void broadcastDragStartedLw(final float touchX, final float touchY) { + // Cache a base-class instance of the clip metadata so that parceling + // works correctly in calling out to the apps. + mDataDescription = (mData != null) ? mData.getDescription() : null; + mNotifiedWindows.clear(); + mDragInProgress = true; + + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")"); + } + + final int N = mService.mWindows.size(); + for (int i = 0; i < N; i++) { + sendDragStartedLw(mService.mWindows.get(i), touchX, touchY, mDataDescription); + } + } + + /* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the + * designated window is potentially a drop recipient. There are race situations + * around DRAG_ENDED broadcast, so we make sure that once we've declared that + * the drag has ended, we never send out another DRAG_STARTED for this drag action. + * + * This method clones the 'event' parameter if it's being delivered to the same + * process, so it's safe for the caller to call recycle() on the event afterwards. + */ + private void sendDragStartedLw(WindowState newWin, float touchX, float touchY, + ClipDescription desc) { + // Don't actually send the event if the drag is supposed to be pinned + // to the originating window but 'newWin' is not that window. + if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) { + final IBinder winBinder = newWin.mClient.asBinder(); + if (winBinder != mLocalWin) { + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "Not dispatching local DRAG_STARTED to " + newWin); + } + return; + } + } + + if (mDragInProgress && newWin.isPotentialDragTarget()) { + DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, + touchX - newWin.mFrame.left, touchY - newWin.mFrame.top, + null, desc, null, false); + try { + newWin.mClient.dispatchDragEvent(event); + // track each window that we've notified that the drag is starting + mNotifiedWindows.add(newWin); + } catch (RemoteException e) { + Slog.w(WindowManagerService.TAG, "Unable to drag-start window " + newWin); + } finally { + // if the callee was local, the dispatch has already recycled the event + if (Process.myPid() != newWin.mSession.mPid) { + event.recycle(); + } + } + } + } + + /* helper - construct and send a DRAG_STARTED event only if the window has not + * previously been notified, i.e. it became visible after the drag operation + * was begun. This is a rare case. + */ + void sendDragStartedIfNeededLw(WindowState newWin) { + if (mDragInProgress) { + // If we have sent the drag-started, we needn't do so again + for (WindowState ws : mNotifiedWindows) { + if (ws == newWin) { + return; + } + } + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "need to send DRAG_STARTED to new window " + newWin); + } + sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription); + } + } + + void broadcastDragEndedLw() { + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "broadcasting DRAG_ENDED"); + } + DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, + 0, 0, null, null, null, mDragResult); + for (WindowState ws: mNotifiedWindows) { + try { + ws.mClient.dispatchDragEvent(evt); + } catch (RemoteException e) { + Slog.w(WindowManagerService.TAG, "Unable to drag-end window " + ws); + } + } + mNotifiedWindows.clear(); + mDragInProgress = false; + evt.recycle(); + } + + void endDragLw() { + mService.mDragState.broadcastDragEndedLw(); + + // stop intercepting input + mService.mDragState.unregister(); + mService.mInputMonitor.updateInputWindowsLw(true /*force*/); + + // free our resources and drop all the object references + mService.mDragState.reset(); + mService.mDragState = null; + + if (WindowManagerService.DEBUG_ORIENTATION) Slog.d(WindowManagerService.TAG, "Performing post-drag rotation"); + boolean changed = mService.setRotationUncheckedLocked( + WindowManagerPolicy.USE_LAST_ROTATION, 0, false); + if (changed) { + mService.mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); + } + } + + void notifyMoveLw(float x, float y) { + final int myPid = Process.myPid(); + + // Move the surface to the given touch + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION notifyMoveLw"); + Surface.openTransaction(); + try { + mSurface.setPosition((int)(x - mThumbOffsetX), (int)(y - mThumbOffsetY)); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DRAG " + + mSurface + ": pos=(" + + (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")"); + } finally { + Surface.closeTransaction(); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION notifyMoveLw"); + } + + // Tell the affected window + WindowState touchedWin = getTouchedWinAtPointLw(x, y); + if (touchedWin == null) { + if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "No touched win at x=" + x + " y=" + y); + return; + } + if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) { + final IBinder touchedBinder = touchedWin.mClient.asBinder(); + if (touchedBinder != mLocalWin) { + // This drag is pinned only to the originating window, but the drag + // point is outside that window. Pretend it's over empty space. + touchedWin = null; + } + } + try { + // have we dragged over a new window? + if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) { + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "sending DRAG_EXITED to " + mTargetWindow); + } + // force DRAG_EXITED_EVENT if appropriate + DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED, + x - mTargetWindow.mFrame.left, y - mTargetWindow.mFrame.top, + null, null, null, false); + mTargetWindow.mClient.dispatchDragEvent(evt); + if (myPid != mTargetWindow.mSession.mPid) { + evt.recycle(); + } + } + if (touchedWin != null) { + if (false && WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "sending DRAG_LOCATION to " + touchedWin); + } + DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION, + x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, + null, null, null, false); + touchedWin.mClient.dispatchDragEvent(evt); + if (myPid != touchedWin.mSession.mPid) { + evt.recycle(); + } + } + } catch (RemoteException e) { + Slog.w(WindowManagerService.TAG, "can't send drag notification to windows"); + } + mTargetWindow = touchedWin; + } + + // Tell the drop target about the data. Returns 'true' if we can immediately + // dispatch the global drag-ended message, 'false' if we need to wait for a + // result from the recipient. + boolean notifyDropLw(float x, float y) { + WindowState touchedWin = getTouchedWinAtPointLw(x, y); + if (touchedWin == null) { + // "drop" outside a valid window -- no recipient to apply a + // timeout to, and we can send the drag-ended message immediately. + mDragResult = false; + return true; + } + + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "sending DROP to " + touchedWin); + } + final int myPid = Process.myPid(); + final IBinder token = touchedWin.mClient.asBinder(); + DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP, + x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, + null, null, mData, false); + try { + touchedWin.mClient.dispatchDragEvent(evt); + + // 5 second timeout for this window to respond to the drop + mService.mH.removeMessages(H.DRAG_END_TIMEOUT, token); + Message msg = mService.mH.obtainMessage(H.DRAG_END_TIMEOUT, token); + mService.mH.sendMessageDelayed(msg, 5000); + } catch (RemoteException e) { + Slog.w(WindowManagerService.TAG, "can't send drop notification to win " + touchedWin); + return true; + } finally { + if (myPid != touchedWin.mSession.mPid) { + evt.recycle(); + } + } + mToken = token; + return false; + } + + // Find the visible, touch-deliverable window under the given point + private WindowState getTouchedWinAtPointLw(float xf, float yf) { + WindowState touchedWin = null; + final int x = (int) xf; + final int y = (int) yf; + final ArrayList<WindowState> windows = mService.mWindows; + final int N = windows.size(); + for (int i = N - 1; i >= 0; i--) { + WindowState child = windows.get(i); + final int flags = child.mAttrs.flags; + if (!child.isVisibleLw()) { + // not visible == don't tell about drags + continue; + } + if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { + // not touchable == don't tell about drags + continue; + } + + child.getTouchableRegion(mTmpRegion); + + final int touchFlags = flags & + (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); + if (mTmpRegion.contains(x, y) || touchFlags == 0) { + // Found it + touchedWin = child; + break; + } + } + + return touchedWin; + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/wm/FadeInOutAnimation.java b/services/java/com/android/server/wm/FadeInOutAnimation.java new file mode 100644 index 000000000000..06f76571b016 --- /dev/null +++ b/services/java/com/android/server/wm/FadeInOutAnimation.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.Transformation; + +/** + * Animation that fade in after 0.5 interpolate time, or fade out in reverse order. + * This is used for opening/closing transition for apps in compatible mode. + */ +class FadeInOutAnimation extends Animation { + boolean mFadeIn; + + public FadeInOutAnimation(boolean fadeIn) { + setInterpolator(new AccelerateInterpolator()); + setDuration(WindowManagerService.DEFAULT_FADE_IN_OUT_DURATION); + mFadeIn = fadeIn; + } + + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + float x = interpolatedTime; + if (!mFadeIn) { + x = 1.0f - x; // reverse the interpolation for fade out + } + t.setAlpha(x); + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/InputApplication.java b/services/java/com/android/server/wm/InputApplication.java index ae0948441757..e04fd31a3ffc 100644 --- a/services/java/com/android/server/InputApplication.java +++ b/services/java/com/android/server/wm/InputApplication.java @@ -14,7 +14,8 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; + /** * Describes input-related application properties for use by the input dispatcher. diff --git a/services/java/com/android/server/InputApplicationHandle.java b/services/java/com/android/server/wm/InputApplicationHandle.java index d396d11caa41..64c8e7ed73db 100644 --- a/services/java/com/android/server/InputApplicationHandle.java +++ b/services/java/com/android/server/wm/InputApplicationHandle.java @@ -14,7 +14,8 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; + /** * Functions as a handle for an application that can receive input. @@ -29,11 +30,11 @@ public final class InputApplicationHandle { private int ptr; // The window manager's application window token. - public final WindowManagerService.AppWindowToken appWindowToken; + public final AppWindowToken appWindowToken; private native void nativeDispose(); - public InputApplicationHandle(WindowManagerService.AppWindowToken appWindowToken) { + public InputApplicationHandle(AppWindowToken appWindowToken) { this.appWindowToken = appWindowToken; } diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/wm/InputManager.java index 8d249ff79b81..80a2a96b47dd 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/wm/InputManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; import com.android.internal.util.XmlUtils; diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java new file mode 100644 index 000000000000..34f36184ba63 --- /dev/null +++ b/services/java/com/android/server/wm/InputMonitor.java @@ -0,0 +1,364 @@ +/** + * + */ +package com.android.server.wm; + +import android.graphics.Rect; +import android.os.Process; +import android.os.RemoteException; +import android.util.Log; +import android.util.Slog; +import android.view.KeyEvent; +import android.view.WindowManager; + +import java.util.ArrayList; + +final class InputMonitor { + private final WindowManagerService mService; + + // Current window with input focus for keys and other non-touch events. May be null. + private WindowState mInputFocus; + + // When true, prevents input dispatch from proceeding until set to false again. + private boolean mInputDispatchFrozen; + + // When true, input dispatch proceeds normally. Otherwise all events are dropped. + private boolean mInputDispatchEnabled = true; + + // When true, need to call updateInputWindowsLw(). + private boolean mUpdateInputWindowsNeeded = true; + + // Temporary list of windows information to provide to the input dispatcher. + private InputWindowList mTempInputWindows = new InputWindowList(); + + // Temporary input application object to provide to the input dispatcher. + private InputApplication mTempInputApplication = new InputApplication(); + + // Set to true when the first input device configuration change notification + // is received to indicate that the input devices are ready. + private final Object mInputDevicesReadyMonitor = new Object(); + private boolean mInputDevicesReady; + + public InputMonitor(WindowManagerService service) { + mService = service; + } + + /* Notifies the window manager about a broken input channel. + * + * Called by the InputManager. + */ + public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) { + if (inputWindowHandle == null) { + return; + } + + synchronized (mService.mWindowMap) { + WindowState windowState = (WindowState) inputWindowHandle.windowState; + Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState); + mService.removeWindowLocked(windowState.mSession, windowState); + } + } + + /* Notifies the window manager about an application that is not responding. + * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch. + * + * Called by the InputManager. + */ + public long notifyANR(InputApplicationHandle inputApplicationHandle, + InputWindowHandle inputWindowHandle) { + AppWindowToken appWindowToken = null; + if (inputWindowHandle != null) { + synchronized (mService.mWindowMap) { + WindowState windowState = (WindowState) inputWindowHandle.windowState; + if (windowState != null) { + Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to " + + windowState.mAttrs.getTitle()); + appWindowToken = windowState.mAppToken; + } + } + } + + if (appWindowToken == null && inputApplicationHandle != null) { + appWindowToken = inputApplicationHandle.appWindowToken; + Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to application " + + appWindowToken.stringName); + } + + if (appWindowToken != null && appWindowToken.appToken != null) { + try { + // Notify the activity manager about the timeout and let it decide whether + // to abort dispatching or keep waiting. + boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(); + if (! abort) { + // The activity manager declined to abort dispatching. + // Wait a bit longer and timeout again later. + return appWindowToken.inputDispatchingTimeoutNanos; + } + } catch (RemoteException ex) { + } + } + return 0; // abort dispatching + } + + private void addDragInputWindowLw(InputWindowList windowList) { + final InputWindow inputWindow = windowList.add(); + inputWindow.inputChannel = mService.mDragState.mServerChannel; + inputWindow.name = "drag"; + inputWindow.layoutParamsFlags = 0; + inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; + inputWindow.dispatchingTimeoutNanos = WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + inputWindow.visible = true; + inputWindow.canReceiveKeys = false; + inputWindow.hasFocus = true; + inputWindow.hasWallpaper = false; + inputWindow.paused = false; + inputWindow.layer = mService.mDragState.getDragLayerLw(); + inputWindow.ownerPid = Process.myPid(); + inputWindow.ownerUid = Process.myUid(); + + // The drag window covers the entire display + inputWindow.frameLeft = 0; + inputWindow.frameTop = 0; + inputWindow.frameRight = mService.mDisplay.getWidth(); + inputWindow.frameBottom = mService.mDisplay.getHeight(); + + // The drag window cannot receive new touches. + inputWindow.touchableRegion.setEmpty(); + } + + public void setUpdateInputWindowsNeededLw() { + mUpdateInputWindowsNeeded = true; + } + + /* Updates the cached window information provided to the input dispatcher. */ + public void updateInputWindowsLw(boolean force) { + if (!force && !mUpdateInputWindowsNeeded) { + return; + } + mUpdateInputWindowsNeeded = false; + + // Populate the input window list with information about all of the windows that + // could potentially receive input. + // As an optimization, we could try to prune the list of windows but this turns + // out to be difficult because only the native code knows for sure which window + // currently has touch focus. + final ArrayList<WindowState> windows = mService.mWindows; + + // If there's a drag in flight, provide a pseudowindow to catch drag input + final boolean inDrag = (mService.mDragState != null); + if (inDrag) { + if (WindowManagerService.DEBUG_DRAG) { + Log.d(WindowManagerService.TAG, "Inserting drag window"); + } + addDragInputWindowLw(mTempInputWindows); + } + + final int N = windows.size(); + for (int i = N - 1; i >= 0; i--) { + final WindowState child = windows.get(i); + if (child.mInputChannel == null || child.mRemoved) { + // Skip this window because it cannot possibly receive input. + continue; + } + + final int flags = child.mAttrs.flags; + final int type = child.mAttrs.type; + + final boolean hasFocus = (child == mInputFocus); + final boolean isVisible = child.isVisibleLw(); + final boolean hasWallpaper = (child == mService.mWallpaperTarget) + && (type != WindowManager.LayoutParams.TYPE_KEYGUARD); + + // If there's a drag in progress and 'child' is a potential drop target, + // make sure it's been told about the drag + if (inDrag && isVisible) { + mService.mDragState.sendDragStartedIfNeededLw(child); + } + + // Add a window to our list of input windows. + final InputWindow inputWindow = mTempInputWindows.add(); + inputWindow.inputWindowHandle = child.mInputWindowHandle; + inputWindow.inputChannel = child.mInputChannel; + inputWindow.name = child.toString(); + inputWindow.layoutParamsFlags = flags; + inputWindow.layoutParamsType = type; + inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); + inputWindow.visible = isVisible; + inputWindow.canReceiveKeys = child.canReceiveKeys(); + inputWindow.hasFocus = hasFocus; + inputWindow.hasWallpaper = hasWallpaper; + inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false; + inputWindow.layer = child.mLayer; + inputWindow.ownerPid = child.mSession.mPid; + inputWindow.ownerUid = child.mSession.mUid; + + final Rect frame = child.mFrame; + inputWindow.frameLeft = frame.left; + inputWindow.frameTop = frame.top; + inputWindow.frameRight = frame.right; + inputWindow.frameBottom = frame.bottom; + + child.getTouchableRegion(inputWindow.touchableRegion); + } + + // Send windows to native code. + mService.mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray()); + + // Clear the list in preparation for the next round. + // Also avoids keeping InputChannel objects referenced unnecessarily. + mTempInputWindows.clear(); + } + + /* Notifies that the input device configuration has changed. */ + public void notifyConfigurationChanged() { + mService.sendNewConfiguration(); + + synchronized (mInputDevicesReadyMonitor) { + if (!mInputDevicesReady) { + mInputDevicesReady = true; + mInputDevicesReadyMonitor.notifyAll(); + } + } + } + + /* Waits until the built-in input devices have been configured. */ + public boolean waitForInputDevicesReady(long timeoutMillis) { + synchronized (mInputDevicesReadyMonitor) { + if (!mInputDevicesReady) { + try { + mInputDevicesReadyMonitor.wait(timeoutMillis); + } catch (InterruptedException ex) { + } + } + return mInputDevicesReady; + } + } + + /* Notifies that the lid switch changed state. */ + public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { + mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen); + } + + /* Provides an opportunity for the window manager policy to intercept early key + * processing as soon as the key has been read from the device. */ + public int interceptKeyBeforeQueueing( + KeyEvent event, int policyFlags, boolean isScreenOn) { + return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn); + } + + /* Provides an opportunity for the window manager policy to process a key before + * ordinary dispatch. */ + public boolean interceptKeyBeforeDispatching( + InputWindowHandle focus, KeyEvent event, int policyFlags) { + WindowState windowState = focus != null ? (WindowState) focus.windowState : null; + return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags); + } + + /* Provides an opportunity for the window manager policy to process a key that + * the application did not handle. */ + public KeyEvent dispatchUnhandledKey( + InputWindowHandle focus, KeyEvent event, int policyFlags) { + WindowState windowState = focus != null ? (WindowState) focus.windowState : null; + return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags); + } + + /* Called when the current input focus changes. + * Layer assignment is assumed to be complete by the time this is called. + */ + public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { + if (WindowManagerService.DEBUG_INPUT) { + Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow); + } + + if (newWindow != mInputFocus) { + if (newWindow != null && newWindow.canReceiveKeys()) { + // Displaying a window implicitly causes dispatching to be unpaused. + // This is to protect against bugs if someone pauses dispatching but + // forgets to resume. + newWindow.mToken.paused = false; + } + + mInputFocus = newWindow; + setUpdateInputWindowsNeededLw(); + + if (updateInputWindows) { + updateInputWindowsLw(false /*force*/); + } + } + } + + public void setFocusedAppLw(AppWindowToken newApp) { + // Focused app has changed. + if (newApp == null) { + mService.mInputManager.setFocusedApplication(null); + } else { + mTempInputApplication.inputApplicationHandle = newApp.mInputApplicationHandle; + mTempInputApplication.name = newApp.toString(); + mTempInputApplication.dispatchingTimeoutNanos = + newApp.inputDispatchingTimeoutNanos; + + mService.mInputManager.setFocusedApplication(mTempInputApplication); + + mTempInputApplication.recycle(); + } + } + + public void pauseDispatchingLw(WindowToken window) { + if (! window.paused) { + if (WindowManagerService.DEBUG_INPUT) { + Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window); + } + + window.paused = true; + updateInputWindowsLw(true /*force*/); + } + } + + public void resumeDispatchingLw(WindowToken window) { + if (window.paused) { + if (WindowManagerService.DEBUG_INPUT) { + Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window); + } + + window.paused = false; + updateInputWindowsLw(true /*force*/); + } + } + + public void freezeInputDispatchingLw() { + if (! mInputDispatchFrozen) { + if (WindowManagerService.DEBUG_INPUT) { + Slog.v(WindowManagerService.TAG, "Freezing input dispatching"); + } + + mInputDispatchFrozen = true; + updateInputDispatchModeLw(); + } + } + + public void thawInputDispatchingLw() { + if (mInputDispatchFrozen) { + if (WindowManagerService.DEBUG_INPUT) { + Slog.v(WindowManagerService.TAG, "Thawing input dispatching"); + } + + mInputDispatchFrozen = false; + updateInputDispatchModeLw(); + } + } + + public void setEventDispatchingLw(boolean enabled) { + if (mInputDispatchEnabled != enabled) { + if (WindowManagerService.DEBUG_INPUT) { + Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled); + } + + mInputDispatchEnabled = enabled; + updateInputDispatchModeLw(); + } + } + + private void updateInputDispatchModeLw() { + mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen); + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/wm/InputWindow.java index 2c2cdfe8ae77..e3eb4732dc81 100644 --- a/services/java/com/android/server/InputWindow.java +++ b/services/java/com/android/server/wm/InputWindow.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; import android.graphics.Region; import android.view.InputChannel; diff --git a/services/java/com/android/server/InputWindowHandle.java b/services/java/com/android/server/wm/InputWindowHandle.java index 4b9293918031..cc508c6f7013 100644 --- a/services/java/com/android/server/InputWindowHandle.java +++ b/services/java/com/android/server/wm/InputWindowHandle.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; import android.view.WindowManagerPolicy; diff --git a/services/java/com/android/server/InputWindowList.java b/services/java/com/android/server/wm/InputWindowList.java index 1cbb2cc6f9c0..6077337a8aae 100644 --- a/services/java/com/android/server/InputWindowList.java +++ b/services/java/com/android/server/wm/InputWindowList.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; /** diff --git a/services/java/com/android/server/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java index ef00b08ff1a4..4356ce5ae427 100644 --- a/services/java/com/android/server/ScreenRotationAnimation.java +++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; // TODO: use com.android.server.wm, once things move there +package com.android.server.wm; import android.content.Context; import android.graphics.Bitmap; diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java new file mode 100644 index 000000000000..b9db17702a81 --- /dev/null +++ b/services/java/com/android/server/wm/Session.java @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import com.android.internal.view.IInputContext; +import com.android.internal.view.IInputMethodClient; +import com.android.internal.view.IInputMethodManager; +import com.android.server.wm.WindowManagerService.H; + +import android.content.ClipData; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.graphics.Region; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Slog; +import android.view.IWindow; +import android.view.IWindowSession; +import android.view.InputChannel; +import android.view.Surface; +import android.view.SurfaceSession; +import android.view.WindowManager; + +import java.io.PrintWriter; + +/** + * This class represents an active client session. There is generally one + * Session object per process that is interacting with the window manager. + */ +final class Session extends IWindowSession.Stub + implements IBinder.DeathRecipient { + final WindowManagerService mService; + final IInputMethodClient mClient; + final IInputContext mInputContext; + final int mUid; + final int mPid; + final String mStringName; + SurfaceSession mSurfaceSession; + int mNumWindow = 0; + boolean mClientDead = false; + + public Session(WindowManagerService service, IInputMethodClient client, + IInputContext inputContext) { + mService = service; + mClient = client; + mInputContext = inputContext; + mUid = Binder.getCallingUid(); + mPid = Binder.getCallingPid(); + StringBuilder sb = new StringBuilder(); + sb.append("Session{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" uid "); + sb.append(mUid); + sb.append("}"); + mStringName = sb.toString(); + + synchronized (mService.mWindowMap) { + if (mService.mInputMethodManager == null && mService.mHaveInputMethods) { + IBinder b = ServiceManager.getService( + Context.INPUT_METHOD_SERVICE); + mService.mInputMethodManager = IInputMethodManager.Stub.asInterface(b); + } + } + long ident = Binder.clearCallingIdentity(); + try { + // Note: it is safe to call in to the input method manager + // here because we are not holding our lock. + if (mService.mInputMethodManager != null) { + mService.mInputMethodManager.addClient(client, inputContext, + mUid, mPid); + } else { + client.setUsingInputMethod(false); + } + client.asBinder().linkToDeath(this, 0); + } catch (RemoteException e) { + // The caller has died, so we can just forget about this. + try { + if (mService.mInputMethodManager != null) { + mService.mInputMethodManager.removeClient(client); + } + } catch (RemoteException ee) { + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + try { + return super.onTransact(code, data, reply, flags); + } catch (RuntimeException e) { + // Log all 'real' exceptions thrown to the caller + if (!(e instanceof SecurityException)) { + Slog.e(WindowManagerService.TAG, "Window Session Crash", e); + } + throw e; + } + } + + public void binderDied() { + // Note: it is safe to call in to the input method manager + // here because we are not holding our lock. + try { + if (mService.mInputMethodManager != null) { + mService.mInputMethodManager.removeClient(mClient); + } + } catch (RemoteException e) { + } + synchronized(mService.mWindowMap) { + mClient.asBinder().unlinkToDeath(this, 0); + mClientDead = true; + killSessionLocked(); + } + } + + public int add(IWindow window, WindowManager.LayoutParams attrs, + int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) { + return mService.addWindow(this, window, attrs, viewVisibility, outContentInsets, + outInputChannel); + } + + public int addWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs, + int viewVisibility, Rect outContentInsets) { + return mService.addWindow(this, window, attrs, viewVisibility, outContentInsets, null); + } + + public void remove(IWindow window) { + mService.removeWindow(this, window); + } + + public int relayout(IWindow window, WindowManager.LayoutParams attrs, + int requestedWidth, int requestedHeight, int viewFlags, + boolean insetsPending, Rect outFrame, Rect outContentInsets, + Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { + //Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); + int res = mService.relayoutWindow(this, window, attrs, + requestedWidth, requestedHeight, viewFlags, insetsPending, + outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface); + //Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); + return res; + } + + public void setTransparentRegion(IWindow window, Region region) { + mService.setTransparentRegionWindow(this, window, region); + } + + public void setInsets(IWindow window, int touchableInsets, + Rect contentInsets, Rect visibleInsets, Region touchableArea) { + mService.setInsetsWindow(this, window, touchableInsets, contentInsets, + visibleInsets, touchableArea); + } + + public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { + mService.getWindowDisplayFrame(this, window, outDisplayFrame); + } + + public void finishDrawing(IWindow window) { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "IWindow finishDrawing called for " + window); + mService.finishDrawingWindow(this, window); + } + + public void setInTouchMode(boolean mode) { + synchronized(mService.mWindowMap) { + mService.mInTouchMode = mode; + } + } + + public boolean getInTouchMode() { + synchronized(mService.mWindowMap) { + return mService.mInTouchMode; + } + } + + public boolean performHapticFeedback(IWindow window, int effectId, + boolean always) { + synchronized(mService.mWindowMap) { + long ident = Binder.clearCallingIdentity(); + try { + return mService.mPolicy.performHapticFeedbackLw( + mService.windowForClientLocked(this, window, true), + effectId, always); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + /* Drag/drop */ + public IBinder prepareDrag(IWindow window, int flags, + int width, int height, Surface outSurface) { + return mService.prepareDragSurface(window, mSurfaceSession, flags, + width, height, outSurface); + } + + public boolean performDrag(IWindow window, IBinder dragToken, + float touchX, float touchY, float thumbCenterX, float thumbCenterY, + ClipData data) { + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "perform drag: win=" + window + " data=" + data); + } + + synchronized (mService.mWindowMap) { + if (mService.mDragState == null) { + Slog.w(WindowManagerService.TAG, "No drag prepared"); + throw new IllegalStateException("performDrag() without prepareDrag()"); + } + + if (dragToken != mService.mDragState.mToken) { + Slog.w(WindowManagerService.TAG, "Performing mismatched drag"); + throw new IllegalStateException("performDrag() does not match prepareDrag()"); + } + + WindowState callingWin = mService.windowForClientLocked(null, window, false); + if (callingWin == null) { + Slog.w(WindowManagerService.TAG, "Bad requesting window " + window); + return false; // !!! TODO: throw here? + } + + // !!! TODO: if input is not still focused on the initiating window, fail + // the drag initiation (e.g. an alarm window popped up just as the application + // called performDrag() + + mService.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder()); + + // !!! TODO: extract the current touch (x, y) in screen coordinates. That + // will let us eliminate the (touchX,touchY) parameters from the API. + + // !!! FIXME: put all this heavy stuff onto the mH looper, as well as + // the actual drag event dispatch stuff in the dragstate + + mService.mDragState.register(); + mService.mInputMonitor.updateInputWindowsLw(true /*force*/); + if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel, + mService.mDragState.mServerChannel)) { + Slog.e(WindowManagerService.TAG, "Unable to transfer touch focus"); + mService.mDragState.unregister(); + mService.mDragState = null; + mService.mInputMonitor.updateInputWindowsLw(true /*force*/); + return false; + } + + mService.mDragState.mData = data; + mService.mDragState.mCurrentX = touchX; + mService.mDragState.mCurrentY = touchY; + mService.mDragState.broadcastDragStartedLw(touchX, touchY); + + // remember the thumb offsets for later + mService.mDragState.mThumbOffsetX = thumbCenterX; + mService.mDragState.mThumbOffsetY = thumbCenterY; + + // Make the surface visible at the proper location + final Surface surface = mService.mDragState.mSurface; + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag"); + Surface.openTransaction(); + try { + surface.setPosition((int)(touchX - thumbCenterX), + (int)(touchY - thumbCenterY)); + surface.setAlpha(.7071f); + surface.setLayer(mService.mDragState.getDragLayerLw()); + surface.show(); + } finally { + Surface.closeTransaction(); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION performDrag"); + } + } + + return true; // success! + } + + public void reportDropResult(IWindow window, boolean consumed) { + IBinder token = window.asBinder(); + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "Drop result=" + consumed + " reported by " + token); + } + + synchronized (mService.mWindowMap) { + long ident = Binder.clearCallingIdentity(); + try { + if (mService.mDragState == null || mService.mDragState.mToken != token) { + Slog.w(WindowManagerService.TAG, "Invalid drop-result claim by " + window); + throw new IllegalStateException("reportDropResult() by non-recipient"); + } + + // The right window has responded, even if it's no longer around, + // so be sure to halt the timeout even if the later WindowState + // lookup fails. + mService.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder()); + WindowState callingWin = mService.windowForClientLocked(null, window, false); + if (callingWin == null) { + Slog.w(WindowManagerService.TAG, "Bad result-reporting window " + window); + return; // !!! TODO: throw here? + } + + mService.mDragState.mDragResult = consumed; + mService.mDragState.endDragLw(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + public void dragRecipientEntered(IWindow window) { + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "Drag into new candidate view @ " + window.asBinder()); + } + } + + public void dragRecipientExited(IWindow window) { + if (WindowManagerService.DEBUG_DRAG) { + Slog.d(WindowManagerService.TAG, "Drag from old candidate view @ " + window.asBinder()); + } + } + + public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) { + synchronized(mService.mWindowMap) { + long ident = Binder.clearCallingIdentity(); + try { + mService.setWindowWallpaperPositionLocked( + mService.windowForClientLocked(this, window, true), + x, y, xStep, yStep); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + public void wallpaperOffsetsComplete(IBinder window) { + mService.wallpaperOffsetsComplete(window); + } + + public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, + int z, Bundle extras, boolean sync) { + synchronized(mService.mWindowMap) { + long ident = Binder.clearCallingIdentity(); + try { + return mService.sendWindowWallpaperCommandLocked( + mService.windowForClientLocked(this, window, true), + action, x, y, z, extras, sync); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + public void wallpaperCommandComplete(IBinder window, Bundle result) { + mService.wallpaperCommandComplete(window, result); + } + + void windowAddedLocked() { + if (mSurfaceSession == null) { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "First window added to " + this + ", creating SurfaceSession"); + mSurfaceSession = new SurfaceSession(); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i( + WindowManagerService.TAG, " NEW SURFACE SESSION " + mSurfaceSession); + mService.mSessions.add(this); + } + mNumWindow++; + } + + void windowRemovedLocked() { + mNumWindow--; + killSessionLocked(); + } + + void killSessionLocked() { + if (mNumWindow <= 0 && mClientDead) { + mService.mSessions.remove(this); + if (mSurfaceSession != null) { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Last window removed from " + this + + ", destroying " + mSurfaceSession); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i( + WindowManagerService.TAG, " KILL SURFACE SESSION " + mSurfaceSession); + try { + mSurfaceSession.kill(); + } catch (Exception e) { + Slog.w(WindowManagerService.TAG, "Exception thrown when killing surface session " + + mSurfaceSession + " in session " + this + + ": " + e.toString()); + } + mSurfaceSession = null; + } + } + } + + void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow); + pw.print(" mClientDead="); pw.print(mClientDead); + pw.print(" mSurfaceSession="); pw.println(mSurfaceSession); + } + + @Override + public String toString() { + return mStringName; + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/wm/StartingData.java b/services/java/com/android/server/wm/StartingData.java new file mode 100644 index 000000000000..625fcfeb27d6 --- /dev/null +++ b/services/java/com/android/server/wm/StartingData.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +final class StartingData { + final String pkg; + final int theme; + final CharSequence nonLocalizedLabel; + final int labelRes; + final int icon; + final int windowFlags; + + StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel, + int _labelRes, int _icon, int _windowFlags) { + pkg = _pkg; + theme = _theme; + nonLocalizedLabel = _nonLocalizedLabel; + labelRes = _labelRes; + icon = _icon; + windowFlags = _windowFlags; + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/StrictModeFlash.java b/services/java/com/android/server/wm/StrictModeFlash.java index 0a6c62514efd..2c62080ea7d2 100644 --- a/services/java/com/android/server/StrictModeFlash.java +++ b/services/java/com/android/server/wm/StrictModeFlash.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.android.server; // TODO: use com.android.server.wm, once things move there +package com.android.server.wm; + import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PixelFormat; -import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Region; import android.util.DisplayMetrics; diff --git a/services/java/com/android/server/ViewServer.java b/services/java/com/android/server/wm/ViewServer.java index 7b5d18ac8b2c..cebd5e70e8a8 100644 --- a/services/java/com/android/server/ViewServer.java +++ b/services/java/com/android/server/wm/ViewServer.java @@ -14,7 +14,8 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; + import android.util.Slog; @@ -33,7 +34,7 @@ import java.io.OutputStreamWriter; /** * The ViewServer is local socket server that can be used to communicate with the * views of the opened windows. Communication with the views is ensured by the - * {@link com.android.server.WindowManagerService} and is a cross-process operation. + * {@link com.android.server.wm.WindowManagerService} and is a cross-process operation. * * {@hide} */ diff --git a/services/java/com/android/server/wm/Watermark.java b/services/java/com/android/server/wm/Watermark.java new file mode 100644 index 000000000000..22126f3052ed --- /dev/null +++ b/services/java/com/android/server/wm/Watermark.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.graphics.Paint.FontMetricsInt; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.TypedValue; +import android.view.Display; +import android.view.Surface; +import android.view.SurfaceSession; +import android.view.Surface.OutOfResourcesException; + +/** + * Displays a watermark on top of the window manager's windows. + */ +class Watermark { + final String[] mTokens; + final String mText; + final Paint mTextPaint; + final int mTextWidth; + final int mTextHeight; + final int mTextAscent; + final int mTextDescent; + final int mDeltaX; + final int mDeltaY; + + Surface mSurface; + int mLastDW; + int mLastDH; + boolean mDrawNeeded; + + Watermark(Display display, SurfaceSession session, String[] tokens) { + final DisplayMetrics dm = new DisplayMetrics(); + display.getMetrics(dm); + + if (false) { + Log.i(WindowManagerService.TAG, "*********************** WATERMARK"); + for (int i=0; i<tokens.length; i++) { + Log.i(WindowManagerService.TAG, " TOKEN #" + i + ": " + tokens[i]); + } + } + + mTokens = tokens; + + StringBuilder builder = new StringBuilder(32); + int len = mTokens[0].length(); + len = len & ~1; + for (int i=0; i<len; i+=2) { + int c1 = mTokens[0].charAt(i); + int c2 = mTokens[0].charAt(i+1); + if (c1 >= 'a' && c1 <= 'f') c1 = c1 - 'a' + 10; + else if (c1 >= 'A' && c1 <= 'F') c1 = c1 - 'A' + 10; + else c1 -= '0'; + if (c2 >= 'a' && c2 <= 'f') c2 = c2 - 'a' + 10; + else if (c2 >= 'A' && c2 <= 'F') c2 = c2 - 'A' + 10; + else c2 -= '0'; + builder.append((char)(255-((c1*16)+c2))); + } + mText = builder.toString(); + if (false) { + Log.i(WindowManagerService.TAG, "Final text: " + mText); + } + + int fontSize = WindowManagerService.getPropertyInt(tokens, 1, + TypedValue.COMPLEX_UNIT_DIP, 20, dm); + + mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mTextPaint.setTextSize(fontSize); + mTextPaint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD)); + + FontMetricsInt fm = mTextPaint.getFontMetricsInt(); + mTextWidth = (int)mTextPaint.measureText(mText); + mTextAscent = fm.ascent; + mTextDescent = fm.descent; + mTextHeight = fm.descent - fm.ascent; + + mDeltaX = WindowManagerService.getPropertyInt(tokens, 2, + TypedValue.COMPLEX_UNIT_PX, mTextWidth*2, dm); + mDeltaY = WindowManagerService.getPropertyInt(tokens, 3, + TypedValue.COMPLEX_UNIT_PX, mTextHeight*3, dm); + int shadowColor = WindowManagerService.getPropertyInt(tokens, 4, + TypedValue.COMPLEX_UNIT_PX, 0xb0000000, dm); + int color = WindowManagerService.getPropertyInt(tokens, 5, + TypedValue.COMPLEX_UNIT_PX, 0x60ffffff, dm); + int shadowRadius = WindowManagerService.getPropertyInt(tokens, 6, + TypedValue.COMPLEX_UNIT_PX, 7, dm); + int shadowDx = WindowManagerService.getPropertyInt(tokens, 8, + TypedValue.COMPLEX_UNIT_PX, 0, dm); + int shadowDy = WindowManagerService.getPropertyInt(tokens, 9, + TypedValue.COMPLEX_UNIT_PX, 0, dm); + + mTextPaint.setColor(color); + mTextPaint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor); + + try { + mSurface = new Surface(session, 0, + "WatermarkSurface", -1, 1, 1, PixelFormat.TRANSLUCENT, 0); + mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER*100); + mSurface.setPosition(0, 0); + mSurface.show(); + } catch (OutOfResourcesException e) { + } + } + + void positionSurface(int dw, int dh) { + if (mLastDW != dw || mLastDH != dh) { + mLastDW = dw; + mLastDH = dh; + mSurface.setSize(dw, dh); + mDrawNeeded = true; + } + } + + void drawIfNeeded() { + if (mDrawNeeded) { + final int dw = mLastDW; + final int dh = mLastDH; + + mDrawNeeded = false; + Rect dirty = new Rect(0, 0, dw, dh); + Canvas c = null; + try { + c = mSurface.lockCanvas(dirty); + } catch (IllegalArgumentException e) { + } catch (OutOfResourcesException e) { + } + if (c != null) { + c.drawColor(0, PorterDuff.Mode.CLEAR); + + int deltaX = mDeltaX; + int deltaY = mDeltaY; + + // deltaX shouldn't be close to a round fraction of our + // x step, or else things will line up too much. + int div = (dw+mTextWidth)/deltaX; + int rem = (dw+mTextWidth) - (div*deltaX); + int qdelta = deltaX/4; + if (rem < qdelta || rem > (deltaX-qdelta)) { + deltaX += deltaX/3; + } + + int y = -mTextHeight; + int x = -mTextWidth; + while (y < (dh+mTextHeight)) { + c.drawText(mText, x, y, mTextPaint); + x += deltaX; + if (x >= dw) { + x -= (dw+mTextWidth); + y += deltaY; + } + } + mSurface.unlockCanvasAndPost(c); + } + } + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 3978b9979866..a598ce9ebcd6 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; @@ -22,7 +22,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; @@ -42,6 +41,10 @@ import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; import com.android.internal.view.IInputMethodManager; import com.android.internal.view.WindowManagerPolicyThread; +import com.android.server.AttributeCache; +import com.android.server.EventLogTags; +import com.android.server.PowerManagerService; +import com.android.server.Watchdog; import com.android.server.am.BatteryStatsService; import android.Manifest; @@ -50,8 +53,6 @@ import android.app.IActivityManager; import android.app.StatusBarManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; -import android.content.ClipData; -import android.content.ClipDescription; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -59,17 +60,12 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; -import android.graphics.Paint; import android.graphics.PixelFormat; -import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Region; -import android.graphics.Typeface; -import android.graphics.Paint.FontMetricsInt; import android.os.BatteryStats; import android.os.Binder; import android.os.Bundle; @@ -98,8 +94,6 @@ import android.util.Slog; import android.util.SparseIntArray; import android.util.TypedValue; import android.view.Display; -import android.view.DragEvent; -import android.view.Gravity; import android.view.IApplicationToken; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; @@ -116,13 +110,10 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceSession; import android.view.View; -import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.WindowManagerPolicy; -import android.view.Surface.OutOfResourcesException; import android.view.WindowManager.LayoutParams; -import android.view.animation.AccelerateInterpolator; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.Transformation; @@ -219,7 +210,7 @@ public class WindowManagerService extends IWindowManager.Stub private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000; // Default input dispatching timeout in nanoseconds. - private static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L; + static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L; static final int UPDATE_FOCUS_NORMAL = 0; static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1; @@ -285,6 +276,8 @@ public class WindowManagerService extends IWindowManager.Stub final IBatteryStats mBatteryStats; + private static final boolean mInEmulator = SystemProperties.get("ro.kernel.qemu").equals("1"); + /** * All currently active sessions with clients. */ @@ -505,336 +498,8 @@ public class WindowManagerService extends IWindowManager.Stub boolean mTurnOnScreen; - /** - * Drag/drop state - */ - class DragState { - IBinder mToken; - Surface mSurface; - int mFlags; - IBinder mLocalWin; - ClipData mData; - ClipDescription mDataDescription; - boolean mDragResult; - float mCurrentX, mCurrentY; - float mThumbOffsetX, mThumbOffsetY; - InputChannel mServerChannel, mClientChannel; - WindowState mTargetWindow; - ArrayList<WindowState> mNotifiedWindows; - boolean mDragInProgress; - - private final Region mTmpRegion = new Region(); - - DragState(IBinder token, Surface surface, int flags, IBinder localWin) { - mToken = token; - mSurface = surface; - mFlags = flags; - mLocalWin = localWin; - mNotifiedWindows = new ArrayList<WindowState>(); - } - - void reset() { - if (mSurface != null) { - mSurface.destroy(); - } - mSurface = null; - mFlags = 0; - mLocalWin = null; - mToken = null; - mData = null; - mThumbOffsetX = mThumbOffsetY = 0; - mNotifiedWindows = null; - } - - void register() { - if (DEBUG_DRAG) Slog.d(TAG, "registering drag input channel"); - if (mClientChannel != null) { - Slog.e(TAG, "Duplicate register of drag input channel"); - } else { - InputChannel[] channels = InputChannel.openInputChannelPair("drag"); - mServerChannel = channels[0]; - mClientChannel = channels[1]; - mInputManager.registerInputChannel(mServerChannel, null); - InputQueue.registerInputChannel(mClientChannel, mDragInputHandler, - mH.getLooper().getQueue()); - } - } - - void unregister() { - if (DEBUG_DRAG) Slog.d(TAG, "unregistering drag input channel"); - if (mClientChannel == null) { - Slog.e(TAG, "Unregister of nonexistent drag input channel"); - } else { - mInputManager.unregisterInputChannel(mServerChannel); - InputQueue.unregisterInputChannel(mClientChannel); - mClientChannel.dispose(); - mServerChannel.dispose(); - mClientChannel = null; - mServerChannel = null; - } - } - - int getDragLayerLw() { - return mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG) - * TYPE_LAYER_MULTIPLIER - + TYPE_LAYER_OFFSET; - } - - /* call out to each visible window/session informing it about the drag - */ - void broadcastDragStartedLw(final float touchX, final float touchY) { - // Cache a base-class instance of the clip metadata so that parceling - // works correctly in calling out to the apps. - mDataDescription = (mData != null) ? mData.getDescription() : null; - mNotifiedWindows.clear(); - mDragInProgress = true; - - if (DEBUG_DRAG) { - Slog.d(TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")"); - } - - final int N = mWindows.size(); - for (int i = 0; i < N; i++) { - sendDragStartedLw(mWindows.get(i), touchX, touchY, mDataDescription); - } - } - - /* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the - * designated window is potentially a drop recipient. There are race situations - * around DRAG_ENDED broadcast, so we make sure that once we've declared that - * the drag has ended, we never send out another DRAG_STARTED for this drag action. - * - * This method clones the 'event' parameter if it's being delivered to the same - * process, so it's safe for the caller to call recycle() on the event afterwards. - */ - private void sendDragStartedLw(WindowState newWin, float touchX, float touchY, - ClipDescription desc) { - // Don't actually send the event if the drag is supposed to be pinned - // to the originating window but 'newWin' is not that window. - if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) { - final IBinder winBinder = newWin.mClient.asBinder(); - if (winBinder != mLocalWin) { - if (DEBUG_DRAG) { - Slog.d(TAG, "Not dispatching local DRAG_STARTED to " + newWin); - } - return; - } - } - - if (mDragInProgress && newWin.isPotentialDragTarget()) { - DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, - touchX - newWin.mFrame.left, touchY - newWin.mFrame.top, - null, desc, null, false); - try { - newWin.mClient.dispatchDragEvent(event); - // track each window that we've notified that the drag is starting - mNotifiedWindows.add(newWin); - } catch (RemoteException e) { - Slog.w(TAG, "Unable to drag-start window " + newWin); - } finally { - // if the callee was local, the dispatch has already recycled the event - if (Process.myPid() != newWin.mSession.mPid) { - event.recycle(); - } - } - } - } - - /* helper - construct and send a DRAG_STARTED event only if the window has not - * previously been notified, i.e. it became visible after the drag operation - * was begun. This is a rare case. - */ - private void sendDragStartedIfNeededLw(WindowState newWin) { - if (mDragInProgress) { - // If we have sent the drag-started, we needn't do so again - for (WindowState ws : mNotifiedWindows) { - if (ws == newWin) { - return; - } - } - if (DEBUG_DRAG) { - Slog.d(TAG, "need to send DRAG_STARTED to new window " + newWin); - } - sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription); - } - } - - void broadcastDragEndedLw() { - if (DEBUG_DRAG) { - Slog.d(TAG, "broadcasting DRAG_ENDED"); - } - DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, - 0, 0, null, null, null, mDragResult); - for (WindowState ws: mNotifiedWindows) { - try { - ws.mClient.dispatchDragEvent(evt); - } catch (RemoteException e) { - Slog.w(TAG, "Unable to drag-end window " + ws); - } - } - mNotifiedWindows.clear(); - mDragInProgress = false; - evt.recycle(); - } - - void endDragLw() { - mDragState.broadcastDragEndedLw(); - - // stop intercepting input - mDragState.unregister(); - mInputMonitor.updateInputWindowsLw(true /*force*/); - - // free our resources and drop all the object references - mDragState.reset(); - mDragState = null; - - if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-drag rotation"); - boolean changed = setRotationUncheckedLocked( - WindowManagerPolicy.USE_LAST_ROTATION, 0, false); - if (changed) { - mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); - } - } - - void notifyMoveLw(float x, float y) { - final int myPid = Process.myPid(); - - // Move the surface to the given touch - if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION notifyMoveLw"); - Surface.openTransaction(); - try { - mSurface.setPosition((int)(x - mThumbOffsetX), (int)(y - mThumbOffsetY)); - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DRAG " - + mSurface + ": pos=(" + - (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")"); - } finally { - Surface.closeTransaction(); - if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION notifyMoveLw"); - } - - // Tell the affected window - WindowState touchedWin = getTouchedWinAtPointLw(x, y); - if (touchedWin == null) { - if (DEBUG_DRAG) Slog.d(TAG, "No touched win at x=" + x + " y=" + y); - return; - } - if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) { - final IBinder touchedBinder = touchedWin.mClient.asBinder(); - if (touchedBinder != mLocalWin) { - // This drag is pinned only to the originating window, but the drag - // point is outside that window. Pretend it's over empty space. - touchedWin = null; - } - } - try { - // have we dragged over a new window? - if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) { - if (DEBUG_DRAG) { - Slog.d(TAG, "sending DRAG_EXITED to " + mTargetWindow); - } - // force DRAG_EXITED_EVENT if appropriate - DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED, - x - mTargetWindow.mFrame.left, y - mTargetWindow.mFrame.top, - null, null, null, false); - mTargetWindow.mClient.dispatchDragEvent(evt); - if (myPid != mTargetWindow.mSession.mPid) { - evt.recycle(); - } - } - if (touchedWin != null) { - if (false && DEBUG_DRAG) { - Slog.d(TAG, "sending DRAG_LOCATION to " + touchedWin); - } - DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION, - x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, - null, null, null, false); - touchedWin.mClient.dispatchDragEvent(evt); - if (myPid != touchedWin.mSession.mPid) { - evt.recycle(); - } - } - } catch (RemoteException e) { - Slog.w(TAG, "can't send drag notification to windows"); - } - mTargetWindow = touchedWin; - } - - // Tell the drop target about the data. Returns 'true' if we can immediately - // dispatch the global drag-ended message, 'false' if we need to wait for a - // result from the recipient. - boolean notifyDropLw(float x, float y) { - WindowState touchedWin = getTouchedWinAtPointLw(x, y); - if (touchedWin == null) { - // "drop" outside a valid window -- no recipient to apply a - // timeout to, and we can send the drag-ended message immediately. - mDragResult = false; - return true; - } - - if (DEBUG_DRAG) { - Slog.d(TAG, "sending DROP to " + touchedWin); - } - final int myPid = Process.myPid(); - final IBinder token = touchedWin.mClient.asBinder(); - DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP, - x - touchedWin.mFrame.left, y - touchedWin.mFrame.top, - null, null, mData, false); - try { - touchedWin.mClient.dispatchDragEvent(evt); - - // 5 second timeout for this window to respond to the drop - mH.removeMessages(H.DRAG_END_TIMEOUT, token); - Message msg = mH.obtainMessage(H.DRAG_END_TIMEOUT, token); - mH.sendMessageDelayed(msg, 5000); - } catch (RemoteException e) { - Slog.w(TAG, "can't send drop notification to win " + touchedWin); - return true; - } finally { - if (myPid != touchedWin.mSession.mPid) { - evt.recycle(); - } - } - mToken = token; - return false; - } - - // Find the visible, touch-deliverable window under the given point - private WindowState getTouchedWinAtPointLw(float xf, float yf) { - WindowState touchedWin = null; - final int x = (int) xf; - final int y = (int) yf; - final ArrayList<WindowState> windows = mWindows; - final int N = windows.size(); - for (int i = N - 1; i >= 0; i--) { - WindowState child = windows.get(i); - final int flags = child.mAttrs.flags; - if (!child.isVisibleLw()) { - // not visible == don't tell about drags - continue; - } - if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { - // not touchable == don't tell about drags - continue; - } - - child.getTouchableRegion(mTmpRegion); - - final int touchFlags = flags & - (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); - if (mTmpRegion.contains(x, y) || touchFlags == 0) { - // Found it - touchedWin = child; - break; - } - } - - return touchedWin; - } - } - DragState mDragState = null; - private final InputHandler mDragInputHandler = new BaseInputHandler() { + final InputHandler mDragInputHandler = new BaseInputHandler() { @Override public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) { boolean handled = false; @@ -2298,7 +1963,7 @@ public class WindowManagerService extends IWindowManager.Stub + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } - token = new WindowToken(attrs.token, -1, false); + token = new WindowToken(this, attrs.token, -1, false); addToken = true; } else if (attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW) { @@ -2332,7 +1997,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - win = new WindowState(session, client, token, + win = new WindowState(this, session, client, token, attachedWindow, attrs, viewVisibility); if (win.mDeathRecipient == null) { // Client has apparently died, so there is no reason to @@ -2630,7 +2295,7 @@ public class WindowManagerService extends IWindowManager.Stub mInputMonitor.updateInputWindowsLw(true /*force*/); } - private static void logSurface(WindowState w, String msg, RuntimeException where) { + static void logSurface(WindowState w, String msg, RuntimeException where) { String str = " SURFACE " + Integer.toHexString(w.hashCode()) + ": " + msg + " / " + w.mAttrs.getTitle(); if (where != null) { @@ -2640,7 +2305,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - private void setTransparentRegionWindow(Session session, IWindow client, Region region) { + void setTransparentRegionWindow(Session session, IWindow client, Region region) { long origId = Binder.clearCallingIdentity(); try { synchronized (mWindowMap) { @@ -3094,7 +2759,7 @@ public class WindowManagerService extends IWindowManager.Stub return null; } - private void applyEnterAnimationLocked(WindowState win) { + void applyEnterAnimationLocked(WindowState win) { int transit = WindowManagerPolicy.TRANSIT_SHOW; if (win.mEnterAnimationPending) { win.mEnterAnimationPending = false; @@ -3104,7 +2769,7 @@ public class WindowManagerService extends IWindowManager.Stub applyAnimationLocked(win, transit, true); } - private boolean applyAnimationLocked(WindowState win, + boolean applyAnimationLocked(WindowState win, int transit, boolean isEntrance) { if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) { // If we are trying to apply an animation, but already running @@ -3360,7 +3025,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.w(TAG, "Attempted to add existing input method token: " + token); return; } - wtoken = new WindowToken(token, type, true); + wtoken = new WindowToken(this, token, type, true); mTokenMap.put(token, wtoken); if (type == TYPE_WALLPAPER) { mWallpaperTokens.add(wtoken); @@ -3448,7 +3113,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.w(TAG, "Attempted to add existing app token: " + token); return; } - wtoken = new AppWindowToken(token); + wtoken = new AppWindowToken(this, token); wtoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; wtoken.groupId = groupId; wtoken.appFullscreen = fullscreen; @@ -5188,7 +4853,9 @@ public class WindowManagerService extends IWindowManager.Stub public void setRotationUnchecked(int rotation, boolean alwaysSendConfiguration, int animFlags) { if(DEBUG_ORIENTATION) Slog.v(TAG, - "alwaysSendConfiguration set to "+alwaysSendConfiguration); + "setRotationUnchecked(rotation=" + rotation + + " alwaysSendConfiguration=" + alwaysSendConfiguration + + " animFlags=" + animFlags); long origId = Binder.clearCallingIdentity(); boolean changed; @@ -5259,7 +4926,9 @@ public class WindowManagerService extends IWindowManager.Stub Slog.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags); mInputManager.setDisplayOrientation(0, rotation); if (mDisplayEnabled) { - if (CUSTOM_SCREEN_ROTATION) { + // NOTE: We disable the rotation in the emulator because + // it doesn't support hardware OpenGL emulation yet. + if (CUSTOM_SCREEN_ROTATION && !mInEmulator) { Surface.freezeDisplay(0); if (!inTransaction) { if (SHOW_TRANSACTIONS) Slog.i(TAG, @@ -5341,8 +5010,8 @@ public class WindowManagerService extends IWindowManager.Stub * * @return True if the server was successfully started, false otherwise. * - * @see com.android.server.ViewServer - * @see com.android.server.ViewServer#VIEW_SERVER_DEFAULT_PORT + * @see com.android.server.wm.ViewServer + * @see com.android.server.wm.ViewServer#VIEW_SERVER_DEFAULT_PORT */ public boolean startViewServer(int port) { if (isSystemSecure()) { @@ -5388,7 +5057,7 @@ public class WindowManagerService extends IWindowManager.Stub * @return True if the server stopped, false if it wasn't started or * couldn't be stopped. * - * @see com.android.server.ViewServer + * @see com.android.server.wm.ViewServer */ public boolean stopViewServer() { if (isSystemSecure()) { @@ -5410,7 +5079,7 @@ public class WindowManagerService extends IWindowManager.Stub * * @return True if the server is running, false otherwise. * - * @see com.android.server.ViewServer + * @see com.android.server.wm.ViewServer */ public boolean isViewServerRunning() { if (isSystemSecure()) { @@ -5572,7 +5241,7 @@ public class WindowManagerService extends IWindowManager.Stub parameters = ""; } - final WindowManagerService.WindowState window = findWindow(hashCode); + final WindowState window = findWindow(hashCode); if (window == null) { return false; } @@ -5883,8 +5552,7 @@ public class WindowManagerService extends IWindowManager.Stub outSurface.copyFrom(surface); final IBinder winBinder = window.asBinder(); token = new Binder(); - // TODO: preserve flags param in DragState - mDragState = new DragState(token, surface, 0, winBinder); + mDragState = new DragState(this, token, surface, /*flags*/ 0, winBinder); mDragState.mSurface = surface; token = mDragState.mToken = new Binder(); @@ -5914,355 +5582,8 @@ public class WindowManagerService extends IWindowManager.Stub // Input Events and Focus Management // ------------------------------------------------------------- - InputMonitor mInputMonitor = new InputMonitor(); + final InputMonitor mInputMonitor = new InputMonitor(this); - /* Tracks the progress of input dispatch and ensures that input dispatch state - * is kept in sync with changes in window focus, visibility, registration, and - * other relevant Window Manager state transitions. */ - final class InputMonitor { - // Current window with input focus for keys and other non-touch events. May be null. - private WindowState mInputFocus; - - // When true, prevents input dispatch from proceeding until set to false again. - private boolean mInputDispatchFrozen; - - // When true, input dispatch proceeds normally. Otherwise all events are dropped. - private boolean mInputDispatchEnabled = true; - - // When true, need to call updateInputWindowsLw(). - private boolean mUpdateInputWindowsNeeded = true; - - // Temporary list of windows information to provide to the input dispatcher. - private InputWindowList mTempInputWindows = new InputWindowList(); - - // Temporary input application object to provide to the input dispatcher. - private InputApplication mTempInputApplication = new InputApplication(); - - // Set to true when the first input device configuration change notification - // is received to indicate that the input devices are ready. - private final Object mInputDevicesReadyMonitor = new Object(); - private boolean mInputDevicesReady; - - /* Notifies the window manager about a broken input channel. - * - * Called by the InputManager. - */ - public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) { - if (inputWindowHandle == null) { - return; - } - - synchronized (mWindowMap) { - WindowState windowState = (WindowState) inputWindowHandle.windowState; - Slog.i(TAG, "WINDOW DIED " + windowState); - removeWindowLocked(windowState.mSession, windowState); - } - } - - /* Notifies the window manager about an application that is not responding. - * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch. - * - * Called by the InputManager. - */ - public long notifyANR(InputApplicationHandle inputApplicationHandle, - InputWindowHandle inputWindowHandle) { - AppWindowToken appWindowToken = null; - if (inputWindowHandle != null) { - synchronized (mWindowMap) { - WindowState windowState = (WindowState) inputWindowHandle.windowState; - if (windowState != null) { - Slog.i(TAG, "Input event dispatching timed out sending to " - + windowState.mAttrs.getTitle()); - appWindowToken = windowState.mAppToken; - } - } - } - - if (appWindowToken == null && inputApplicationHandle != null) { - appWindowToken = inputApplicationHandle.appWindowToken; - Slog.i(TAG, "Input event dispatching timed out sending to application " - + appWindowToken.stringName); - } - - if (appWindowToken != null && appWindowToken.appToken != null) { - try { - // Notify the activity manager about the timeout and let it decide whether - // to abort dispatching or keep waiting. - boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(); - if (! abort) { - // The activity manager declined to abort dispatching. - // Wait a bit longer and timeout again later. - return appWindowToken.inputDispatchingTimeoutNanos; - } - } catch (RemoteException ex) { - } - } - return 0; // abort dispatching - } - - private void addDragInputWindowLw(InputWindowList windowList) { - final InputWindow inputWindow = windowList.add(); - inputWindow.inputChannel = mDragState.mServerChannel; - inputWindow.name = "drag"; - inputWindow.layoutParamsFlags = 0; - inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; - inputWindow.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; - inputWindow.visible = true; - inputWindow.canReceiveKeys = false; - inputWindow.hasFocus = true; - inputWindow.hasWallpaper = false; - inputWindow.paused = false; - inputWindow.layer = mDragState.getDragLayerLw(); - inputWindow.ownerPid = Process.myPid(); - inputWindow.ownerUid = Process.myUid(); - - // The drag window covers the entire display - inputWindow.frameLeft = 0; - inputWindow.frameTop = 0; - inputWindow.frameRight = mDisplay.getWidth(); - inputWindow.frameBottom = mDisplay.getHeight(); - - // The drag window cannot receive new touches. - inputWindow.touchableRegion.setEmpty(); - } - - public void setUpdateInputWindowsNeededLw() { - mUpdateInputWindowsNeeded = true; - } - - /* Updates the cached window information provided to the input dispatcher. */ - public void updateInputWindowsLw(boolean force) { - if (!force && !mUpdateInputWindowsNeeded) { - return; - } - mUpdateInputWindowsNeeded = false; - - // Populate the input window list with information about all of the windows that - // could potentially receive input. - // As an optimization, we could try to prune the list of windows but this turns - // out to be difficult because only the native code knows for sure which window - // currently has touch focus. - final ArrayList<WindowState> windows = mWindows; - - // If there's a drag in flight, provide a pseudowindow to catch drag input - final boolean inDrag = (mDragState != null); - if (inDrag) { - if (DEBUG_DRAG) { - Log.d(TAG, "Inserting drag window"); - } - addDragInputWindowLw(mTempInputWindows); - } - - final int N = windows.size(); - for (int i = N - 1; i >= 0; i--) { - final WindowState child = windows.get(i); - if (child.mInputChannel == null || child.mRemoved) { - // Skip this window because it cannot possibly receive input. - continue; - } - - final int flags = child.mAttrs.flags; - final int type = child.mAttrs.type; - - final boolean hasFocus = (child == mInputFocus); - final boolean isVisible = child.isVisibleLw(); - final boolean hasWallpaper = (child == mWallpaperTarget) - && (type != WindowManager.LayoutParams.TYPE_KEYGUARD); - - // If there's a drag in progress and 'child' is a potential drop target, - // make sure it's been told about the drag - if (inDrag && isVisible) { - mDragState.sendDragStartedIfNeededLw(child); - } - - // Add a window to our list of input windows. - final InputWindow inputWindow = mTempInputWindows.add(); - inputWindow.inputWindowHandle = child.mInputWindowHandle; - inputWindow.inputChannel = child.mInputChannel; - inputWindow.name = child.toString(); - inputWindow.layoutParamsFlags = flags; - inputWindow.layoutParamsType = type; - inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); - inputWindow.visible = isVisible; - inputWindow.canReceiveKeys = child.canReceiveKeys(); - inputWindow.hasFocus = hasFocus; - inputWindow.hasWallpaper = hasWallpaper; - inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false; - inputWindow.layer = child.mLayer; - inputWindow.ownerPid = child.mSession.mPid; - inputWindow.ownerUid = child.mSession.mUid; - - final Rect frame = child.mFrame; - inputWindow.frameLeft = frame.left; - inputWindow.frameTop = frame.top; - inputWindow.frameRight = frame.right; - inputWindow.frameBottom = frame.bottom; - - child.getTouchableRegion(inputWindow.touchableRegion); - } - - // Send windows to native code. - mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray()); - - // Clear the list in preparation for the next round. - // Also avoids keeping InputChannel objects referenced unnecessarily. - mTempInputWindows.clear(); - } - - /* Notifies that the input device configuration has changed. */ - public void notifyConfigurationChanged() { - sendNewConfiguration(); - - synchronized (mInputDevicesReadyMonitor) { - if (!mInputDevicesReady) { - mInputDevicesReady = true; - mInputDevicesReadyMonitor.notifyAll(); - } - } - } - - /* Waits until the built-in input devices have been configured. */ - public boolean waitForInputDevicesReady(long timeoutMillis) { - synchronized (mInputDevicesReadyMonitor) { - if (!mInputDevicesReady) { - try { - mInputDevicesReadyMonitor.wait(timeoutMillis); - } catch (InterruptedException ex) { - } - } - return mInputDevicesReady; - } - } - - /* Notifies that the lid switch changed state. */ - public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { - mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen); - } - - /* Provides an opportunity for the window manager policy to intercept early key - * processing as soon as the key has been read from the device. */ - public int interceptKeyBeforeQueueing( - KeyEvent event, int policyFlags, boolean isScreenOn) { - return mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn); - } - - /* Provides an opportunity for the window manager policy to process a key before - * ordinary dispatch. */ - public boolean interceptKeyBeforeDispatching( - InputWindowHandle focus, KeyEvent event, int policyFlags) { - WindowState windowState = focus != null ? (WindowState) focus.windowState : null; - return mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags); - } - - /* Provides an opportunity for the window manager policy to process a key that - * the application did not handle. */ - public KeyEvent dispatchUnhandledKey( - InputWindowHandle focus, KeyEvent event, int policyFlags) { - WindowState windowState = focus != null ? (WindowState) focus.windowState : null; - return mPolicy.dispatchUnhandledKey(windowState, event, policyFlags); - } - - /* Called when the current input focus changes. - * Layer assignment is assumed to be complete by the time this is called. - */ - public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { - if (DEBUG_INPUT) { - Slog.d(TAG, "Input focus has changed to " + newWindow); - } - - if (newWindow != mInputFocus) { - if (newWindow != null && newWindow.canReceiveKeys()) { - // Displaying a window implicitly causes dispatching to be unpaused. - // This is to protect against bugs if someone pauses dispatching but - // forgets to resume. - newWindow.mToken.paused = false; - } - - mInputFocus = newWindow; - setUpdateInputWindowsNeededLw(); - - if (updateInputWindows) { - updateInputWindowsLw(false /*force*/); - } - } - } - - public void setFocusedAppLw(AppWindowToken newApp) { - // Focused app has changed. - if (newApp == null) { - mInputManager.setFocusedApplication(null); - } else { - mTempInputApplication.inputApplicationHandle = newApp.mInputApplicationHandle; - mTempInputApplication.name = newApp.toString(); - mTempInputApplication.dispatchingTimeoutNanos = - newApp.inputDispatchingTimeoutNanos; - - mInputManager.setFocusedApplication(mTempInputApplication); - - mTempInputApplication.recycle(); - } - } - - public void pauseDispatchingLw(WindowToken window) { - if (! window.paused) { - if (DEBUG_INPUT) { - Slog.v(TAG, "Pausing WindowToken " + window); - } - - window.paused = true; - updateInputWindowsLw(true /*force*/); - } - } - - public void resumeDispatchingLw(WindowToken window) { - if (window.paused) { - if (DEBUG_INPUT) { - Slog.v(TAG, "Resuming WindowToken " + window); - } - - window.paused = false; - updateInputWindowsLw(true /*force*/); - } - } - - public void freezeInputDispatchingLw() { - if (! mInputDispatchFrozen) { - if (DEBUG_INPUT) { - Slog.v(TAG, "Freezing input dispatching"); - } - - mInputDispatchFrozen = true; - updateInputDispatchModeLw(); - } - } - - public void thawInputDispatchingLw() { - if (mInputDispatchFrozen) { - if (DEBUG_INPUT) { - Slog.v(TAG, "Thawing input dispatching"); - } - - mInputDispatchFrozen = false; - updateInputDispatchModeLw(); - } - } - - public void setEventDispatchingLw(boolean enabled) { - if (mInputDispatchEnabled != enabled) { - if (DEBUG_INPUT) { - Slog.v(TAG, "Setting event dispatching to " + enabled); - } - - mInputDispatchEnabled = enabled; - updateInputDispatchModeLw(); - } - } - - private void updateInputDispatchModeLw() { - mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen); - } - } - public void pauseKeyDispatching(IBinder _token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "pauseKeyDispatching()")) { @@ -6491,2409 +5812,6 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.systemReady(); } - // ------------------------------------------------------------- - // Client Session State - // ------------------------------------------------------------- - - private final class Session extends IWindowSession.Stub - implements IBinder.DeathRecipient { - final IInputMethodClient mClient; - final IInputContext mInputContext; - final int mUid; - final int mPid; - final String mStringName; - SurfaceSession mSurfaceSession; - int mNumWindow = 0; - boolean mClientDead = false; - - public Session(IInputMethodClient client, IInputContext inputContext) { - mClient = client; - mInputContext = inputContext; - mUid = Binder.getCallingUid(); - mPid = Binder.getCallingPid(); - StringBuilder sb = new StringBuilder(); - sb.append("Session{"); - sb.append(Integer.toHexString(System.identityHashCode(this))); - sb.append(" uid "); - sb.append(mUid); - sb.append("}"); - mStringName = sb.toString(); - - synchronized (mWindowMap) { - if (mInputMethodManager == null && mHaveInputMethods) { - IBinder b = ServiceManager.getService( - Context.INPUT_METHOD_SERVICE); - mInputMethodManager = IInputMethodManager.Stub.asInterface(b); - } - } - long ident = Binder.clearCallingIdentity(); - try { - // Note: it is safe to call in to the input method manager - // here because we are not holding our lock. - if (mInputMethodManager != null) { - mInputMethodManager.addClient(client, inputContext, - mUid, mPid); - } else { - client.setUsingInputMethod(false); - } - client.asBinder().linkToDeath(this, 0); - } catch (RemoteException e) { - // The caller has died, so we can just forget about this. - try { - if (mInputMethodManager != null) { - mInputMethodManager.removeClient(client); - } - } catch (RemoteException ee) { - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public boolean onTransact(int code, Parcel data, Parcel reply, int flags) - throws RemoteException { - try { - return super.onTransact(code, data, reply, flags); - } catch (RuntimeException e) { - // Log all 'real' exceptions thrown to the caller - if (!(e instanceof SecurityException)) { - Slog.e(TAG, "Window Session Crash", e); - } - throw e; - } - } - - public void binderDied() { - // Note: it is safe to call in to the input method manager - // here because we are not holding our lock. - try { - if (mInputMethodManager != null) { - mInputMethodManager.removeClient(mClient); - } - } catch (RemoteException e) { - } - synchronized(mWindowMap) { - mClient.asBinder().unlinkToDeath(this, 0); - mClientDead = true; - killSessionLocked(); - } - } - - public int add(IWindow window, WindowManager.LayoutParams attrs, - int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) { - return addWindow(this, window, attrs, viewVisibility, outContentInsets, - outInputChannel); - } - - public int addWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs, - int viewVisibility, Rect outContentInsets) { - return addWindow(this, window, attrs, viewVisibility, outContentInsets, null); - } - - public void remove(IWindow window) { - removeWindow(this, window); - } - - public int relayout(IWindow window, WindowManager.LayoutParams attrs, - int requestedWidth, int requestedHeight, int viewFlags, - boolean insetsPending, Rect outFrame, Rect outContentInsets, - Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { - //Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); - int res = relayoutWindow(this, window, attrs, - requestedWidth, requestedHeight, viewFlags, insetsPending, - outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface); - //Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); - return res; - } - - public void setTransparentRegion(IWindow window, Region region) { - setTransparentRegionWindow(this, window, region); - } - - public void setInsets(IWindow window, int touchableInsets, - Rect contentInsets, Rect visibleInsets, Region touchableArea) { - setInsetsWindow(this, window, touchableInsets, contentInsets, - visibleInsets, touchableArea); - } - - public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { - getWindowDisplayFrame(this, window, outDisplayFrame); - } - - public void finishDrawing(IWindow window) { - if (localLOGV) Slog.v( - TAG, "IWindow finishDrawing called for " + window); - finishDrawingWindow(this, window); - } - - public void setInTouchMode(boolean mode) { - synchronized(mWindowMap) { - mInTouchMode = mode; - } - } - - public boolean getInTouchMode() { - synchronized(mWindowMap) { - return mInTouchMode; - } - } - - public boolean performHapticFeedback(IWindow window, int effectId, - boolean always) { - synchronized(mWindowMap) { - long ident = Binder.clearCallingIdentity(); - try { - return mPolicy.performHapticFeedbackLw( - windowForClientLocked(this, window, true), - effectId, always); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - /* Drag/drop */ - public IBinder prepareDrag(IWindow window, int flags, - int width, int height, Surface outSurface) { - return prepareDragSurface(window, mSurfaceSession, flags, - width, height, outSurface); - } - - public boolean performDrag(IWindow window, IBinder dragToken, - float touchX, float touchY, float thumbCenterX, float thumbCenterY, - ClipData data) { - if (DEBUG_DRAG) { - Slog.d(TAG, "perform drag: win=" + window + " data=" + data); - } - - synchronized (mWindowMap) { - if (mDragState == null) { - Slog.w(TAG, "No drag prepared"); - throw new IllegalStateException("performDrag() without prepareDrag()"); - } - - if (dragToken != mDragState.mToken) { - Slog.w(TAG, "Performing mismatched drag"); - throw new IllegalStateException("performDrag() does not match prepareDrag()"); - } - - WindowState callingWin = windowForClientLocked(null, window, false); - if (callingWin == null) { - Slog.w(TAG, "Bad requesting window " + window); - return false; // !!! TODO: throw here? - } - - // !!! TODO: if input is not still focused on the initiating window, fail - // the drag initiation (e.g. an alarm window popped up just as the application - // called performDrag() - - mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder()); - - // !!! TODO: extract the current touch (x, y) in screen coordinates. That - // will let us eliminate the (touchX,touchY) parameters from the API. - - // !!! FIXME: put all this heavy stuff onto the mH looper, as well as - // the actual drag event dispatch stuff in the dragstate - - mDragState.register(); - mInputMonitor.updateInputWindowsLw(true /*force*/); - if (!mInputManager.transferTouchFocus(callingWin.mInputChannel, - mDragState.mServerChannel)) { - Slog.e(TAG, "Unable to transfer touch focus"); - mDragState.unregister(); - mDragState = null; - mInputMonitor.updateInputWindowsLw(true /*force*/); - return false; - } - - mDragState.mData = data; - mDragState.mCurrentX = touchX; - mDragState.mCurrentY = touchY; - mDragState.broadcastDragStartedLw(touchX, touchY); - - // remember the thumb offsets for later - mDragState.mThumbOffsetX = thumbCenterX; - mDragState.mThumbOffsetY = thumbCenterY; - - // Make the surface visible at the proper location - final Surface surface = mDragState.mSurface; - if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION performDrag"); - Surface.openTransaction(); - try { - surface.setPosition((int)(touchX - thumbCenterX), - (int)(touchY - thumbCenterY)); - surface.setAlpha(.7071f); - surface.setLayer(mDragState.getDragLayerLw()); - surface.show(); - } finally { - Surface.closeTransaction(); - if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION performDrag"); - } - } - - return true; // success! - } - - public void reportDropResult(IWindow window, boolean consumed) { - IBinder token = window.asBinder(); - if (DEBUG_DRAG) { - Slog.d(TAG, "Drop result=" + consumed + " reported by " + token); - } - - synchronized (mWindowMap) { - long ident = Binder.clearCallingIdentity(); - try { - if (mDragState == null || mDragState.mToken != token) { - Slog.w(TAG, "Invalid drop-result claim by " + window); - throw new IllegalStateException("reportDropResult() by non-recipient"); - } - - // The right window has responded, even if it's no longer around, - // so be sure to halt the timeout even if the later WindowState - // lookup fails. - mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder()); - WindowState callingWin = windowForClientLocked(null, window, false); - if (callingWin == null) { - Slog.w(TAG, "Bad result-reporting window " + window); - return; // !!! TODO: throw here? - } - - mDragState.mDragResult = consumed; - mDragState.endDragLw(); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - public void dragRecipientEntered(IWindow window) { - if (DEBUG_DRAG) { - Slog.d(TAG, "Drag into new candidate view @ " + window.asBinder()); - } - } - - public void dragRecipientExited(IWindow window) { - if (DEBUG_DRAG) { - Slog.d(TAG, "Drag from old candidate view @ " + window.asBinder()); - } - } - - public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) { - synchronized(mWindowMap) { - long ident = Binder.clearCallingIdentity(); - try { - setWindowWallpaperPositionLocked( - windowForClientLocked(this, window, true), - x, y, xStep, yStep); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - public void wallpaperOffsetsComplete(IBinder window) { - WindowManagerService.this.wallpaperOffsetsComplete(window); - } - - public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, - int z, Bundle extras, boolean sync) { - synchronized(mWindowMap) { - long ident = Binder.clearCallingIdentity(); - try { - return sendWindowWallpaperCommandLocked( - windowForClientLocked(this, window, true), - action, x, y, z, extras, sync); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - public void wallpaperCommandComplete(IBinder window, Bundle result) { - WindowManagerService.this.wallpaperCommandComplete(window, result); - } - - void windowAddedLocked() { - if (mSurfaceSession == null) { - if (localLOGV) Slog.v( - TAG, "First window added to " + this + ", creating SurfaceSession"); - mSurfaceSession = new SurfaceSession(); - if (SHOW_TRANSACTIONS) Slog.i( - TAG, " NEW SURFACE SESSION " + mSurfaceSession); - mSessions.add(this); - } - mNumWindow++; - } - - void windowRemovedLocked() { - mNumWindow--; - killSessionLocked(); - } - - void killSessionLocked() { - if (mNumWindow <= 0 && mClientDead) { - mSessions.remove(this); - if (mSurfaceSession != null) { - if (localLOGV) Slog.v( - TAG, "Last window removed from " + this - + ", destroying " + mSurfaceSession); - if (SHOW_TRANSACTIONS) Slog.i( - TAG, " KILL SURFACE SESSION " + mSurfaceSession); - try { - mSurfaceSession.kill(); - } catch (Exception e) { - Slog.w(TAG, "Exception thrown when killing surface session " - + mSurfaceSession + " in session " + this - + ": " + e.toString()); - } - mSurfaceSession = null; - } - } - } - - void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow); - pw.print(" mClientDead="); pw.print(mClientDead); - pw.print(" mSurfaceSession="); pw.println(mSurfaceSession); - } - - @Override - public String toString() { - return mStringName; - } - } - - // ------------------------------------------------------------- - // Client Window State - // ------------------------------------------------------------- - - private final class WindowState implements WindowManagerPolicy.WindowState { - final Session mSession; - final IWindow mClient; - WindowToken mToken; - WindowToken mRootToken; - AppWindowToken mAppToken; - AppWindowToken mTargetAppToken; - final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams(); - final DeathRecipient mDeathRecipient; - final WindowState mAttachedWindow; - final ArrayList<WindowState> mChildWindows = new ArrayList<WindowState>(); - final int mBaseLayer; - final int mSubLayer; - final boolean mLayoutAttached; - final boolean mIsImWindow; - final boolean mIsWallpaper; - final boolean mIsFloatingLayer; - int mViewVisibility; - boolean mPolicyVisibility = true; - boolean mPolicyVisibilityAfterAnim = true; - boolean mAppFreezing; - Surface mSurface; - boolean mReportDestroySurface; - boolean mSurfacePendingDestroy; - boolean mAttachedHidden; // is our parent window hidden? - boolean mLastHidden; // was this window last hidden? - boolean mWallpaperVisible; // for wallpaper, what was last vis report? - int mRequestedWidth; - int mRequestedHeight; - int mLastRequestedWidth; - int mLastRequestedHeight; - int mLayer; - int mAnimLayer; - int mLastLayer; - boolean mHaveFrame; - boolean mObscured; - boolean mTurnOnScreen; - - int mLayoutSeq = -1; - - Configuration mConfiguration = null; - - // Actual frame shown on-screen (may be modified by animation) - final Rect mShownFrame = new Rect(); - final Rect mLastShownFrame = new Rect(); - - /** - * Set when we have changed the size of the surface, to know that - * we must tell them application to resize (and thus redraw itself). - */ - boolean mSurfaceResized; - - /** - * Insets that determine the actually visible area - */ - final Rect mVisibleInsets = new Rect(); - final Rect mLastVisibleInsets = new Rect(); - boolean mVisibleInsetsChanged; - - /** - * Insets that are covered by system windows - */ - final Rect mContentInsets = new Rect(); - final Rect mLastContentInsets = new Rect(); - boolean mContentInsetsChanged; - - /** - * Set to true if we are waiting for this window to receive its - * given internal insets before laying out other windows based on it. - */ - boolean mGivenInsetsPending; - - /** - * These are the content insets that were given during layout for - * this window, to be applied to windows behind it. - */ - final Rect mGivenContentInsets = new Rect(); - - /** - * These are the visible insets that were given during layout for - * this window, to be applied to windows behind it. - */ - final Rect mGivenVisibleInsets = new Rect(); - - /** - * This is the given touchable area relative to the window frame, or null if none. - */ - final Region mGivenTouchableRegion = new Region(); - - /** - * Flag indicating whether the touchable region should be adjusted by - * the visible insets; if false the area outside the visible insets is - * NOT touchable, so we must use those to adjust the frame during hit - * tests. - */ - int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; - - // Current transformation being applied. - boolean mHaveMatrix; - float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1; - float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1; - float mHScale=1, mVScale=1; - float mLastHScale=1, mLastVScale=1; - final Matrix mTmpMatrix = new Matrix(); - - // "Real" frame that the application sees. - final Rect mFrame = new Rect(); - final Rect mLastFrame = new Rect(); - - final Rect mContainingFrame = new Rect(); - final Rect mDisplayFrame = new Rect(); - final Rect mContentFrame = new Rect(); - final Rect mParentFrame = new Rect(); - final Rect mVisibleFrame = new Rect(); - - boolean mContentChanged; - - float mShownAlpha = 1; - float mAlpha = 1; - float mLastAlpha = 1; - - // Set to true if, when the window gets displayed, it should perform - // an enter animation. - boolean mEnterAnimationPending; - - // Currently running animation. - boolean mAnimating; - boolean mLocalAnimating; - Animation mAnimation; - boolean mAnimationIsEntrance; - boolean mHasTransformation; - boolean mHasLocalTransformation; - final Transformation mTransformation = new Transformation(); - - // If a window showing a wallpaper: the requested offset for the - // wallpaper; if a wallpaper window: the currently applied offset. - float mWallpaperX = -1; - float mWallpaperY = -1; - - // If a window showing a wallpaper: what fraction of the offset - // range corresponds to a full virtual screen. - float mWallpaperXStep = -1; - float mWallpaperYStep = -1; - - // Wallpaper windows: pixels offset based on above variables. - int mXOffset; - int mYOffset; - - // This is set after IWindowSession.relayout() has been called at - // least once for the window. It allows us to detect the situation - // where we don't yet have a surface, but should have one soon, so - // we can give the window focus before waiting for the relayout. - boolean mRelayoutCalled; - - // This is set after the Surface has been created but before the - // window has been drawn. During this time the surface is hidden. - boolean mDrawPending; - - // This is set after the window has finished drawing for the first - // time but before its surface is shown. The surface will be - // displayed when the next layout is run. - boolean mCommitDrawPending; - - // This is set during the time after the window's drawing has been - // committed, and before its surface is actually shown. It is used - // to delay showing the surface until all windows in a token are ready - // to be shown. - boolean mReadyToShow; - - // Set when the window has been shown in the screen the first time. - boolean mHasDrawn; - - // Currently running an exit animation? - boolean mExiting; - - // Currently on the mDestroySurface list? - boolean mDestroying; - - // Completely remove from window manager after exit animation? - boolean mRemoveOnExit; - - // Set when the orientation is changing and this window has not yet - // been updated for the new orientation. - boolean mOrientationChanging; - - // Is this window now (or just being) removed? - boolean mRemoved; - - // Temp for keeping track of windows that have been removed when - // rebuilding window list. - boolean mRebuilding; - - // For debugging, this is the last information given to the surface flinger. - boolean mSurfaceShown; - int mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH; - int mSurfaceLayer; - float mSurfaceAlpha; - - // Input channel and input window handle used by the input dispatcher. - InputWindowHandle mInputWindowHandle; - InputChannel mInputChannel; - - // Used to improve performance of toString() - String mStringNameCache; - CharSequence mLastTitle; - boolean mWasPaused; - - WindowState(Session s, IWindow c, WindowToken token, - WindowState attachedWindow, WindowManager.LayoutParams a, - int viewVisibility) { - mSession = s; - mClient = c; - mToken = token; - mAttrs.copyFrom(a); - mViewVisibility = viewVisibility; - DeathRecipient deathRecipient = new DeathRecipient(); - mAlpha = a.alpha; - if (localLOGV) Slog.v( - TAG, "Window " + this + " client=" + c.asBinder() - + " token=" + token + " (" + mAttrs.token + ")"); - try { - c.asBinder().linkToDeath(deathRecipient, 0); - } catch (RemoteException e) { - mDeathRecipient = null; - mAttachedWindow = null; - mLayoutAttached = false; - mIsImWindow = false; - mIsWallpaper = false; - mIsFloatingLayer = false; - mBaseLayer = 0; - mSubLayer = 0; - return; - } - mDeathRecipient = deathRecipient; - - if ((mAttrs.type >= FIRST_SUB_WINDOW && - mAttrs.type <= LAST_SUB_WINDOW)) { - // The multiplier here is to reserve space for multiple - // windows in the same type layer. - mBaseLayer = mPolicy.windowTypeToLayerLw( - attachedWindow.mAttrs.type) * TYPE_LAYER_MULTIPLIER - + TYPE_LAYER_OFFSET; - mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type); - mAttachedWindow = attachedWindow; - if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to " + mAttachedWindow); - mAttachedWindow.mChildWindows.add(this); - mLayoutAttached = mAttrs.type != - WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; - mIsImWindow = attachedWindow.mAttrs.type == TYPE_INPUT_METHOD - || attachedWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG; - mIsWallpaper = attachedWindow.mAttrs.type == TYPE_WALLPAPER; - mIsFloatingLayer = mIsImWindow || mIsWallpaper; - } else { - // The multiplier here is to reserve space for multiple - // windows in the same type layer. - mBaseLayer = mPolicy.windowTypeToLayerLw(a.type) - * TYPE_LAYER_MULTIPLIER - + TYPE_LAYER_OFFSET; - mSubLayer = 0; - mAttachedWindow = null; - mLayoutAttached = false; - mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD - || mAttrs.type == TYPE_INPUT_METHOD_DIALOG; - mIsWallpaper = mAttrs.type == TYPE_WALLPAPER; - mIsFloatingLayer = mIsImWindow || mIsWallpaper; - } - - WindowState appWin = this; - while (appWin.mAttachedWindow != null) { - appWin = mAttachedWindow; - } - WindowToken appToken = appWin.mToken; - while (appToken.appWindowToken == null) { - WindowToken parent = mTokenMap.get(appToken.token); - if (parent == null || appToken == parent) { - break; - } - appToken = parent; - } - mRootToken = appToken; - mAppToken = appToken.appWindowToken; - - mSurface = null; - mRequestedWidth = 0; - mRequestedHeight = 0; - mLastRequestedWidth = 0; - mLastRequestedHeight = 0; - mXOffset = 0; - mYOffset = 0; - mLayer = 0; - mAnimLayer = 0; - mLastLayer = 0; - mInputWindowHandle = new InputWindowHandle( - mAppToken != null ? mAppToken.mInputApplicationHandle : null, this); - } - - void attach() { - if (localLOGV) Slog.v( - TAG, "Attaching " + this + " token=" + mToken - + ", list=" + mToken.windows); - mSession.windowAddedLocked(); - } - - public void computeFrameLw(Rect pf, Rect df, Rect cf, Rect vf) { - mHaveFrame = true; - - final Rect container = mContainingFrame; - container.set(pf); - - final Rect display = mDisplayFrame; - display.set(df); - - if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) { - container.intersect(mCompatibleScreenFrame); - if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) { - display.intersect(mCompatibleScreenFrame); - } - } - - final int pw = container.right - container.left; - final int ph = container.bottom - container.top; - - int w,h; - if ((mAttrs.flags & mAttrs.FLAG_SCALED) != 0) { - w = mAttrs.width < 0 ? pw : mAttrs.width; - h = mAttrs.height< 0 ? ph : mAttrs.height; - } else { - w = mAttrs.width == mAttrs.MATCH_PARENT ? pw : mRequestedWidth; - h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight; - } - - if (!mParentFrame.equals(pf)) { - //Slog.i(TAG, "Window " + this + " content frame from " + mParentFrame - // + " to " + pf); - mParentFrame.set(pf); - mContentChanged = true; - } - - final Rect content = mContentFrame; - content.set(cf); - - final Rect visible = mVisibleFrame; - visible.set(vf); - - final Rect frame = mFrame; - final int fw = frame.width(); - final int fh = frame.height(); - - //System.out.println("In: w=" + w + " h=" + h + " container=" + - // container + " x=" + mAttrs.x + " y=" + mAttrs.y); - - Gravity.apply(mAttrs.gravity, w, h, container, - (int) (mAttrs.x + mAttrs.horizontalMargin * pw), - (int) (mAttrs.y + mAttrs.verticalMargin * ph), frame); - - //System.out.println("Out: " + mFrame); - - // Now make sure the window fits in the overall display. - Gravity.applyDisplay(mAttrs.gravity, df, frame); - - // Make sure the content and visible frames are inside of the - // final window frame. - if (content.left < frame.left) content.left = frame.left; - if (content.top < frame.top) content.top = frame.top; - if (content.right > frame.right) content.right = frame.right; - if (content.bottom > frame.bottom) content.bottom = frame.bottom; - if (visible.left < frame.left) visible.left = frame.left; - if (visible.top < frame.top) visible.top = frame.top; - if (visible.right > frame.right) visible.right = frame.right; - if (visible.bottom > frame.bottom) visible.bottom = frame.bottom; - - final Rect contentInsets = mContentInsets; - contentInsets.left = content.left-frame.left; - contentInsets.top = content.top-frame.top; - contentInsets.right = frame.right-content.right; - contentInsets.bottom = frame.bottom-content.bottom; - - final Rect visibleInsets = mVisibleInsets; - visibleInsets.left = visible.left-frame.left; - visibleInsets.top = visible.top-frame.top; - visibleInsets.right = frame.right-visible.right; - visibleInsets.bottom = frame.bottom-visible.bottom; - - if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) { - updateWallpaperOffsetLocked(this, mDisplay.getWidth(), - mDisplay.getHeight(), false); - } - - if (localLOGV) { - //if ("com.google.android.youtube".equals(mAttrs.packageName) - // && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { - Slog.v(TAG, "Resolving (mRequestedWidth=" - + mRequestedWidth + ", mRequestedheight=" - + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph - + "): frame=" + mFrame.toShortString() - + " ci=" + contentInsets.toShortString() - + " vi=" + visibleInsets.toShortString()); - //} - } - } - - public Rect getFrameLw() { - return mFrame; - } - - public Rect getShownFrameLw() { - return mShownFrame; - } - - public Rect getDisplayFrameLw() { - return mDisplayFrame; - } - - public Rect getContentFrameLw() { - return mContentFrame; - } - - public Rect getVisibleFrameLw() { - return mVisibleFrame; - } - - public boolean getGivenInsetsPendingLw() { - return mGivenInsetsPending; - } - - public Rect getGivenContentInsetsLw() { - return mGivenContentInsets; - } - - public Rect getGivenVisibleInsetsLw() { - return mGivenVisibleInsets; - } - - public WindowManager.LayoutParams getAttrs() { - return mAttrs; - } - - public int getSurfaceLayer() { - return mLayer; - } - - public IApplicationToken getAppToken() { - return mAppToken != null ? mAppToken.appToken : null; - } - - public long getInputDispatchingTimeoutNanos() { - return mAppToken != null - ? mAppToken.inputDispatchingTimeoutNanos - : DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; - } - - public boolean hasAppShownWindows() { - return mAppToken != null ? mAppToken.firstWindowDrawn : false; - } - - public void setAnimation(Animation anim) { - if (localLOGV) Slog.v( - TAG, "Setting animation in " + this + ": " + anim); - mAnimating = false; - mLocalAnimating = false; - mAnimation = anim; - mAnimation.restrictDuration(MAX_ANIMATION_DURATION); - mAnimation.scaleCurrentDuration(mWindowAnimationScale); - } - - public void clearAnimation() { - if (mAnimation != null) { - mAnimating = true; - mLocalAnimating = false; - mAnimation.cancel(); - mAnimation = null; - } - } - - Surface createSurfaceLocked() { - if (mSurface == null) { - mReportDestroySurface = false; - mSurfacePendingDestroy = false; - mDrawPending = true; - mCommitDrawPending = false; - mReadyToShow = false; - if (mAppToken != null) { - mAppToken.allDrawn = false; - } - - int flags = 0; - - if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) { - flags |= Surface.SECURE; - } - if (DEBUG_VISIBILITY) Slog.v( - TAG, "Creating surface in session " - + mSession.mSurfaceSession + " window " + this - + " w=" + mFrame.width() - + " h=" + mFrame.height() + " format=" - + mAttrs.format + " flags=" + flags); - - int w = mFrame.width(); - int h = mFrame.height(); - if ((mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) { - // for a scaled surface, we always want the requested - // size. - w = mRequestedWidth; - h = mRequestedHeight; - } - - // Something is wrong and SurfaceFlinger will not like this, - // try to revert to sane values - if (w <= 0) w = 1; - if (h <= 0) h = 1; - - mSurfaceShown = false; - mSurfaceLayer = 0; - mSurfaceAlpha = 1; - mSurfaceX = 0; - mSurfaceY = 0; - mSurfaceW = w; - mSurfaceH = h; - try { - final boolean isHwAccelerated = (mAttrs.flags & - WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; - final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : mAttrs.format; - if (isHwAccelerated && mAttrs.format == PixelFormat.OPAQUE) { - flags |= Surface.OPAQUE; - } - mSurface = new Surface( - mSession.mSurfaceSession, mSession.mPid, - mAttrs.getTitle().toString(), - 0, w, h, format, flags); - if (SHOW_TRANSACTIONS) Slog.i(TAG, " CREATE SURFACE " - + mSurface + " IN SESSION " - + mSession.mSurfaceSession - + ": pid=" + mSession.mPid + " format=" - + mAttrs.format + " flags=0x" - + Integer.toHexString(flags) - + " / " + this); - } catch (Surface.OutOfResourcesException e) { - Slog.w(TAG, "OutOfResourcesException creating surface"); - reclaimSomeSurfaceMemoryLocked(this, "create"); - return null; - } catch (Exception e) { - Slog.e(TAG, "Exception creating surface", e); - return null; - } - - if (localLOGV) Slog.v( - TAG, "Got surface: " + mSurface - + ", set left=" + mFrame.left + " top=" + mFrame.top - + ", animLayer=" + mAnimLayer); - if (SHOW_TRANSACTIONS) { - Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked"); - logSurface(this, "CREATE pos=(" + mFrame.left + "," + mFrame.top + ") (" + - mFrame.width() + "x" + mFrame.height() + "), layer=" + - mAnimLayer + " HIDE", null); - } - Surface.openTransaction(); - try { - try { - mSurfaceX = mFrame.left + mXOffset; - mSurfaceY = mFrame.top + mYOffset; - mSurface.setPosition(mSurfaceX, mSurfaceY); - mSurfaceLayer = mAnimLayer; - mSurface.setLayer(mAnimLayer); - mSurfaceShown = false; - mSurface.hide(); - if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) { - if (SHOW_TRANSACTIONS) logSurface(this, "DITHER", null); - mSurface.setFlags(Surface.SURFACE_DITHER, - Surface.SURFACE_DITHER); - } - } catch (RuntimeException e) { - Slog.w(TAG, "Error creating surface in " + w, e); - reclaimSomeSurfaceMemoryLocked(this, "create-init"); - } - mLastHidden = true; - } finally { - Surface.closeTransaction(); - if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION createSurfaceLocked"); - } - if (localLOGV) Slog.v( - TAG, "Created surface " + this); - } - return mSurface; - } - - void destroySurfaceLocked() { - if (mAppToken != null && this == mAppToken.startingWindow) { - mAppToken.startingDisplayed = false; - } - - if (mSurface != null) { - mDrawPending = false; - mCommitDrawPending = false; - mReadyToShow = false; - - int i = mChildWindows.size(); - while (i > 0) { - i--; - WindowState c = mChildWindows.get(i); - c.mAttachedHidden = true; - } - - if (mReportDestroySurface) { - mReportDestroySurface = false; - mSurfacePendingDestroy = true; - try { - mClient.dispatchGetNewSurface(); - // We'll really destroy on the next time around. - return; - } catch (RemoteException e) { - } - } - - try { - if (DEBUG_VISIBILITY) { - RuntimeException e = null; - if (!HIDE_STACK_CRAWLS) { - e = new RuntimeException(); - e.fillInStackTrace(); - } - Slog.w(TAG, "Window " + this + " destroying surface " - + mSurface + ", session " + mSession, e); - } - if (SHOW_TRANSACTIONS) { - RuntimeException e = null; - if (!HIDE_STACK_CRAWLS) { - e = new RuntimeException(); - e.fillInStackTrace(); - } - if (SHOW_TRANSACTIONS) logSurface(this, "DESTROY", e); - } - mSurface.destroy(); - } catch (RuntimeException e) { - Slog.w(TAG, "Exception thrown when destroying Window " + this - + " surface " + mSurface + " session " + mSession - + ": " + e.toString()); - } - - mSurfaceShown = false; - mSurface = null; - } - } - - boolean finishDrawingLocked() { - if (mDrawPending) { - if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) Slog.v( - TAG, "finishDrawingLocked: " + mSurface); - mCommitDrawPending = true; - mDrawPending = false; - return true; - } - return false; - } - - // This must be called while inside a transaction. - boolean commitFinishDrawingLocked(long currentTime) { - //Slog.i(TAG, "commitFinishDrawingLocked: " + mSurface); - if (!mCommitDrawPending) { - return false; - } - mCommitDrawPending = false; - mReadyToShow = true; - final boolean starting = mAttrs.type == TYPE_APPLICATION_STARTING; - final AppWindowToken atoken = mAppToken; - if (atoken == null || atoken.allDrawn || starting) { - performShowLocked(); - } - return true; - } - - // This must be called while inside a transaction. - boolean performShowLocked() { - if (DEBUG_VISIBILITY) { - RuntimeException e = null; - if (!HIDE_STACK_CRAWLS) { - e = new RuntimeException(); - e.fillInStackTrace(); - } - Slog.v(TAG, "performShow on " + this - + ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay() - + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e); - } - if (mReadyToShow && isReadyForDisplay()) { - if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) logSurface(this, - "SHOW (performShowLocked)", null); - if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this - + " during animation: policyVis=" + mPolicyVisibility - + " attHidden=" + mAttachedHidden - + " tok.hiddenRequested=" - + (mAppToken != null ? mAppToken.hiddenRequested : false) - + " tok.hidden=" - + (mAppToken != null ? mAppToken.hidden : false) - + " animating=" + mAnimating - + " tok animating=" - + (mAppToken != null ? mAppToken.animating : false)); - if (!showSurfaceRobustlyLocked(this)) { - return false; - } - mLastAlpha = -1; - mHasDrawn = true; - mLastHidden = false; - mReadyToShow = false; - enableScreenIfNeededLocked(); - - applyEnterAnimationLocked(this); - - int i = mChildWindows.size(); - while (i > 0) { - i--; - WindowState c = mChildWindows.get(i); - if (c.mAttachedHidden) { - c.mAttachedHidden = false; - if (c.mSurface != null) { - c.performShowLocked(); - // It hadn't been shown, which means layout not - // performed on it, so now we want to make sure to - // do a layout. If called from within the transaction - // loop, this will cause it to restart with a new - // layout. - mLayoutNeeded = true; - } - } - } - - if (mAttrs.type != TYPE_APPLICATION_STARTING - && mAppToken != null) { - mAppToken.firstWindowDrawn = true; - - if (mAppToken.startingData != null) { - if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Slog.v(TAG, - "Finish starting " + mToken - + ": first real window is shown, no animation"); - // If this initial window is animating, stop it -- we - // will do an animation to reveal it from behind the - // starting window, so there is no need for it to also - // be doing its own stuff. - if (mAnimation != null) { - mAnimation.cancel(); - mAnimation = null; - // Make sure we clean up the animation. - mAnimating = true; - } - mFinishedStarting.add(mAppToken); - mH.sendEmptyMessage(H.FINISHED_STARTING); - } - mAppToken.updateReportedVisibilityLocked(); - } - } - return true; - } - - // This must be called while inside a transaction. Returns true if - // there is more animation to run. - boolean stepAnimationLocked(long currentTime, int dw, int dh) { - if (!mDisplayFrozen && mPolicy.isScreenOn()) { - // We will run animations as long as the display isn't frozen. - - if (!mDrawPending && !mCommitDrawPending && mAnimation != null) { - mHasTransformation = true; - mHasLocalTransformation = true; - if (!mLocalAnimating) { - if (DEBUG_ANIM) Slog.v( - TAG, "Starting animation in " + this + - " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() + - " dw=" + dw + " dh=" + dh + " scale=" + mWindowAnimationScale); - mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh); - mAnimation.setStartTime(currentTime); - mLocalAnimating = true; - mAnimating = true; - } - mTransformation.clear(); - final boolean more = mAnimation.getTransformation( - currentTime, mTransformation); - if (DEBUG_ANIM) Slog.v( - TAG, "Stepped animation in " + this + - ": more=" + more + ", xform=" + mTransformation); - if (more) { - // we're not done! - return true; - } - if (DEBUG_ANIM) Slog.v( - TAG, "Finished animation in " + this + - " @ " + currentTime); - - if (mAnimation != null) { - mAnimation.cancel(); - mAnimation = null; - } - //WindowManagerService.this.dump(); - } - mHasLocalTransformation = false; - if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null - && mAppToken.animation != null) { - // When our app token is animating, we kind-of pretend like - // we are as well. Note the mLocalAnimating mAnimationIsEntrance - // part of this check means that we will only do this if - // our window is not currently exiting, or it is not - // locally animating itself. The idea being that one that - // is exiting and doing a local animation should be removed - // once that animation is done. - mAnimating = true; - mHasTransformation = true; - mTransformation.clear(); - return false; - } else if (mHasTransformation) { - // Little trick to get through the path below to act like - // we have finished an animation. - mAnimating = true; - } else if (isAnimating()) { - mAnimating = true; - } - } else if (mAnimation != null) { - // If the display is frozen, and there is a pending animation, - // clear it and make sure we run the cleanup code. - mAnimating = true; - mLocalAnimating = true; - mAnimation.cancel(); - mAnimation = null; - } - - if (!mAnimating && !mLocalAnimating) { - return false; - } - - if (DEBUG_ANIM) Slog.v( - TAG, "Animation done in " + this + ": exiting=" + mExiting - + ", reportedVisible=" - + (mAppToken != null ? mAppToken.reportedVisible : false)); - - mAnimating = false; - mLocalAnimating = false; - if (mAnimation != null) { - mAnimation.cancel(); - mAnimation = null; - } - mAnimLayer = mLayer; - if (mIsImWindow) { - mAnimLayer += mInputMethodAnimLayerAdjustment; - } else if (mIsWallpaper) { - mAnimLayer += mWallpaperAnimLayerAdjustment; - } - if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this - + " anim layer: " + mAnimLayer); - mHasTransformation = false; - mHasLocalTransformation = false; - if (mPolicyVisibility != mPolicyVisibilityAfterAnim) { - if (DEBUG_VISIBILITY) { - Slog.v(TAG, "Policy visibility changing after anim in " + this + ": " - + mPolicyVisibilityAfterAnim); - } - mPolicyVisibility = mPolicyVisibilityAfterAnim; - if (!mPolicyVisibility) { - if (mCurrentFocus == this) { - mFocusMayChange = true; - } - // Window is no longer visible -- make sure if we were waiting - // for it to be displayed before enabling the display, that - // we allow the display to be enabled now. - enableScreenIfNeededLocked(); - } - } - mTransformation.clear(); - if (mHasDrawn - && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING - && mAppToken != null - && mAppToken.firstWindowDrawn - && mAppToken.startingData != null) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Finish starting " - + mToken + ": first real window done animating"); - mFinishedStarting.add(mAppToken); - mH.sendEmptyMessage(H.FINISHED_STARTING); - } - - finishExit(); - - if (mAppToken != null) { - mAppToken.updateReportedVisibilityLocked(); - } - - return false; - } - - void finishExit() { - if (DEBUG_ANIM) Slog.v( - TAG, "finishExit in " + this - + ": exiting=" + mExiting - + " remove=" + mRemoveOnExit - + " windowAnimating=" + isWindowAnimating()); - - final int N = mChildWindows.size(); - for (int i=0; i<N; i++) { - mChildWindows.get(i).finishExit(); - } - - if (!mExiting) { - return; - } - - if (isWindowAnimating()) { - return; - } - - if (localLOGV) Slog.v( - TAG, "Exit animation finished in " + this - + ": remove=" + mRemoveOnExit); - if (mSurface != null) { - mDestroySurface.add(this); - mDestroying = true; - if (SHOW_TRANSACTIONS) logSurface(this, "HIDE (finishExit)", null); - mSurfaceShown = false; - try { - mSurface.hide(); - } catch (RuntimeException e) { - Slog.w(TAG, "Error hiding surface in " + this, e); - } - mLastHidden = true; - } - mExiting = false; - if (mRemoveOnExit) { - mPendingRemove.add(this); - mRemoveOnExit = false; - } - } - - boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { - if (dsdx < .99999f || dsdx > 1.00001f) return false; - if (dtdy < .99999f || dtdy > 1.00001f) return false; - if (dtdx < -.000001f || dtdx > .000001f) return false; - if (dsdy < -.000001f || dsdy > .000001f) return false; - return true; - } - - void computeShownFrameLocked() { - final boolean selfTransformation = mHasLocalTransformation; - Transformation attachedTransformation = - (mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation) - ? mAttachedWindow.mTransformation : null; - Transformation appTransformation = - (mAppToken != null && mAppToken.hasTransformation) - ? mAppToken.transformation : null; - - // Wallpapers are animated based on the "real" window they - // are currently targeting. - if (mAttrs.type == TYPE_WALLPAPER && mLowerWallpaperTarget == null - && mWallpaperTarget != null) { - if (mWallpaperTarget.mHasLocalTransformation && - mWallpaperTarget.mAnimation != null && - !mWallpaperTarget.mAnimation.getDetachWallpaper()) { - attachedTransformation = mWallpaperTarget.mTransformation; - if (DEBUG_WALLPAPER && attachedTransformation != null) { - Slog.v(TAG, "WP target attached xform: " + attachedTransformation); - } - } - if (mWallpaperTarget.mAppToken != null && - mWallpaperTarget.mAppToken.hasTransformation && - mWallpaperTarget.mAppToken.animation != null && - !mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) { - appTransformation = mWallpaperTarget.mAppToken.transformation; - if (DEBUG_WALLPAPER && appTransformation != null) { - Slog.v(TAG, "WP target app xform: " + appTransformation); - } - } - } - - final boolean screenAnimation = mScreenRotationAnimation != null - && mScreenRotationAnimation.isAnimating(); - if (selfTransformation || attachedTransformation != null - || appTransformation != null || screenAnimation) { - // cache often used attributes locally - final Rect frame = mFrame; - final float tmpFloats[] = mTmpFloats; - final Matrix tmpMatrix = mTmpMatrix; - - // Compute the desired transformation. - tmpMatrix.setTranslate(0, 0); - if (selfTransformation) { - tmpMatrix.postConcat(mTransformation.getMatrix()); - } - tmpMatrix.postTranslate(frame.left + mXOffset, frame.top + mYOffset); - if (attachedTransformation != null) { - tmpMatrix.postConcat(attachedTransformation.getMatrix()); - } - if (appTransformation != null) { - tmpMatrix.postConcat(appTransformation.getMatrix()); - } - if (screenAnimation) { - tmpMatrix.postConcat( - mScreenRotationAnimation.getEnterTransformation().getMatrix()); - } - - // "convert" it into SurfaceFlinger's format - // (a 2x2 matrix + an offset) - // Here we must not transform the position of the surface - // since it is already included in the transformation. - //Slog.i(TAG, "Transform: " + matrix); - - mHaveMatrix = true; - tmpMatrix.getValues(tmpFloats); - mDsDx = tmpFloats[Matrix.MSCALE_X]; - mDtDx = tmpFloats[Matrix.MSKEW_Y]; - mDsDy = tmpFloats[Matrix.MSKEW_X]; - mDtDy = tmpFloats[Matrix.MSCALE_Y]; - int x = (int)tmpFloats[Matrix.MTRANS_X]; - int y = (int)tmpFloats[Matrix.MTRANS_Y]; - int w = frame.width(); - int h = frame.height(); - mShownFrame.set(x, y, x+w, y+h); - - // Now set the alpha... but because our current hardware - // can't do alpha transformation on a non-opaque surface, - // turn it off if we are running an animation that is also - // transforming since it is more important to have that - // animation be smooth. - mShownAlpha = mAlpha; - if (!mLimitedAlphaCompositing - || (!PixelFormat.formatHasAlpha(mAttrs.format) - || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy) - && x == frame.left && y == frame.top))) { - //Slog.i(TAG, "Applying alpha transform"); - if (selfTransformation) { - mShownAlpha *= mTransformation.getAlpha(); - } - if (attachedTransformation != null) { - mShownAlpha *= attachedTransformation.getAlpha(); - } - if (appTransformation != null) { - mShownAlpha *= appTransformation.getAlpha(); - } - if (screenAnimation) { - mShownAlpha *= - mScreenRotationAnimation.getEnterTransformation().getAlpha(); - } - } else { - //Slog.i(TAG, "Not applying alpha transform"); - } - - if (localLOGV) Slog.v( - TAG, "Continuing animation in " + this + - ": " + mShownFrame + - ", alpha=" + mTransformation.getAlpha()); - return; - } - - mShownFrame.set(mFrame); - if (mXOffset != 0 || mYOffset != 0) { - mShownFrame.offset(mXOffset, mYOffset); - } - mShownAlpha = mAlpha; - mHaveMatrix = false; - mDsDx = 1; - mDtDx = 0; - mDsDy = 0; - mDtDy = 1; - } - - /** - * Is this window visible? It is not visible if there is no - * surface, or we are in the process of running an exit animation - * that will remove the surface, or its app token has been hidden. - */ - public boolean isVisibleLw() { - final AppWindowToken atoken = mAppToken; - return mSurface != null && mPolicyVisibility && !mAttachedHidden - && (atoken == null || !atoken.hiddenRequested) - && !mExiting && !mDestroying; - } - - /** - * Like {@link #isVisibleLw}, but also counts a window that is currently - * "hidden" behind the keyguard as visible. This allows us to apply - * things like window flags that impact the keyguard. - * XXX I am starting to think we need to have ANOTHER visibility flag - * for this "hidden behind keyguard" state rather than overloading - * mPolicyVisibility. Ungh. - */ - public boolean isVisibleOrBehindKeyguardLw() { - final AppWindowToken atoken = mAppToken; - return mSurface != null && !mAttachedHidden - && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested) - && !mDrawPending && !mCommitDrawPending - && !mExiting && !mDestroying; - } - - /** - * Is this window visible, ignoring its app token? It is not visible - * if there is no surface, or we are in the process of running an exit animation - * that will remove the surface. - */ - public boolean isWinVisibleLw() { - final AppWindowToken atoken = mAppToken; - return mSurface != null && mPolicyVisibility && !mAttachedHidden - && (atoken == null || !atoken.hiddenRequested || atoken.animating) - && !mExiting && !mDestroying; - } - - /** - * The same as isVisible(), but follows the current hidden state of - * the associated app token, not the pending requested hidden state. - */ - boolean isVisibleNow() { - return mSurface != null && mPolicyVisibility && !mAttachedHidden - && !mRootToken.hidden && !mExiting && !mDestroying; - } - - /** - * Can this window possibly be a drag/drop target? The test here is - * a combination of the above "visible now" with the check that the - * Input Manager uses when discarding windows from input consideration. - */ - boolean isPotentialDragTarget() { - return isVisibleNow() && (mInputChannel != null) && !mRemoved; - } - - /** - * Same as isVisible(), but we also count it as visible between the - * call to IWindowSession.add() and the first relayout(). - */ - boolean isVisibleOrAdding() { - final AppWindowToken atoken = mAppToken; - return ((mSurface != null && !mReportDestroySurface) - || (!mRelayoutCalled && mViewVisibility == View.VISIBLE)) - && mPolicyVisibility && !mAttachedHidden - && (atoken == null || !atoken.hiddenRequested) - && !mExiting && !mDestroying; - } - - /** - * Is this window currently on-screen? It is on-screen either if it - * is visible or it is currently running an animation before no longer - * being visible. - */ - boolean isOnScreen() { - final AppWindowToken atoken = mAppToken; - if (atoken != null) { - return mSurface != null && mPolicyVisibility && !mDestroying - && ((!mAttachedHidden && !atoken.hiddenRequested) - || mAnimation != null || atoken.animation != null); - } else { - return mSurface != null && mPolicyVisibility && !mDestroying - && (!mAttachedHidden || mAnimation != null); - } - } - - /** - * Like isOnScreen(), but we don't return true if the window is part - * of a transition that has not yet been started. - */ - boolean isReadyForDisplay() { - if (mRootToken.waitingToShow && - mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { - return false; - } - final AppWindowToken atoken = mAppToken; - final boolean animating = atoken != null - ? (atoken.animation != null) : false; - return mSurface != null && mPolicyVisibility && !mDestroying - && ((!mAttachedHidden && mViewVisibility == View.VISIBLE - && !mRootToken.hidden) - || mAnimation != null || animating); - } - - /** Is the window or its container currently animating? */ - boolean isAnimating() { - final WindowState attached = mAttachedWindow; - final AppWindowToken atoken = mAppToken; - return mAnimation != null - || (attached != null && attached.mAnimation != null) - || (atoken != null && - (atoken.animation != null - || atoken.inPendingTransaction)); - } - - /** Is this window currently animating? */ - boolean isWindowAnimating() { - return mAnimation != null; - } - - /** - * Like isOnScreen, but returns false if the surface hasn't yet - * been drawn. - */ - public boolean isDisplayedLw() { - final AppWindowToken atoken = mAppToken; - return mSurface != null && mPolicyVisibility && !mDestroying - && !mDrawPending && !mCommitDrawPending - && ((!mAttachedHidden && - (atoken == null || !atoken.hiddenRequested)) - || mAnimating); - } - - /** - * Returns true if the window has a surface that it has drawn a - * complete UI in to. - */ - public boolean isDrawnLw() { - final AppWindowToken atoken = mAppToken; - return mSurface != null && !mDestroying - && !mDrawPending && !mCommitDrawPending; - } - - /** - * Return true if the window is opaque and fully drawn. This indicates - * it may obscure windows behind it. - */ - boolean isOpaqueDrawn() { - return (mAttrs.format == PixelFormat.OPAQUE - || mAttrs.type == TYPE_WALLPAPER) - && mSurface != null && mAnimation == null - && (mAppToken == null || mAppToken.animation == null) - && !mDrawPending && !mCommitDrawPending; - } - - /** - * Return whether this window is wanting to have a translation - * animation applied to it for an in-progress move. (Only makes - * sense to call from performLayoutAndPlaceSurfacesLockedInner().) - */ - boolean shouldAnimateMove() { - return mContentChanged && !mExiting && !mLastHidden && !mDisplayFrozen - && (mFrame.top != mLastFrame.top - || mFrame.left != mLastFrame.left) - && (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove()) - && mPolicy.isScreenOn(); - } - - boolean needsBackgroundFiller(int screenWidth, int screenHeight) { - return - // only if the application is requesting compatible window - (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0 && - // only if it's visible - mHasDrawn && mViewVisibility == View.VISIBLE && - // and only if the application fills the compatible screen - mFrame.left <= mCompatibleScreenFrame.left && - mFrame.top <= mCompatibleScreenFrame.top && - mFrame.right >= mCompatibleScreenFrame.right && - mFrame.bottom >= mCompatibleScreenFrame.bottom; - } - - boolean isFullscreen(int screenWidth, int screenHeight) { - return mFrame.left <= 0 && mFrame.top <= 0 && - mFrame.right >= screenWidth && mFrame.bottom >= screenHeight; - } - - void removeLocked() { - disposeInputChannel(); - - if (mAttachedWindow != null) { - if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Removing " + this + " from " + mAttachedWindow); - mAttachedWindow.mChildWindows.remove(this); - } - destroySurfaceLocked(); - mSession.windowRemovedLocked(); - try { - mClient.asBinder().unlinkToDeath(mDeathRecipient, 0); - } catch (RuntimeException e) { - // Ignore if it has already been removed (usually because - // we are doing this as part of processing a death note.) - } - } - - void disposeInputChannel() { - if (mInputChannel != null) { - mInputManager.unregisterInputChannel(mInputChannel); - - mInputChannel.dispose(); - mInputChannel = null; - } - } - - private class DeathRecipient implements IBinder.DeathRecipient { - public void binderDied() { - try { - synchronized(mWindowMap) { - WindowState win = windowForClientLocked(mSession, mClient, false); - Slog.i(TAG, "WIN DEATH: " + win); - if (win != null) { - removeWindowLocked(mSession, win); - } - } - } catch (IllegalArgumentException ex) { - // This will happen if the window has already been - // removed. - } - } - } - - /** Returns true if this window desires key events. */ - public final boolean canReceiveKeys() { - return isVisibleOrAdding() - && (mViewVisibility == View.VISIBLE) - && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0); - } - - public boolean hasDrawnLw() { - return mHasDrawn; - } - - public boolean showLw(boolean doAnimation) { - return showLw(doAnimation, true); - } - - boolean showLw(boolean doAnimation, boolean requestAnim) { - if (mPolicyVisibility && mPolicyVisibilityAfterAnim) { - return false; - } - if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this); - if (doAnimation) { - if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility=" - + mPolicyVisibility + " mAnimation=" + mAnimation); - if (mDisplayFrozen || !mPolicy.isScreenOn()) { - doAnimation = false; - } else if (mPolicyVisibility && mAnimation == null) { - // Check for the case where we are currently visible and - // not animating; we do not want to do animation at such a - // point to become visible when we already are. - doAnimation = false; - } - } - mPolicyVisibility = true; - mPolicyVisibilityAfterAnim = true; - if (doAnimation) { - applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true); - } - if (requestAnim) { - requestAnimationLocked(0); - } - return true; - } - - public boolean hideLw(boolean doAnimation) { - return hideLw(doAnimation, true); - } - - boolean hideLw(boolean doAnimation, boolean requestAnim) { - if (doAnimation) { - if (mDisplayFrozen || !mPolicy.isScreenOn()) { - doAnimation = false; - } - } - boolean current = doAnimation ? mPolicyVisibilityAfterAnim - : mPolicyVisibility; - if (!current) { - return false; - } - if (doAnimation) { - applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false); - if (mAnimation == null) { - doAnimation = false; - } - } - if (doAnimation) { - mPolicyVisibilityAfterAnim = false; - } else { - if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this); - mPolicyVisibilityAfterAnim = false; - mPolicyVisibility = false; - // Window is no longer visible -- make sure if we were waiting - // for it to be displayed before enabling the display, that - // we allow the display to be enabled now. - enableScreenIfNeededLocked(); - if (mCurrentFocus == this) { - mFocusMayChange = true; - } - } - if (requestAnim) { - requestAnimationLocked(0); - } - return true; - } - - public void getTouchableRegion(Region outRegion) { - final Rect frame = mFrame; - switch (mTouchableInsets) { - default: - case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME: - outRegion.set(frame); - break; - case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: { - final Rect inset = mGivenContentInsets; - outRegion.set( - frame.left + inset.left, frame.top + inset.top, - frame.right - inset.right, frame.bottom - inset.bottom); - break; - } - case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: { - final Rect inset = mGivenVisibleInsets; - outRegion.set( - frame.left + inset.left, frame.top + inset.top, - frame.right - inset.right, frame.bottom - inset.bottom); - break; - } - case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: { - final Region givenTouchableRegion = mGivenTouchableRegion; - outRegion.set(givenTouchableRegion); - outRegion.translate(frame.left, frame.top); - break; - } - } - } - - void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("mSession="); pw.print(mSession); - pw.print(" mClient="); pw.println(mClient.asBinder()); - pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs); - if (mAttachedWindow != null || mLayoutAttached) { - pw.print(prefix); pw.print("mAttachedWindow="); pw.print(mAttachedWindow); - pw.print(" mLayoutAttached="); pw.println(mLayoutAttached); - } - if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) { - pw.print(prefix); pw.print("mIsImWindow="); pw.print(mIsImWindow); - pw.print(" mIsWallpaper="); pw.print(mIsWallpaper); - pw.print(" mIsFloatingLayer="); pw.print(mIsFloatingLayer); - pw.print(" mWallpaperVisible="); pw.println(mWallpaperVisible); - } - pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer); - pw.print(" mSubLayer="); pw.print(mSubLayer); - pw.print(" mAnimLayer="); pw.print(mLayer); pw.print("+"); - pw.print((mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment - : (mAppToken != null ? mAppToken.animLayerAdjustment : 0))); - pw.print("="); pw.print(mAnimLayer); - pw.print(" mLastLayer="); pw.println(mLastLayer); - if (mSurface != null) { - pw.print(prefix); pw.print("mSurface="); pw.println(mSurface); - pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown); - pw.print(" layer="); pw.print(mSurfaceLayer); - pw.print(" alpha="); pw.print(mSurfaceAlpha); - pw.print(" rect=("); pw.print(mSurfaceX); - pw.print(","); pw.print(mSurfaceY); - pw.print(") "); pw.print(mSurfaceW); - pw.print(" x "); pw.println(mSurfaceH); - } - pw.print(prefix); pw.print("mToken="); pw.println(mToken); - pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken); - if (mAppToken != null) { - pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken); - } - if (mTargetAppToken != null) { - pw.print(prefix); pw.print("mTargetAppToken="); pw.println(mTargetAppToken); - } - pw.print(prefix); pw.print("mViewVisibility=0x"); - pw.print(Integer.toHexString(mViewVisibility)); - pw.print(" mLastHidden="); pw.print(mLastHidden); - pw.print(" mHaveFrame="); pw.print(mHaveFrame); - pw.print(" mObscured="); pw.println(mObscured); - if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || mAttachedHidden) { - pw.print(prefix); pw.print("mPolicyVisibility="); - pw.print(mPolicyVisibility); - pw.print(" mPolicyVisibilityAfterAnim="); - pw.print(mPolicyVisibilityAfterAnim); - pw.print(" mAttachedHidden="); pw.println(mAttachedHidden); - } - if (!mRelayoutCalled) { - pw.print(prefix); pw.print("mRelayoutCalled="); pw.println(mRelayoutCalled); - } - pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth); - pw.print(" h="); pw.print(mRequestedHeight); - pw.print(" mLayoutSeq="); pw.println(mLayoutSeq); - if (mXOffset != 0 || mYOffset != 0) { - pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset); - pw.print(" y="); pw.println(mYOffset); - } - pw.print(prefix); pw.print("mGivenContentInsets="); - mGivenContentInsets.printShortString(pw); - pw.print(" mGivenVisibleInsets="); - mGivenVisibleInsets.printShortString(pw); - pw.println(); - if (mTouchableInsets != 0 || mGivenInsetsPending) { - pw.print(prefix); pw.print("mTouchableInsets="); pw.print(mTouchableInsets); - pw.print(" mGivenInsetsPending="); pw.println(mGivenInsetsPending); - } - pw.print(prefix); pw.print("mConfiguration="); pw.println(mConfiguration); - pw.print(prefix); pw.print("mShownFrame="); - mShownFrame.printShortString(pw); - pw.print(" last="); mLastShownFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw); - pw.print(" last="); mLastFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print("mContainingFrame="); - mContainingFrame.printShortString(pw); - pw.print(" mParentFrame="); - mParentFrame.printShortString(pw); - pw.print(" mDisplayFrame="); - mDisplayFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print("mContentFrame="); mContentFrame.printShortString(pw); - pw.print(" mVisibleFrame="); mVisibleFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print("mContentInsets="); mContentInsets.printShortString(pw); - pw.print(" last="); mLastContentInsets.printShortString(pw); - pw.print(" mVisibleInsets="); mVisibleInsets.printShortString(pw); - pw.print(" last="); mLastVisibleInsets.printShortString(pw); - pw.println(); - if (mAnimating || mLocalAnimating || mAnimationIsEntrance - || mAnimation != null) { - pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating); - pw.print(" mLocalAnimating="); pw.print(mLocalAnimating); - pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance); - pw.print(" mAnimation="); pw.println(mAnimation); - } - if (mHasTransformation || mHasLocalTransformation) { - pw.print(prefix); pw.print("XForm: has="); - pw.print(mHasTransformation); - pw.print(" hasLocal="); pw.print(mHasLocalTransformation); - pw.print(" "); mTransformation.printShortString(pw); - pw.println(); - } - if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) { - pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha); - pw.print(" mAlpha="); pw.print(mAlpha); - pw.print(" mLastAlpha="); pw.println(mLastAlpha); - } - if (mHaveMatrix) { - pw.print(prefix); pw.print("mDsDx="); pw.print(mDsDx); - pw.print(" mDtDx="); pw.print(mDtDx); - pw.print(" mDsDy="); pw.print(mDsDy); - pw.print(" mDtDy="); pw.println(mDtDy); - } - pw.print(prefix); pw.print("mDrawPending="); pw.print(mDrawPending); - pw.print(" mCommitDrawPending="); pw.print(mCommitDrawPending); - pw.print(" mReadyToShow="); pw.print(mReadyToShow); - pw.print(" mHasDrawn="); pw.println(mHasDrawn); - if (mExiting || mRemoveOnExit || mDestroying || mRemoved) { - pw.print(prefix); pw.print("mExiting="); pw.print(mExiting); - pw.print(" mRemoveOnExit="); pw.print(mRemoveOnExit); - pw.print(" mDestroying="); pw.print(mDestroying); - pw.print(" mRemoved="); pw.println(mRemoved); - } - if (mOrientationChanging || mAppFreezing || mTurnOnScreen) { - pw.print(prefix); pw.print("mOrientationChanging="); - pw.print(mOrientationChanging); - pw.print(" mAppFreezing="); pw.print(mAppFreezing); - pw.print(" mTurnOnScreen="); pw.println(mTurnOnScreen); - } - if (mHScale != 1 || mVScale != 1) { - pw.print(prefix); pw.print("mHScale="); pw.print(mHScale); - pw.print(" mVScale="); pw.println(mVScale); - } - if (mWallpaperX != -1 || mWallpaperY != -1) { - pw.print(prefix); pw.print("mWallpaperX="); pw.print(mWallpaperX); - pw.print(" mWallpaperY="); pw.println(mWallpaperY); - } - if (mWallpaperXStep != -1 || mWallpaperYStep != -1) { - pw.print(prefix); pw.print("mWallpaperXStep="); pw.print(mWallpaperXStep); - pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep); - } - } - - String makeInputChannelName() { - return Integer.toHexString(System.identityHashCode(this)) - + " " + mAttrs.getTitle(); - } - - @Override - public String toString() { - if (mStringNameCache == null || mLastTitle != mAttrs.getTitle() - || mWasPaused != mToken.paused) { - mLastTitle = mAttrs.getTitle(); - mWasPaused = mToken.paused; - mStringNameCache = "Window{" + Integer.toHexString(System.identityHashCode(this)) - + " " + mLastTitle + " paused=" + mWasPaused + "}"; - } - return mStringNameCache; - } - } - - // ------------------------------------------------------------- - // Window Token State - // ------------------------------------------------------------- - - class WindowToken { - // The actual token. - final IBinder token; - - // The type of window this token is for, as per WindowManager.LayoutParams. - final int windowType; - - // Set if this token was explicitly added by a client, so should - // not be removed when all windows are removed. - final boolean explicit; - - // For printing. - String stringName; - - // If this is an AppWindowToken, this is non-null. - AppWindowToken appWindowToken; - - // All of the windows associated with this token. - final ArrayList<WindowState> windows = new ArrayList<WindowState>(); - - // Is key dispatching paused for this token? - boolean paused = false; - - // Should this token's windows be hidden? - boolean hidden; - - // Temporary for finding which tokens no longer have visible windows. - boolean hasVisible; - - // Set to true when this token is in a pending transaction where it - // will be shown. - boolean waitingToShow; - - // Set to true when this token is in a pending transaction where it - // will be hidden. - boolean waitingToHide; - - // Set to true when this token is in a pending transaction where its - // windows will be put to the bottom of the list. - boolean sendingToBottom; - - // Set to true when this token is in a pending transaction where its - // windows will be put to the top of the list. - boolean sendingToTop; - - WindowToken(IBinder _token, int type, boolean _explicit) { - token = _token; - windowType = type; - explicit = _explicit; - } - - void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("token="); pw.println(token); - pw.print(prefix); pw.print("windows="); pw.println(windows); - pw.print(prefix); pw.print("windowType="); pw.print(windowType); - pw.print(" hidden="); pw.print(hidden); - pw.print(" hasVisible="); pw.println(hasVisible); - if (waitingToShow || waitingToHide || sendingToBottom || sendingToTop) { - pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow); - pw.print(" waitingToHide="); pw.print(waitingToHide); - pw.print(" sendingToBottom="); pw.print(sendingToBottom); - pw.print(" sendingToTop="); pw.println(sendingToTop); - } - } - - @Override - public String toString() { - if (stringName == null) { - StringBuilder sb = new StringBuilder(); - sb.append("WindowToken{"); - sb.append(Integer.toHexString(System.identityHashCode(this))); - sb.append(" token="); sb.append(token); sb.append('}'); - stringName = sb.toString(); - } - return stringName; - } - }; - - class AppWindowToken extends WindowToken { - // Non-null only for application tokens. - final IApplicationToken appToken; - - // All of the windows and child windows that are included in this - // application token. Note this list is NOT sorted! - final ArrayList<WindowState> allAppWindows = new ArrayList<WindowState>(); - - int groupId = -1; - boolean appFullscreen; - int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - - // The input dispatching timeout for this application token in nanoseconds. - long inputDispatchingTimeoutNanos; - - // These are used for determining when all windows associated with - // an activity have been drawn, so they can be made visible together - // at the same time. - int lastTransactionSequence = mTransactionSequence-1; - int numInterestingWindows; - int numDrawnWindows; - boolean inPendingTransaction; - boolean allDrawn; - - // Is this token going to be hidden in a little while? If so, it - // won't be taken into account for setting the screen orientation. - boolean willBeHidden; - - // Is this window's surface needed? This is almost like hidden, except - // it will sometimes be true a little earlier: when the token has - // been shown, but is still waiting for its app transition to execute - // before making its windows shown. - boolean hiddenRequested; - - // Have we told the window clients to hide themselves? - boolean clientHidden; - - // Last visibility state we reported to the app token. - boolean reportedVisible; - - // Set to true when the token has been removed from the window mgr. - boolean removed; - - // Have we been asked to have this token keep the screen frozen? - boolean freezingScreen; - - boolean animating; - Animation animation; - boolean hasTransformation; - final Transformation transformation = new Transformation(); - - // Offset to the window of all layers in the token, for use by - // AppWindowToken animations. - int animLayerAdjustment; - - // Information about an application starting window if displayed. - StartingData startingData; - WindowState startingWindow; - View startingView; - boolean startingDisplayed; - boolean startingMoved; - boolean firstWindowDrawn; - - // Input application handle used by the input dispatcher. - InputApplicationHandle mInputApplicationHandle; - - AppWindowToken(IApplicationToken _token) { - super(_token.asBinder(), - WindowManager.LayoutParams.TYPE_APPLICATION, true); - appWindowToken = this; - appToken = _token; - mInputApplicationHandle = new InputApplicationHandle(this); - } - - public void setAnimation(Animation anim) { - if (localLOGV) Slog.v( - TAG, "Setting animation in " + this + ": " + anim); - animation = anim; - animating = false; - anim.restrictDuration(MAX_ANIMATION_DURATION); - anim.scaleCurrentDuration(mTransitionAnimationScale); - int zorder = anim.getZAdjustment(); - int adj = 0; - if (zorder == Animation.ZORDER_TOP) { - adj = TYPE_LAYER_OFFSET; - } else if (zorder == Animation.ZORDER_BOTTOM) { - adj = -TYPE_LAYER_OFFSET; - } - - if (animLayerAdjustment != adj) { - animLayerAdjustment = adj; - updateLayers(); - } - } - - public void setDummyAnimation() { - if (animation == null) { - if (localLOGV) Slog.v( - TAG, "Setting dummy animation in " + this); - animation = sDummyAnimation; - } - } - - public void clearAnimation() { - if (animation != null) { - animation = null; - animating = true; - } - } - - void updateLayers() { - final int N = allAppWindows.size(); - final int adj = animLayerAdjustment; - for (int i=0; i<N; i++) { - WindowState w = allAppWindows.get(i); - w.mAnimLayer = w.mLayer + adj; - if (DEBUG_LAYERS) Slog.v(TAG, "Updating layer " + w + ": " - + w.mAnimLayer); - if (w == mInputMethodTarget && !mInputMethodTargetWaitingAnim) { - setInputMethodAnimLayerAdjustment(adj); - } - if (w == mWallpaperTarget && mLowerWallpaperTarget == null) { - setWallpaperAnimLayerAdjustmentLocked(adj); - } - } - } - - void sendAppVisibilityToClients() { - final int N = allAppWindows.size(); - for (int i=0; i<N; i++) { - WindowState win = allAppWindows.get(i); - if (win == startingWindow && clientHidden) { - // Don't hide the starting window. - continue; - } - try { - if (DEBUG_VISIBILITY) Slog.v(TAG, - "Setting visibility of " + win + ": " + (!clientHidden)); - win.mClient.dispatchAppVisibility(!clientHidden); - } catch (RemoteException e) { - } - } - } - - void showAllWindowsLocked() { - final int NW = allAppWindows.size(); - for (int i=0; i<NW; i++) { - WindowState w = allAppWindows.get(i); - if (DEBUG_VISIBILITY) Slog.v(TAG, - "performing show on: " + w); - w.performShowLocked(); - } - } - - // This must be called while inside a transaction. - boolean stepAnimationLocked(long currentTime, int dw, int dh) { - if (!mDisplayFrozen && mPolicy.isScreenOn()) { - // We will run animations as long as the display isn't frozen. - - if (animation == sDummyAnimation) { - // This guy is going to animate, but not yet. For now count - // it as not animating for purposes of scheduling transactions; - // when it is really time to animate, this will be set to - // a real animation and the next call will execute normally. - return false; - } - - if ((allDrawn || animating || startingDisplayed) && animation != null) { - if (!animating) { - if (DEBUG_ANIM) Slog.v( - TAG, "Starting animation in " + this + - " @ " + currentTime + ": dw=" + dw + " dh=" + dh - + " scale=" + mTransitionAnimationScale - + " allDrawn=" + allDrawn + " animating=" + animating); - animation.initialize(dw, dh, dw, dh); - animation.setStartTime(currentTime); - animating = true; - } - transformation.clear(); - final boolean more = animation.getTransformation( - currentTime, transformation); - if (DEBUG_ANIM) Slog.v( - TAG, "Stepped animation in " + this + - ": more=" + more + ", xform=" + transformation); - if (more) { - // we're done! - hasTransformation = true; - return true; - } - if (DEBUG_ANIM) Slog.v( - TAG, "Finished animation in " + this + - " @ " + currentTime); - animation = null; - } - } else if (animation != null) { - // If the display is frozen, and there is a pending animation, - // clear it and make sure we run the cleanup code. - animating = true; - animation = null; - } - - hasTransformation = false; - - if (!animating) { - return false; - } - - clearAnimation(); - animating = false; - if (animLayerAdjustment != 0) { - animLayerAdjustment = 0; - updateLayers(); - } - if (mInputMethodTarget != null && mInputMethodTarget.mAppToken == this) { - moveInputMethodWindowsIfNeededLocked(true); - } - - if (DEBUG_ANIM) Slog.v( - TAG, "Animation done in " + this - + ": reportedVisible=" + reportedVisible); - - transformation.clear(); - - final int N = windows.size(); - for (int i=0; i<N; i++) { - windows.get(i).finishExit(); - } - updateReportedVisibilityLocked(); - - return false; - } - - void updateReportedVisibilityLocked() { - if (appToken == null) { - return; - } - - int numInteresting = 0; - int numVisible = 0; - boolean nowGone = true; - - if (DEBUG_VISIBILITY) Slog.v(TAG, "Update reported visibility: " + this); - final int N = allAppWindows.size(); - for (int i=0; i<N; i++) { - WindowState win = allAppWindows.get(i); - if (win == startingWindow || win.mAppFreezing - || win.mViewVisibility != View.VISIBLE - || win.mAttrs.type == TYPE_APPLICATION_STARTING - || win.mDestroying) { - continue; - } - if (DEBUG_VISIBILITY) { - Slog.v(TAG, "Win " + win + ": isDrawn=" - + win.isDrawnLw() - + ", isAnimating=" + win.isAnimating()); - if (!win.isDrawnLw()) { - Slog.v(TAG, "Not displayed: s=" + win.mSurface - + " pv=" + win.mPolicyVisibility - + " dp=" + win.mDrawPending - + " cdp=" + win.mCommitDrawPending - + " ah=" + win.mAttachedHidden - + " th=" - + (win.mAppToken != null - ? win.mAppToken.hiddenRequested : false) - + " a=" + win.mAnimating); - } - } - numInteresting++; - if (win.isDrawnLw()) { - if (!win.isAnimating()) { - numVisible++; - } - nowGone = false; - } else if (win.isAnimating()) { - nowGone = false; - } - } - - boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting; - if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting=" - + numInteresting + " visible=" + numVisible); - if (nowVisible != reportedVisible) { - if (DEBUG_VISIBILITY) Slog.v( - TAG, "Visibility changed in " + this - + ": vis=" + nowVisible); - reportedVisible = nowVisible; - Message m = mH.obtainMessage( - H.REPORT_APPLICATION_TOKEN_WINDOWS, - nowVisible ? 1 : 0, - nowGone ? 1 : 0, - this); - mH.sendMessage(m); - } - } - - WindowState findMainWindow() { - int j = windows.size(); - while (j > 0) { - j--; - WindowState win = windows.get(j); - if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION - || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) { - return win; - } - } - return null; - } - - void dump(PrintWriter pw, String prefix) { - super.dump(pw, prefix); - if (appToken != null) { - pw.print(prefix); pw.println("app=true"); - } - if (allAppWindows.size() > 0) { - pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows); - } - pw.print(prefix); pw.print("groupId="); pw.print(groupId); - pw.print(" appFullscreen="); pw.print(appFullscreen); - pw.print(" requestedOrientation="); pw.println(requestedOrientation); - pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested); - pw.print(" clientHidden="); pw.print(clientHidden); - pw.print(" willBeHidden="); pw.print(willBeHidden); - pw.print(" reportedVisible="); pw.println(reportedVisible); - if (paused || freezingScreen) { - pw.print(prefix); pw.print("paused="); pw.print(paused); - pw.print(" freezingScreen="); pw.println(freezingScreen); - } - if (numInterestingWindows != 0 || numDrawnWindows != 0 - || inPendingTransaction || allDrawn) { - pw.print(prefix); pw.print("numInterestingWindows="); - pw.print(numInterestingWindows); - pw.print(" numDrawnWindows="); pw.print(numDrawnWindows); - pw.print(" inPendingTransaction="); pw.print(inPendingTransaction); - pw.print(" allDrawn="); pw.println(allDrawn); - } - if (animating || animation != null) { - pw.print(prefix); pw.print("animating="); pw.print(animating); - pw.print(" animation="); pw.println(animation); - } - if (hasTransformation) { - pw.print(prefix); pw.print("XForm: "); - transformation.printShortString(pw); - pw.println(); - } - if (animLayerAdjustment != 0) { - pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment); - } - if (startingData != null || removed || firstWindowDrawn) { - pw.print(prefix); pw.print("startingData="); pw.print(startingData); - pw.print(" removed="); pw.print(removed); - pw.print(" firstWindowDrawn="); pw.println(firstWindowDrawn); - } - if (startingWindow != null || startingView != null - || startingDisplayed || startingMoved) { - pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow); - pw.print(" startingView="); pw.print(startingView); - pw.print(" startingDisplayed="); pw.print(startingDisplayed); - pw.print(" startingMoved"); pw.println(startingMoved); - } - } - - @Override - public String toString() { - if (stringName == null) { - StringBuilder sb = new StringBuilder(); - sb.append("AppWindowToken{"); - sb.append(Integer.toHexString(System.identityHashCode(this))); - sb.append(" token="); sb.append(token); sb.append('}'); - stringName = sb.toString(); - } - return stringName; - } - } - - // ------------------------------------------------------------- - // DummyAnimation - // ------------------------------------------------------------- - // This is an animation that does nothing: it just immediately finishes // itself every time it is called. It is used as a stub animation in cases // where we want to synchronize multiple things that may be animating. @@ -8908,26 +5826,7 @@ public class WindowManagerService extends IWindowManager.Stub // Async Handler // ------------------------------------------------------------- - static final class StartingData { - final String pkg; - final int theme; - final CharSequence nonLocalizedLabel; - final int labelRes; - final int icon; - final int windowFlags; - - StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel, - int _labelRes, int _icon, int _windowFlags) { - pkg = _pkg; - theme = _theme; - nonLocalizedLabel = _nonLocalizedLabel; - labelRes = _labelRes; - icon = _icon; - windowFlags = _windowFlags; - } - } - - private final class H extends Handler { + final class H extends Handler { public static final int REPORT_FOCUS_CHANGE = 2; public static final int REPORT_LOSING_FOCUS = 3; public static final int ANIMATE = 4; @@ -9337,7 +6236,7 @@ public class WindowManagerService extends IWindowManager.Stub IInputContext inputContext) { if (client == null) throw new IllegalArgumentException("null client"); if (inputContext == null) throw new IllegalArgumentException("null inputContext"); - Session session = new Session(client, inputContext); + Session session = new Session(this, client, inputContext); return session; } @@ -11471,148 +8370,6 @@ public class WindowManagerService extends IWindowManager.Stub return val; } - static class Watermark { - final String[] mTokens; - final String mText; - final Paint mTextPaint; - final int mTextWidth; - final int mTextHeight; - final int mTextAscent; - final int mTextDescent; - final int mDeltaX; - final int mDeltaY; - - Surface mSurface; - int mLastDW; - int mLastDH; - boolean mDrawNeeded; - - Watermark(Display display, SurfaceSession session, String[] tokens) { - final DisplayMetrics dm = new DisplayMetrics(); - display.getMetrics(dm); - - if (false) { - Log.i(TAG, "*********************** WATERMARK"); - for (int i=0; i<tokens.length; i++) { - Log.i(TAG, " TOKEN #" + i + ": " + tokens[i]); - } - } - - mTokens = tokens; - - StringBuilder builder = new StringBuilder(32); - int len = mTokens[0].length(); - len = len & ~1; - for (int i=0; i<len; i+=2) { - int c1 = mTokens[0].charAt(i); - int c2 = mTokens[0].charAt(i+1); - if (c1 >= 'a' && c1 <= 'f') c1 = c1 - 'a' + 10; - else if (c1 >= 'A' && c1 <= 'F') c1 = c1 - 'A' + 10; - else c1 -= '0'; - if (c2 >= 'a' && c2 <= 'f') c2 = c2 - 'a' + 10; - else if (c2 >= 'A' && c2 <= 'F') c2 = c2 - 'A' + 10; - else c2 -= '0'; - builder.append((char)(255-((c1*16)+c2))); - } - mText = builder.toString(); - if (false) { - Log.i(TAG, "Final text: " + mText); - } - - int fontSize = getPropertyInt(tokens, 1, - TypedValue.COMPLEX_UNIT_DIP, 20, dm); - - mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mTextPaint.setTextSize(fontSize); - mTextPaint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD)); - - FontMetricsInt fm = mTextPaint.getFontMetricsInt(); - mTextWidth = (int)mTextPaint.measureText(mText); - mTextAscent = fm.ascent; - mTextDescent = fm.descent; - mTextHeight = fm.descent - fm.ascent; - - mDeltaX = getPropertyInt(tokens, 2, - TypedValue.COMPLEX_UNIT_PX, mTextWidth*2, dm); - mDeltaY = getPropertyInt(tokens, 3, - TypedValue.COMPLEX_UNIT_PX, mTextHeight*3, dm); - int shadowColor = getPropertyInt(tokens, 4, - TypedValue.COMPLEX_UNIT_PX, 0xb0000000, dm); - int color = getPropertyInt(tokens, 5, - TypedValue.COMPLEX_UNIT_PX, 0x60ffffff, dm); - int shadowRadius = getPropertyInt(tokens, 6, - TypedValue.COMPLEX_UNIT_PX, 7, dm); - int shadowDx = getPropertyInt(tokens, 8, - TypedValue.COMPLEX_UNIT_PX, 0, dm); - int shadowDy = getPropertyInt(tokens, 9, - TypedValue.COMPLEX_UNIT_PX, 0, dm); - - mTextPaint.setColor(color); - mTextPaint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor); - - try { - mSurface = new Surface(session, 0, - "WatermarkSurface", -1, 1, 1, PixelFormat.TRANSLUCENT, 0); - mSurface.setLayer(TYPE_LAYER_MULTIPLIER*100); - mSurface.setPosition(0, 0); - mSurface.show(); - } catch (OutOfResourcesException e) { - } - } - - void positionSurface(int dw, int dh) { - if (mLastDW != dw || mLastDH != dh) { - mLastDW = dw; - mLastDH = dh; - mSurface.setSize(dw, dh); - mDrawNeeded = true; - } - } - - void drawIfNeeded() { - if (mDrawNeeded) { - final int dw = mLastDW; - final int dh = mLastDH; - - mDrawNeeded = false; - Rect dirty = new Rect(0, 0, dw, dh); - Canvas c = null; - try { - c = mSurface.lockCanvas(dirty); - } catch (IllegalArgumentException e) { - } catch (OutOfResourcesException e) { - } - if (c != null) { - c.drawColor(0, PorterDuff.Mode.CLEAR); - - int deltaX = mDeltaX; - int deltaY = mDeltaY; - - // deltaX shouldn't be close to a round fraction of our - // x step, or else things will line up too much. - int div = (dw+mTextWidth)/deltaX; - int rem = (dw+mTextWidth) - (div*deltaX); - int qdelta = deltaX/4; - if (rem < qdelta || rem > (deltaX-qdelta)) { - deltaX += deltaX/3; - } - - int y = -mTextHeight; - int x = -mTextWidth; - while (y < (dh+mTextHeight)) { - c.drawText(mText, x, y, mTextPaint); - x += deltaX; - if (x >= dw) { - x -= (dw+mTextWidth); - y += deltaY; - } - } - mSurface.unlockCanvasAndPost(c); - } - } - } - } - void createWatermark() { if (mWatermark != null) { return; @@ -11894,190 +8651,6 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mKeyguardTokenWatcher) { } } - /** - * DimAnimator class that controls the dim animation. This holds the surface and - * all state used for dim animation. - */ - private static class DimAnimator { - Surface mDimSurface; - boolean mDimShown = false; - float mDimCurrentAlpha; - float mDimTargetAlpha; - float mDimDeltaPerMs; - long mLastDimAnimTime; - - int mLastDimWidth, mLastDimHeight; - - DimAnimator (SurfaceSession session) { - if (mDimSurface == null) { - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " - + mDimSurface + ": CREATE"); - try { - mDimSurface = new Surface(session, 0, - "DimSurface", - -1, 16, 16, PixelFormat.OPAQUE, - Surface.FX_SURFACE_DIM); - mDimSurface.setAlpha(0.0f); - } catch (Exception e) { - Slog.e(TAG, "Exception creating Dim surface", e); - } - } - } - - /** - * Show the dim surface. - */ - void show(int dw, int dh) { - if (!mDimShown) { - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" + - dw + "x" + dh + ")"); - mDimShown = true; - try { - mLastDimWidth = dw; - mLastDimHeight = dh; - mDimSurface.setPosition(0, 0); - mDimSurface.setSize(dw, dh); - mDimSurface.show(); - } catch (RuntimeException e) { - Slog.w(TAG, "Failure showing dim surface", e); - } - } else if (mLastDimWidth != dw || mLastDimHeight != dh) { - mLastDimWidth = dw; - mLastDimHeight = dh; - mDimSurface.setSize(dw, dh); - } - } - - /** - * Set's the dim surface's layer and update dim parameters that will be used in - * {@link updateSurface} after all windows are examined. - */ - void updateParameters(Resources res, WindowState w, long currentTime) { - mDimSurface.setLayer(w.mAnimLayer-1); - - final float target = w.mExiting ? 0 : w.mAttrs.dimAmount; - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface - + ": layer=" + (w.mAnimLayer-1) + " target=" + target); - if (mDimTargetAlpha != target) { - // If the desired dim level has changed, then - // start an animation to it. - mLastDimAnimTime = currentTime; - long duration = (w.mAnimating && w.mAnimation != null) - ? w.mAnimation.computeDurationHint() - : DEFAULT_DIM_DURATION; - if (target > mDimTargetAlpha) { - TypedValue tv = new TypedValue(); - res.getValue(com.android.internal.R.fraction.config_dimBehindFadeDuration, - tv, true); - if (tv.type == TypedValue.TYPE_FRACTION) { - duration = (long)tv.getFraction((float)duration, (float)duration); - } else if (tv.type >= TypedValue.TYPE_FIRST_INT - && tv.type <= TypedValue.TYPE_LAST_INT) { - duration = tv.data; - } - } - if (duration < 1) { - // Don't divide by zero - duration = 1; - } - mDimTargetAlpha = target; - mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha) / duration; - } - } - - /** - * Updating the surface's alpha. Returns true if the animation continues, or returns - * false when the animation is finished and the dim surface is hidden. - */ - boolean updateSurface(boolean dimming, long currentTime, boolean displayFrozen) { - if (!dimming) { - if (mDimTargetAlpha != 0) { - mLastDimAnimTime = currentTime; - mDimTargetAlpha = 0; - mDimDeltaPerMs = (-mDimCurrentAlpha) / DEFAULT_DIM_DURATION; - } - } - - boolean animating = false; - if (mLastDimAnimTime != 0) { - mDimCurrentAlpha += mDimDeltaPerMs - * (currentTime-mLastDimAnimTime); - boolean more = true; - if (displayFrozen) { - // If the display is frozen, there is no reason to animate. - more = false; - } else if (mDimDeltaPerMs > 0) { - if (mDimCurrentAlpha > mDimTargetAlpha) { - more = false; - } - } else if (mDimDeltaPerMs < 0) { - if (mDimCurrentAlpha < mDimTargetAlpha) { - more = false; - } - } else { - more = false; - } - - // Do we need to continue animating? - if (more) { - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " - + mDimSurface + ": alpha=" + mDimCurrentAlpha); - mLastDimAnimTime = currentTime; - mDimSurface.setAlpha(mDimCurrentAlpha); - animating = true; - } else { - mDimCurrentAlpha = mDimTargetAlpha; - mLastDimAnimTime = 0; - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " - + mDimSurface + ": final alpha=" + mDimCurrentAlpha); - mDimSurface.setAlpha(mDimCurrentAlpha); - if (!dimming) { - if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface - + ": HIDE"); - try { - mDimSurface.hide(); - } catch (RuntimeException e) { - Slog.w(TAG, "Illegal argument exception hiding dim surface"); - } - mDimShown = false; - } - } - } - return animating; - } - - public void printTo(PrintWriter pw) { - pw.print(" mDimShown="); pw.print(mDimShown); - pw.print(" current="); pw.print(mDimCurrentAlpha); - pw.print(" target="); pw.print(mDimTargetAlpha); - pw.print(" delta="); pw.print(mDimDeltaPerMs); - pw.print(" lastAnimTime="); pw.println(mLastDimAnimTime); - } - } - - /** - * Animation that fade in after 0.5 interpolate time, or fade out in reverse order. - * This is used for opening/closing transition for apps in compatible mode. - */ - private static class FadeInOutAnimation extends Animation { - boolean mFadeIn; - - public FadeInOutAnimation(boolean fadeIn) { - setInterpolator(new AccelerateInterpolator()); - setDuration(DEFAULT_FADE_IN_OUT_DURATION); - mFadeIn = fadeIn; - } - - @Override - protected void applyTransformation(float interpolatedTime, Transformation t) { - float x = interpolatedTime; - if (!mFadeIn) { - x = 1.0f - x; // reverse the interpolation for fade out - } - t.setAlpha(x); - } - } - public interface OnHardKeyboardStatusChangeListener { public void onHardKeyboardStatusChange(boolean available, boolean enabled); } diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java new file mode 100644 index 000000000000..d0eec898c3af --- /dev/null +++ b/services/java/com/android/server/wm/WindowState.java @@ -0,0 +1,1623 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; +import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; + +import com.android.server.wm.WindowManagerService.H; + +import android.content.res.Configuration; +import android.graphics.Matrix; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.Region; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; +import android.view.Gravity; +import android.view.IApplicationToken; +import android.view.IWindow; +import android.view.InputChannel; +import android.view.Surface; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.view.WindowManagerPolicy; +import android.view.WindowManager.LayoutParams; +import android.view.animation.Animation; +import android.view.animation.Transformation; + +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * A window in the window manager. + */ +final class WindowState implements WindowManagerPolicy.WindowState { + final WindowManagerService mService; + final Session mSession; + final IWindow mClient; + WindowToken mToken; + WindowToken mRootToken; + AppWindowToken mAppToken; + AppWindowToken mTargetAppToken; + final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams(); + final DeathRecipient mDeathRecipient; + final WindowState mAttachedWindow; + final ArrayList<WindowState> mChildWindows = new ArrayList<WindowState>(); + final int mBaseLayer; + final int mSubLayer; + final boolean mLayoutAttached; + final boolean mIsImWindow; + final boolean mIsWallpaper; + final boolean mIsFloatingLayer; + int mViewVisibility; + boolean mPolicyVisibility = true; + boolean mPolicyVisibilityAfterAnim = true; + boolean mAppFreezing; + Surface mSurface; + boolean mReportDestroySurface; + boolean mSurfacePendingDestroy; + boolean mAttachedHidden; // is our parent window hidden? + boolean mLastHidden; // was this window last hidden? + boolean mWallpaperVisible; // for wallpaper, what was last vis report? + int mRequestedWidth; + int mRequestedHeight; + int mLastRequestedWidth; + int mLastRequestedHeight; + int mLayer; + int mAnimLayer; + int mLastLayer; + boolean mHaveFrame; + boolean mObscured; + boolean mTurnOnScreen; + + int mLayoutSeq = -1; + + Configuration mConfiguration = null; + + // Actual frame shown on-screen (may be modified by animation) + final Rect mShownFrame = new Rect(); + final Rect mLastShownFrame = new Rect(); + + /** + * Set when we have changed the size of the surface, to know that + * we must tell them application to resize (and thus redraw itself). + */ + boolean mSurfaceResized; + + /** + * Insets that determine the actually visible area + */ + final Rect mVisibleInsets = new Rect(); + final Rect mLastVisibleInsets = new Rect(); + boolean mVisibleInsetsChanged; + + /** + * Insets that are covered by system windows + */ + final Rect mContentInsets = new Rect(); + final Rect mLastContentInsets = new Rect(); + boolean mContentInsetsChanged; + + /** + * Set to true if we are waiting for this window to receive its + * given internal insets before laying out other windows based on it. + */ + boolean mGivenInsetsPending; + + /** + * These are the content insets that were given during layout for + * this window, to be applied to windows behind it. + */ + final Rect mGivenContentInsets = new Rect(); + + /** + * These are the visible insets that were given during layout for + * this window, to be applied to windows behind it. + */ + final Rect mGivenVisibleInsets = new Rect(); + + /** + * This is the given touchable area relative to the window frame, or null if none. + */ + final Region mGivenTouchableRegion = new Region(); + + /** + * Flag indicating whether the touchable region should be adjusted by + * the visible insets; if false the area outside the visible insets is + * NOT touchable, so we must use those to adjust the frame during hit + * tests. + */ + int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; + + // Current transformation being applied. + boolean mHaveMatrix; + float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1; + float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1; + float mHScale=1, mVScale=1; + float mLastHScale=1, mLastVScale=1; + final Matrix mTmpMatrix = new Matrix(); + + // "Real" frame that the application sees. + final Rect mFrame = new Rect(); + final Rect mLastFrame = new Rect(); + + final Rect mContainingFrame = new Rect(); + final Rect mDisplayFrame = new Rect(); + final Rect mContentFrame = new Rect(); + final Rect mParentFrame = new Rect(); + final Rect mVisibleFrame = new Rect(); + + boolean mContentChanged; + + float mShownAlpha = 1; + float mAlpha = 1; + float mLastAlpha = 1; + + // Set to true if, when the window gets displayed, it should perform + // an enter animation. + boolean mEnterAnimationPending; + + // Currently running animation. + boolean mAnimating; + boolean mLocalAnimating; + Animation mAnimation; + boolean mAnimationIsEntrance; + boolean mHasTransformation; + boolean mHasLocalTransformation; + final Transformation mTransformation = new Transformation(); + + // If a window showing a wallpaper: the requested offset for the + // wallpaper; if a wallpaper window: the currently applied offset. + float mWallpaperX = -1; + float mWallpaperY = -1; + + // If a window showing a wallpaper: what fraction of the offset + // range corresponds to a full virtual screen. + float mWallpaperXStep = -1; + float mWallpaperYStep = -1; + + // Wallpaper windows: pixels offset based on above variables. + int mXOffset; + int mYOffset; + + // This is set after IWindowSession.relayout() has been called at + // least once for the window. It allows us to detect the situation + // where we don't yet have a surface, but should have one soon, so + // we can give the window focus before waiting for the relayout. + boolean mRelayoutCalled; + + // This is set after the Surface has been created but before the + // window has been drawn. During this time the surface is hidden. + boolean mDrawPending; + + // This is set after the window has finished drawing for the first + // time but before its surface is shown. The surface will be + // displayed when the next layout is run. + boolean mCommitDrawPending; + + // This is set during the time after the window's drawing has been + // committed, and before its surface is actually shown. It is used + // to delay showing the surface until all windows in a token are ready + // to be shown. + boolean mReadyToShow; + + // Set when the window has been shown in the screen the first time. + boolean mHasDrawn; + + // Currently running an exit animation? + boolean mExiting; + + // Currently on the mDestroySurface list? + boolean mDestroying; + + // Completely remove from window manager after exit animation? + boolean mRemoveOnExit; + + // Set when the orientation is changing and this window has not yet + // been updated for the new orientation. + boolean mOrientationChanging; + + // Is this window now (or just being) removed? + boolean mRemoved; + + // Temp for keeping track of windows that have been removed when + // rebuilding window list. + boolean mRebuilding; + + // For debugging, this is the last information given to the surface flinger. + boolean mSurfaceShown; + int mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH; + int mSurfaceLayer; + float mSurfaceAlpha; + + // Input channel and input window handle used by the input dispatcher. + InputWindowHandle mInputWindowHandle; + InputChannel mInputChannel; + + // Used to improve performance of toString() + String mStringNameCache; + CharSequence mLastTitle; + boolean mWasPaused; + + WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, + WindowState attachedWindow, WindowManager.LayoutParams a, + int viewVisibility) { + mService = service; + mSession = s; + mClient = c; + mToken = token; + mAttrs.copyFrom(a); + mViewVisibility = viewVisibility; + DeathRecipient deathRecipient = new DeathRecipient(); + mAlpha = a.alpha; + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Window " + this + " client=" + c.asBinder() + + " token=" + token + " (" + mAttrs.token + ")"); + try { + c.asBinder().linkToDeath(deathRecipient, 0); + } catch (RemoteException e) { + mDeathRecipient = null; + mAttachedWindow = null; + mLayoutAttached = false; + mIsImWindow = false; + mIsWallpaper = false; + mIsFloatingLayer = false; + mBaseLayer = 0; + mSubLayer = 0; + return; + } + mDeathRecipient = deathRecipient; + + if ((mAttrs.type >= FIRST_SUB_WINDOW && + mAttrs.type <= LAST_SUB_WINDOW)) { + // The multiplier here is to reserve space for multiple + // windows in the same type layer. + mBaseLayer = mService.mPolicy.windowTypeToLayerLw( + attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER + + WindowManagerService.TYPE_LAYER_OFFSET; + mSubLayer = mService.mPolicy.subWindowTypeToLayerLw(a.type); + mAttachedWindow = attachedWindow; + if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(WindowManagerService.TAG, "Adding " + this + " to " + mAttachedWindow); + mAttachedWindow.mChildWindows.add(this); + mLayoutAttached = mAttrs.type != + WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; + mIsImWindow = attachedWindow.mAttrs.type == TYPE_INPUT_METHOD + || attachedWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG; + mIsWallpaper = attachedWindow.mAttrs.type == TYPE_WALLPAPER; + mIsFloatingLayer = mIsImWindow || mIsWallpaper; + } else { + // The multiplier here is to reserve space for multiple + // windows in the same type layer. + mBaseLayer = mService.mPolicy.windowTypeToLayerLw(a.type) + * WindowManagerService.TYPE_LAYER_MULTIPLIER + + WindowManagerService.TYPE_LAYER_OFFSET; + mSubLayer = 0; + mAttachedWindow = null; + mLayoutAttached = false; + mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD + || mAttrs.type == TYPE_INPUT_METHOD_DIALOG; + mIsWallpaper = mAttrs.type == TYPE_WALLPAPER; + mIsFloatingLayer = mIsImWindow || mIsWallpaper; + } + + WindowState appWin = this; + while (appWin.mAttachedWindow != null) { + appWin = mAttachedWindow; + } + WindowToken appToken = appWin.mToken; + while (appToken.appWindowToken == null) { + WindowToken parent = mService.mTokenMap.get(appToken.token); + if (parent == null || appToken == parent) { + break; + } + appToken = parent; + } + mRootToken = appToken; + mAppToken = appToken.appWindowToken; + + mSurface = null; + mRequestedWidth = 0; + mRequestedHeight = 0; + mLastRequestedWidth = 0; + mLastRequestedHeight = 0; + mXOffset = 0; + mYOffset = 0; + mLayer = 0; + mAnimLayer = 0; + mLastLayer = 0; + mInputWindowHandle = new InputWindowHandle( + mAppToken != null ? mAppToken.mInputApplicationHandle : null, this); + } + + void attach() { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Attaching " + this + " token=" + mToken + + ", list=" + mToken.windows); + mSession.windowAddedLocked(); + } + + public void computeFrameLw(Rect pf, Rect df, Rect cf, Rect vf) { + mHaveFrame = true; + + final Rect container = mContainingFrame; + container.set(pf); + + final Rect display = mDisplayFrame; + display.set(df); + + if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) { + container.intersect(mService.mCompatibleScreenFrame); + if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) { + display.intersect(mService.mCompatibleScreenFrame); + } + } + + final int pw = container.right - container.left; + final int ph = container.bottom - container.top; + + int w,h; + if ((mAttrs.flags & mAttrs.FLAG_SCALED) != 0) { + w = mAttrs.width < 0 ? pw : mAttrs.width; + h = mAttrs.height< 0 ? ph : mAttrs.height; + } else { + w = mAttrs.width == mAttrs.MATCH_PARENT ? pw : mRequestedWidth; + h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight; + } + + if (!mParentFrame.equals(pf)) { + //Slog.i(TAG, "Window " + this + " content frame from " + mParentFrame + // + " to " + pf); + mParentFrame.set(pf); + mContentChanged = true; + } + + final Rect content = mContentFrame; + content.set(cf); + + final Rect visible = mVisibleFrame; + visible.set(vf); + + final Rect frame = mFrame; + final int fw = frame.width(); + final int fh = frame.height(); + + //System.out.println("In: w=" + w + " h=" + h + " container=" + + // container + " x=" + mAttrs.x + " y=" + mAttrs.y); + + Gravity.apply(mAttrs.gravity, w, h, container, + (int) (mAttrs.x + mAttrs.horizontalMargin * pw), + (int) (mAttrs.y + mAttrs.verticalMargin * ph), frame); + + //System.out.println("Out: " + mFrame); + + // Now make sure the window fits in the overall display. + Gravity.applyDisplay(mAttrs.gravity, df, frame); + + // Make sure the content and visible frames are inside of the + // final window frame. + if (content.left < frame.left) content.left = frame.left; + if (content.top < frame.top) content.top = frame.top; + if (content.right > frame.right) content.right = frame.right; + if (content.bottom > frame.bottom) content.bottom = frame.bottom; + if (visible.left < frame.left) visible.left = frame.left; + if (visible.top < frame.top) visible.top = frame.top; + if (visible.right > frame.right) visible.right = frame.right; + if (visible.bottom > frame.bottom) visible.bottom = frame.bottom; + + final Rect contentInsets = mContentInsets; + contentInsets.left = content.left-frame.left; + contentInsets.top = content.top-frame.top; + contentInsets.right = frame.right-content.right; + contentInsets.bottom = frame.bottom-content.bottom; + + final Rect visibleInsets = mVisibleInsets; + visibleInsets.left = visible.left-frame.left; + visibleInsets.top = visible.top-frame.top; + visibleInsets.right = frame.right-visible.right; + visibleInsets.bottom = frame.bottom-visible.bottom; + + if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) { + mService.updateWallpaperOffsetLocked(this, mService.mDisplay.getWidth(), + mService.mDisplay.getHeight(), false); + } + + if (WindowManagerService.localLOGV) { + //if ("com.google.android.youtube".equals(mAttrs.packageName) + // && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { + Slog.v(WindowManagerService.TAG, "Resolving (mRequestedWidth=" + + mRequestedWidth + ", mRequestedheight=" + + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph + + "): frame=" + mFrame.toShortString() + + " ci=" + contentInsets.toShortString() + + " vi=" + visibleInsets.toShortString()); + //} + } + } + + public Rect getFrameLw() { + return mFrame; + } + + public Rect getShownFrameLw() { + return mShownFrame; + } + + public Rect getDisplayFrameLw() { + return mDisplayFrame; + } + + public Rect getContentFrameLw() { + return mContentFrame; + } + + public Rect getVisibleFrameLw() { + return mVisibleFrame; + } + + public boolean getGivenInsetsPendingLw() { + return mGivenInsetsPending; + } + + public Rect getGivenContentInsetsLw() { + return mGivenContentInsets; + } + + public Rect getGivenVisibleInsetsLw() { + return mGivenVisibleInsets; + } + + public WindowManager.LayoutParams getAttrs() { + return mAttrs; + } + + public int getSurfaceLayer() { + return mLayer; + } + + public IApplicationToken getAppToken() { + return mAppToken != null ? mAppToken.appToken : null; + } + + public long getInputDispatchingTimeoutNanos() { + return mAppToken != null + ? mAppToken.inputDispatchingTimeoutNanos + : WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + } + + public boolean hasAppShownWindows() { + return mAppToken != null ? mAppToken.firstWindowDrawn : false; + } + + public void setAnimation(Animation anim) { + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Setting animation in " + this + ": " + anim); + mAnimating = false; + mLocalAnimating = false; + mAnimation = anim; + mAnimation.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION); + mAnimation.scaleCurrentDuration(mService.mWindowAnimationScale); + } + + public void clearAnimation() { + if (mAnimation != null) { + mAnimating = true; + mLocalAnimating = false; + mAnimation.cancel(); + mAnimation = null; + } + } + + Surface createSurfaceLocked() { + if (mSurface == null) { + mReportDestroySurface = false; + mSurfacePendingDestroy = false; + mDrawPending = true; + mCommitDrawPending = false; + mReadyToShow = false; + if (mAppToken != null) { + mAppToken.allDrawn = false; + } + + int flags = 0; + + if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) { + flags |= Surface.SECURE; + } + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v( + WindowManagerService.TAG, "Creating surface in session " + + mSession.mSurfaceSession + " window " + this + + " w=" + mFrame.width() + + " h=" + mFrame.height() + " format=" + + mAttrs.format + " flags=" + flags); + + int w = mFrame.width(); + int h = mFrame.height(); + if ((mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) { + // for a scaled surface, we always want the requested + // size. + w = mRequestedWidth; + h = mRequestedHeight; + } + + // Something is wrong and SurfaceFlinger will not like this, + // try to revert to sane values + if (w <= 0) w = 1; + if (h <= 0) h = 1; + + mSurfaceShown = false; + mSurfaceLayer = 0; + mSurfaceAlpha = 1; + mSurfaceX = 0; + mSurfaceY = 0; + mSurfaceW = w; + mSurfaceH = h; + try { + final boolean isHwAccelerated = (mAttrs.flags & + WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; + final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : mAttrs.format; + if (isHwAccelerated && mAttrs.format == PixelFormat.OPAQUE) { + flags |= Surface.OPAQUE; + } + mSurface = new Surface( + mSession.mSurfaceSession, mSession.mPid, + mAttrs.getTitle().toString(), + 0, w, h, format, flags); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " CREATE SURFACE " + + mSurface + " IN SESSION " + + mSession.mSurfaceSession + + ": pid=" + mSession.mPid + " format=" + + mAttrs.format + " flags=0x" + + Integer.toHexString(flags) + + " / " + this); + } catch (Surface.OutOfResourcesException e) { + Slog.w(WindowManagerService.TAG, "OutOfResourcesException creating surface"); + mService.reclaimSomeSurfaceMemoryLocked(this, "create"); + return null; + } catch (Exception e) { + Slog.e(WindowManagerService.TAG, "Exception creating surface", e); + return null; + } + + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Got surface: " + mSurface + + ", set left=" + mFrame.left + " top=" + mFrame.top + + ", animLayer=" + mAnimLayer); + if (WindowManagerService.SHOW_TRANSACTIONS) { + Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION createSurfaceLocked"); + WindowManagerService.logSurface(this, "CREATE pos=(" + mFrame.left + "," + mFrame.top + ") (" + + mFrame.width() + "x" + mFrame.height() + "), layer=" + + mAnimLayer + " HIDE", null); + } + Surface.openTransaction(); + try { + try { + mSurfaceX = mFrame.left + mXOffset; + mSurfaceY = mFrame.top + mYOffset; + mSurface.setPosition(mSurfaceX, mSurfaceY); + mSurfaceLayer = mAnimLayer; + mSurface.setLayer(mAnimLayer); + mSurfaceShown = false; + mSurface.hide(); + if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) { + if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(this, "DITHER", null); + mSurface.setFlags(Surface.SURFACE_DITHER, + Surface.SURFACE_DITHER); + } + } catch (RuntimeException e) { + Slog.w(WindowManagerService.TAG, "Error creating surface in " + w, e); + mService.reclaimSomeSurfaceMemoryLocked(this, "create-init"); + } + mLastHidden = true; + } finally { + Surface.closeTransaction(); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION createSurfaceLocked"); + } + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Created surface " + this); + } + return mSurface; + } + + void destroySurfaceLocked() { + if (mAppToken != null && this == mAppToken.startingWindow) { + mAppToken.startingDisplayed = false; + } + + if (mSurface != null) { + mDrawPending = false; + mCommitDrawPending = false; + mReadyToShow = false; + + int i = mChildWindows.size(); + while (i > 0) { + i--; + WindowState c = mChildWindows.get(i); + c.mAttachedHidden = true; + } + + if (mReportDestroySurface) { + mReportDestroySurface = false; + mSurfacePendingDestroy = true; + try { + mClient.dispatchGetNewSurface(); + // We'll really destroy on the next time around. + return; + } catch (RemoteException e) { + } + } + + try { + if (WindowManagerService.DEBUG_VISIBILITY) { + RuntimeException e = null; + if (!WindowManagerService.HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.w(WindowManagerService.TAG, "Window " + this + " destroying surface " + + mSurface + ", session " + mSession, e); + } + if (WindowManagerService.SHOW_TRANSACTIONS) { + RuntimeException e = null; + if (!WindowManagerService.HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(this, "DESTROY", e); + } + mSurface.destroy(); + } catch (RuntimeException e) { + Slog.w(WindowManagerService.TAG, "Exception thrown when destroying Window " + this + + " surface " + mSurface + " session " + mSession + + ": " + e.toString()); + } + + mSurfaceShown = false; + mSurface = null; + } + } + + boolean finishDrawingLocked() { + if (mDrawPending) { + if (WindowManagerService.SHOW_TRANSACTIONS || WindowManagerService.DEBUG_ORIENTATION) Slog.v( + WindowManagerService.TAG, "finishDrawingLocked: " + mSurface); + mCommitDrawPending = true; + mDrawPending = false; + return true; + } + return false; + } + + // This must be called while inside a transaction. + boolean commitFinishDrawingLocked(long currentTime) { + //Slog.i(TAG, "commitFinishDrawingLocked: " + mSurface); + if (!mCommitDrawPending) { + return false; + } + mCommitDrawPending = false; + mReadyToShow = true; + final boolean starting = mAttrs.type == TYPE_APPLICATION_STARTING; + final AppWindowToken atoken = mAppToken; + if (atoken == null || atoken.allDrawn || starting) { + performShowLocked(); + } + return true; + } + + // This must be called while inside a transaction. + boolean performShowLocked() { + if (WindowManagerService.DEBUG_VISIBILITY) { + RuntimeException e = null; + if (!WindowManagerService.HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.v(WindowManagerService.TAG, "performShow on " + this + + ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay() + + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e); + } + if (mReadyToShow && isReadyForDisplay()) { + if (WindowManagerService.SHOW_TRANSACTIONS || WindowManagerService.DEBUG_ORIENTATION) WindowManagerService.logSurface(this, + "SHOW (performShowLocked)", null); + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Showing " + this + + " during animation: policyVis=" + mPolicyVisibility + + " attHidden=" + mAttachedHidden + + " tok.hiddenRequested=" + + (mAppToken != null ? mAppToken.hiddenRequested : false) + + " tok.hidden=" + + (mAppToken != null ? mAppToken.hidden : false) + + " animating=" + mAnimating + + " tok animating=" + + (mAppToken != null ? mAppToken.animating : false)); + if (!mService.showSurfaceRobustlyLocked(this)) { + return false; + } + mLastAlpha = -1; + mHasDrawn = true; + mLastHidden = false; + mReadyToShow = false; + mService.enableScreenIfNeededLocked(); + + mService.applyEnterAnimationLocked(this); + + int i = mChildWindows.size(); + while (i > 0) { + i--; + WindowState c = mChildWindows.get(i); + if (c.mAttachedHidden) { + c.mAttachedHidden = false; + if (c.mSurface != null) { + c.performShowLocked(); + // It hadn't been shown, which means layout not + // performed on it, so now we want to make sure to + // do a layout. If called from within the transaction + // loop, this will cause it to restart with a new + // layout. + mService.mLayoutNeeded = true; + } + } + } + + if (mAttrs.type != TYPE_APPLICATION_STARTING + && mAppToken != null) { + mAppToken.firstWindowDrawn = true; + + if (mAppToken.startingData != null) { + if (WindowManagerService.DEBUG_STARTING_WINDOW || WindowManagerService.DEBUG_ANIM) Slog.v(WindowManagerService.TAG, + "Finish starting " + mToken + + ": first real window is shown, no animation"); + // If this initial window is animating, stop it -- we + // will do an animation to reveal it from behind the + // starting window, so there is no need for it to also + // be doing its own stuff. + if (mAnimation != null) { + mAnimation.cancel(); + mAnimation = null; + // Make sure we clean up the animation. + mAnimating = true; + } + mService.mFinishedStarting.add(mAppToken); + mService.mH.sendEmptyMessage(H.FINISHED_STARTING); + } + mAppToken.updateReportedVisibilityLocked(); + } + } + return true; + } + + // This must be called while inside a transaction. Returns true if + // there is more animation to run. + boolean stepAnimationLocked(long currentTime, int dw, int dh) { + if (!mService.mDisplayFrozen && mService.mPolicy.isScreenOn()) { + // We will run animations as long as the display isn't frozen. + + if (!mDrawPending && !mCommitDrawPending && mAnimation != null) { + mHasTransformation = true; + mHasLocalTransformation = true; + if (!mLocalAnimating) { + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Starting animation in " + this + + " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() + + " dw=" + dw + " dh=" + dh + " scale=" + mService.mWindowAnimationScale); + mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh); + mAnimation.setStartTime(currentTime); + mLocalAnimating = true; + mAnimating = true; + } + mTransformation.clear(); + final boolean more = mAnimation.getTransformation( + currentTime, mTransformation); + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Stepped animation in " + this + + ": more=" + more + ", xform=" + mTransformation); + if (more) { + // we're not done! + return true; + } + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Finished animation in " + this + + " @ " + currentTime); + + if (mAnimation != null) { + mAnimation.cancel(); + mAnimation = null; + } + //WindowManagerService.this.dump(); + } + mHasLocalTransformation = false; + if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null + && mAppToken.animation != null) { + // When our app token is animating, we kind-of pretend like + // we are as well. Note the mLocalAnimating mAnimationIsEntrance + // part of this check means that we will only do this if + // our window is not currently exiting, or it is not + // locally animating itself. The idea being that one that + // is exiting and doing a local animation should be removed + // once that animation is done. + mAnimating = true; + mHasTransformation = true; + mTransformation.clear(); + return false; + } else if (mHasTransformation) { + // Little trick to get through the path below to act like + // we have finished an animation. + mAnimating = true; + } else if (isAnimating()) { + mAnimating = true; + } + } else if (mAnimation != null) { + // If the display is frozen, and there is a pending animation, + // clear it and make sure we run the cleanup code. + mAnimating = true; + mLocalAnimating = true; + mAnimation.cancel(); + mAnimation = null; + } + + if (!mAnimating && !mLocalAnimating) { + return false; + } + + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "Animation done in " + this + ": exiting=" + mExiting + + ", reportedVisible=" + + (mAppToken != null ? mAppToken.reportedVisible : false)); + + mAnimating = false; + mLocalAnimating = false; + if (mAnimation != null) { + mAnimation.cancel(); + mAnimation = null; + } + mAnimLayer = mLayer; + if (mIsImWindow) { + mAnimLayer += mService.mInputMethodAnimLayerAdjustment; + } else if (mIsWallpaper) { + mAnimLayer += mService.mWallpaperAnimLayerAdjustment; + } + if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Stepping win " + this + + " anim layer: " + mAnimLayer); + mHasTransformation = false; + mHasLocalTransformation = false; + if (mPolicyVisibility != mPolicyVisibilityAfterAnim) { + if (WindowManagerService.DEBUG_VISIBILITY) { + Slog.v(WindowManagerService.TAG, "Policy visibility changing after anim in " + this + ": " + + mPolicyVisibilityAfterAnim); + } + mPolicyVisibility = mPolicyVisibilityAfterAnim; + if (!mPolicyVisibility) { + if (mService.mCurrentFocus == this) { + mService.mFocusMayChange = true; + } + // Window is no longer visible -- make sure if we were waiting + // for it to be displayed before enabling the display, that + // we allow the display to be enabled now. + mService.enableScreenIfNeededLocked(); + } + } + mTransformation.clear(); + if (mHasDrawn + && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING + && mAppToken != null + && mAppToken.firstWindowDrawn + && mAppToken.startingData != null) { + if (WindowManagerService.DEBUG_STARTING_WINDOW) Slog.v(WindowManagerService.TAG, "Finish starting " + + mToken + ": first real window done animating"); + mService.mFinishedStarting.add(mAppToken); + mService.mH.sendEmptyMessage(H.FINISHED_STARTING); + } + + finishExit(); + + if (mAppToken != null) { + mAppToken.updateReportedVisibilityLocked(); + } + + return false; + } + + void finishExit() { + if (WindowManagerService.DEBUG_ANIM) Slog.v( + WindowManagerService.TAG, "finishExit in " + this + + ": exiting=" + mExiting + + " remove=" + mRemoveOnExit + + " windowAnimating=" + isWindowAnimating()); + + final int N = mChildWindows.size(); + for (int i=0; i<N; i++) { + mChildWindows.get(i).finishExit(); + } + + if (!mExiting) { + return; + } + + if (isWindowAnimating()) { + return; + } + + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Exit animation finished in " + this + + ": remove=" + mRemoveOnExit); + if (mSurface != null) { + mService.mDestroySurface.add(this); + mDestroying = true; + if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(this, "HIDE (finishExit)", null); + mSurfaceShown = false; + try { + mSurface.hide(); + } catch (RuntimeException e) { + Slog.w(WindowManagerService.TAG, "Error hiding surface in " + this, e); + } + mLastHidden = true; + } + mExiting = false; + if (mRemoveOnExit) { + mService.mPendingRemove.add(this); + mRemoveOnExit = false; + } + } + + boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { + if (dsdx < .99999f || dsdx > 1.00001f) return false; + if (dtdy < .99999f || dtdy > 1.00001f) return false; + if (dtdx < -.000001f || dtdx > .000001f) return false; + if (dsdy < -.000001f || dsdy > .000001f) return false; + return true; + } + + void computeShownFrameLocked() { + final boolean selfTransformation = mHasLocalTransformation; + Transformation attachedTransformation = + (mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation) + ? mAttachedWindow.mTransformation : null; + Transformation appTransformation = + (mAppToken != null && mAppToken.hasTransformation) + ? mAppToken.transformation : null; + + // Wallpapers are animated based on the "real" window they + // are currently targeting. + if (mAttrs.type == TYPE_WALLPAPER && mService.mLowerWallpaperTarget == null + && mService.mWallpaperTarget != null) { + if (mService.mWallpaperTarget.mHasLocalTransformation && + mService.mWallpaperTarget.mAnimation != null && + !mService.mWallpaperTarget.mAnimation.getDetachWallpaper()) { + attachedTransformation = mService.mWallpaperTarget.mTransformation; + if (WindowManagerService.DEBUG_WALLPAPER && attachedTransformation != null) { + Slog.v(WindowManagerService.TAG, "WP target attached xform: " + attachedTransformation); + } + } + if (mService.mWallpaperTarget.mAppToken != null && + mService.mWallpaperTarget.mAppToken.hasTransformation && + mService.mWallpaperTarget.mAppToken.animation != null && + !mService.mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) { + appTransformation = mService.mWallpaperTarget.mAppToken.transformation; + if (WindowManagerService.DEBUG_WALLPAPER && appTransformation != null) { + Slog.v(WindowManagerService.TAG, "WP target app xform: " + appTransformation); + } + } + } + + final boolean screenAnimation = mService.mScreenRotationAnimation != null + && mService.mScreenRotationAnimation.isAnimating(); + if (selfTransformation || attachedTransformation != null + || appTransformation != null || screenAnimation) { + // cache often used attributes locally + final Rect frame = mFrame; + final float tmpFloats[] = mService.mTmpFloats; + final Matrix tmpMatrix = mTmpMatrix; + + // Compute the desired transformation. + tmpMatrix.setTranslate(0, 0); + if (selfTransformation) { + tmpMatrix.postConcat(mTransformation.getMatrix()); + } + tmpMatrix.postTranslate(frame.left + mXOffset, frame.top + mYOffset); + if (attachedTransformation != null) { + tmpMatrix.postConcat(attachedTransformation.getMatrix()); + } + if (appTransformation != null) { + tmpMatrix.postConcat(appTransformation.getMatrix()); + } + if (screenAnimation) { + tmpMatrix.postConcat( + mService.mScreenRotationAnimation.getEnterTransformation().getMatrix()); + } + + // "convert" it into SurfaceFlinger's format + // (a 2x2 matrix + an offset) + // Here we must not transform the position of the surface + // since it is already included in the transformation. + //Slog.i(TAG, "Transform: " + matrix); + + mHaveMatrix = true; + tmpMatrix.getValues(tmpFloats); + mDsDx = tmpFloats[Matrix.MSCALE_X]; + mDtDx = tmpFloats[Matrix.MSKEW_Y]; + mDsDy = tmpFloats[Matrix.MSKEW_X]; + mDtDy = tmpFloats[Matrix.MSCALE_Y]; + int x = (int)tmpFloats[Matrix.MTRANS_X]; + int y = (int)tmpFloats[Matrix.MTRANS_Y]; + int w = frame.width(); + int h = frame.height(); + mShownFrame.set(x, y, x+w, y+h); + + // Now set the alpha... but because our current hardware + // can't do alpha transformation on a non-opaque surface, + // turn it off if we are running an animation that is also + // transforming since it is more important to have that + // animation be smooth. + mShownAlpha = mAlpha; + if (!mService.mLimitedAlphaCompositing + || (!PixelFormat.formatHasAlpha(mAttrs.format) + || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy) + && x == frame.left && y == frame.top))) { + //Slog.i(TAG, "Applying alpha transform"); + if (selfTransformation) { + mShownAlpha *= mTransformation.getAlpha(); + } + if (attachedTransformation != null) { + mShownAlpha *= attachedTransformation.getAlpha(); + } + if (appTransformation != null) { + mShownAlpha *= appTransformation.getAlpha(); + } + if (screenAnimation) { + mShownAlpha *= + mService.mScreenRotationAnimation.getEnterTransformation().getAlpha(); + } + } else { + //Slog.i(TAG, "Not applying alpha transform"); + } + + if (WindowManagerService.localLOGV) Slog.v( + WindowManagerService.TAG, "Continuing animation in " + this + + ": " + mShownFrame + + ", alpha=" + mTransformation.getAlpha()); + return; + } + + mShownFrame.set(mFrame); + if (mXOffset != 0 || mYOffset != 0) { + mShownFrame.offset(mXOffset, mYOffset); + } + mShownAlpha = mAlpha; + mHaveMatrix = false; + mDsDx = 1; + mDtDx = 0; + mDsDy = 0; + mDtDy = 1; + } + + /** + * Is this window visible? It is not visible if there is no + * surface, or we are in the process of running an exit animation + * that will remove the surface, or its app token has been hidden. + */ + public boolean isVisibleLw() { + final AppWindowToken atoken = mAppToken; + return mSurface != null && mPolicyVisibility && !mAttachedHidden + && (atoken == null || !atoken.hiddenRequested) + && !mExiting && !mDestroying; + } + + /** + * Like {@link #isVisibleLw}, but also counts a window that is currently + * "hidden" behind the keyguard as visible. This allows us to apply + * things like window flags that impact the keyguard. + * XXX I am starting to think we need to have ANOTHER visibility flag + * for this "hidden behind keyguard" state rather than overloading + * mPolicyVisibility. Ungh. + */ + public boolean isVisibleOrBehindKeyguardLw() { + final AppWindowToken atoken = mAppToken; + return mSurface != null && !mAttachedHidden + && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested) + && !mDrawPending && !mCommitDrawPending + && !mExiting && !mDestroying; + } + + /** + * Is this window visible, ignoring its app token? It is not visible + * if there is no surface, or we are in the process of running an exit animation + * that will remove the surface. + */ + public boolean isWinVisibleLw() { + final AppWindowToken atoken = mAppToken; + return mSurface != null && mPolicyVisibility && !mAttachedHidden + && (atoken == null || !atoken.hiddenRequested || atoken.animating) + && !mExiting && !mDestroying; + } + + /** + * The same as isVisible(), but follows the current hidden state of + * the associated app token, not the pending requested hidden state. + */ + boolean isVisibleNow() { + return mSurface != null && mPolicyVisibility && !mAttachedHidden + && !mRootToken.hidden && !mExiting && !mDestroying; + } + + /** + * Can this window possibly be a drag/drop target? The test here is + * a combination of the above "visible now" with the check that the + * Input Manager uses when discarding windows from input consideration. + */ + boolean isPotentialDragTarget() { + return isVisibleNow() && (mInputChannel != null) && !mRemoved; + } + + /** + * Same as isVisible(), but we also count it as visible between the + * call to IWindowSession.add() and the first relayout(). + */ + boolean isVisibleOrAdding() { + final AppWindowToken atoken = mAppToken; + return ((mSurface != null && !mReportDestroySurface) + || (!mRelayoutCalled && mViewVisibility == View.VISIBLE)) + && mPolicyVisibility && !mAttachedHidden + && (atoken == null || !atoken.hiddenRequested) + && !mExiting && !mDestroying; + } + + /** + * Is this window currently on-screen? It is on-screen either if it + * is visible or it is currently running an animation before no longer + * being visible. + */ + boolean isOnScreen() { + final AppWindowToken atoken = mAppToken; + if (atoken != null) { + return mSurface != null && mPolicyVisibility && !mDestroying + && ((!mAttachedHidden && !atoken.hiddenRequested) + || mAnimation != null || atoken.animation != null); + } else { + return mSurface != null && mPolicyVisibility && !mDestroying + && (!mAttachedHidden || mAnimation != null); + } + } + + /** + * Like isOnScreen(), but we don't return true if the window is part + * of a transition that has not yet been started. + */ + boolean isReadyForDisplay() { + if (mRootToken.waitingToShow && + mService.mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { + return false; + } + final AppWindowToken atoken = mAppToken; + final boolean animating = atoken != null + ? (atoken.animation != null) : false; + return mSurface != null && mPolicyVisibility && !mDestroying + && ((!mAttachedHidden && mViewVisibility == View.VISIBLE + && !mRootToken.hidden) + || mAnimation != null || animating); + } + + /** Is the window or its container currently animating? */ + boolean isAnimating() { + final WindowState attached = mAttachedWindow; + final AppWindowToken atoken = mAppToken; + return mAnimation != null + || (attached != null && attached.mAnimation != null) + || (atoken != null && + (atoken.animation != null + || atoken.inPendingTransaction)); + } + + /** Is this window currently animating? */ + boolean isWindowAnimating() { + return mAnimation != null; + } + + /** + * Like isOnScreen, but returns false if the surface hasn't yet + * been drawn. + */ + public boolean isDisplayedLw() { + final AppWindowToken atoken = mAppToken; + return mSurface != null && mPolicyVisibility && !mDestroying + && !mDrawPending && !mCommitDrawPending + && ((!mAttachedHidden && + (atoken == null || !atoken.hiddenRequested)) + || mAnimating); + } + + /** + * Returns true if the window has a surface that it has drawn a + * complete UI in to. + */ + public boolean isDrawnLw() { + final AppWindowToken atoken = mAppToken; + return mSurface != null && !mDestroying + && !mDrawPending && !mCommitDrawPending; + } + + /** + * Return true if the window is opaque and fully drawn. This indicates + * it may obscure windows behind it. + */ + boolean isOpaqueDrawn() { + return (mAttrs.format == PixelFormat.OPAQUE + || mAttrs.type == TYPE_WALLPAPER) + && mSurface != null && mAnimation == null + && (mAppToken == null || mAppToken.animation == null) + && !mDrawPending && !mCommitDrawPending; + } + + /** + * Return whether this window is wanting to have a translation + * animation applied to it for an in-progress move. (Only makes + * sense to call from performLayoutAndPlaceSurfacesLockedInner().) + */ + boolean shouldAnimateMove() { + return mContentChanged && !mExiting && !mLastHidden && !mService.mDisplayFrozen + && (mFrame.top != mLastFrame.top + || mFrame.left != mLastFrame.left) + && (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove()) + && mService.mPolicy.isScreenOn(); + } + + boolean needsBackgroundFiller(int screenWidth, int screenHeight) { + return + // only if the application is requesting compatible window + (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0 && + // only if it's visible + mHasDrawn && mViewVisibility == View.VISIBLE && + // and only if the application fills the compatible screen + mFrame.left <= mService.mCompatibleScreenFrame.left && + mFrame.top <= mService.mCompatibleScreenFrame.top && + mFrame.right >= mService.mCompatibleScreenFrame.right && + mFrame.bottom >= mService.mCompatibleScreenFrame.bottom; + } + + boolean isFullscreen(int screenWidth, int screenHeight) { + return mFrame.left <= 0 && mFrame.top <= 0 && + mFrame.right >= screenWidth && mFrame.bottom >= screenHeight; + } + + void removeLocked() { + disposeInputChannel(); + + if (mAttachedWindow != null) { + if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(WindowManagerService.TAG, "Removing " + this + " from " + mAttachedWindow); + mAttachedWindow.mChildWindows.remove(this); + } + destroySurfaceLocked(); + mSession.windowRemovedLocked(); + try { + mClient.asBinder().unlinkToDeath(mDeathRecipient, 0); + } catch (RuntimeException e) { + // Ignore if it has already been removed (usually because + // we are doing this as part of processing a death note.) + } + } + + void disposeInputChannel() { + if (mInputChannel != null) { + mService.mInputManager.unregisterInputChannel(mInputChannel); + + mInputChannel.dispose(); + mInputChannel = null; + } + } + + private class DeathRecipient implements IBinder.DeathRecipient { + public void binderDied() { + try { + synchronized(mService.mWindowMap) { + WindowState win = mService.windowForClientLocked(mSession, mClient, false); + Slog.i(WindowManagerService.TAG, "WIN DEATH: " + win); + if (win != null) { + mService.removeWindowLocked(mSession, win); + } + } + } catch (IllegalArgumentException ex) { + // This will happen if the window has already been + // removed. + } + } + } + + /** Returns true if this window desires key events. */ + public final boolean canReceiveKeys() { + return isVisibleOrAdding() + && (mViewVisibility == View.VISIBLE) + && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0); + } + + public boolean hasDrawnLw() { + return mHasDrawn; + } + + public boolean showLw(boolean doAnimation) { + return showLw(doAnimation, true); + } + + boolean showLw(boolean doAnimation, boolean requestAnim) { + if (mPolicyVisibility && mPolicyVisibilityAfterAnim) { + return false; + } + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Policy visibility true: " + this); + if (doAnimation) { + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "doAnimation: mPolicyVisibility=" + + mPolicyVisibility + " mAnimation=" + mAnimation); + if (mService.mDisplayFrozen || !mService.mPolicy.isScreenOn()) { + doAnimation = false; + } else if (mPolicyVisibility && mAnimation == null) { + // Check for the case where we are currently visible and + // not animating; we do not want to do animation at such a + // point to become visible when we already are. + doAnimation = false; + } + } + mPolicyVisibility = true; + mPolicyVisibilityAfterAnim = true; + if (doAnimation) { + mService.applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true); + } + if (requestAnim) { + mService.requestAnimationLocked(0); + } + return true; + } + + public boolean hideLw(boolean doAnimation) { + return hideLw(doAnimation, true); + } + + boolean hideLw(boolean doAnimation, boolean requestAnim) { + if (doAnimation) { + if (mService.mDisplayFrozen || !mService.mPolicy.isScreenOn()) { + doAnimation = false; + } + } + boolean current = doAnimation ? mPolicyVisibilityAfterAnim + : mPolicyVisibility; + if (!current) { + return false; + } + if (doAnimation) { + mService.applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false); + if (mAnimation == null) { + doAnimation = false; + } + } + if (doAnimation) { + mPolicyVisibilityAfterAnim = false; + } else { + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Policy visibility false: " + this); + mPolicyVisibilityAfterAnim = false; + mPolicyVisibility = false; + // Window is no longer visible -- make sure if we were waiting + // for it to be displayed before enabling the display, that + // we allow the display to be enabled now. + mService.enableScreenIfNeededLocked(); + if (mService.mCurrentFocus == this) { + mService.mFocusMayChange = true; + } + } + if (requestAnim) { + mService.requestAnimationLocked(0); + } + return true; + } + + public void getTouchableRegion(Region outRegion) { + final Rect frame = mFrame; + switch (mTouchableInsets) { + default: + case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME: + outRegion.set(frame); + break; + case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: { + final Rect inset = mGivenContentInsets; + outRegion.set( + frame.left + inset.left, frame.top + inset.top, + frame.right - inset.right, frame.bottom - inset.bottom); + break; + } + case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: { + final Rect inset = mGivenVisibleInsets; + outRegion.set( + frame.left + inset.left, frame.top + inset.top, + frame.right - inset.right, frame.bottom - inset.bottom); + break; + } + case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: { + final Region givenTouchableRegion = mGivenTouchableRegion; + outRegion.set(givenTouchableRegion); + outRegion.translate(frame.left, frame.top); + break; + } + } + } + + void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("mSession="); pw.print(mSession); + pw.print(" mClient="); pw.println(mClient.asBinder()); + pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs); + if (mAttachedWindow != null || mLayoutAttached) { + pw.print(prefix); pw.print("mAttachedWindow="); pw.print(mAttachedWindow); + pw.print(" mLayoutAttached="); pw.println(mLayoutAttached); + } + if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) { + pw.print(prefix); pw.print("mIsImWindow="); pw.print(mIsImWindow); + pw.print(" mIsWallpaper="); pw.print(mIsWallpaper); + pw.print(" mIsFloatingLayer="); pw.print(mIsFloatingLayer); + pw.print(" mWallpaperVisible="); pw.println(mWallpaperVisible); + } + pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer); + pw.print(" mSubLayer="); pw.print(mSubLayer); + pw.print(" mAnimLayer="); pw.print(mLayer); pw.print("+"); + pw.print((mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment + : (mAppToken != null ? mAppToken.animLayerAdjustment : 0))); + pw.print("="); pw.print(mAnimLayer); + pw.print(" mLastLayer="); pw.println(mLastLayer); + if (mSurface != null) { + pw.print(prefix); pw.print("mSurface="); pw.println(mSurface); + pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown); + pw.print(" layer="); pw.print(mSurfaceLayer); + pw.print(" alpha="); pw.print(mSurfaceAlpha); + pw.print(" rect=("); pw.print(mSurfaceX); + pw.print(","); pw.print(mSurfaceY); + pw.print(") "); pw.print(mSurfaceW); + pw.print(" x "); pw.println(mSurfaceH); + } + pw.print(prefix); pw.print("mToken="); pw.println(mToken); + pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken); + if (mAppToken != null) { + pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken); + } + if (mTargetAppToken != null) { + pw.print(prefix); pw.print("mTargetAppToken="); pw.println(mTargetAppToken); + } + pw.print(prefix); pw.print("mViewVisibility=0x"); + pw.print(Integer.toHexString(mViewVisibility)); + pw.print(" mLastHidden="); pw.print(mLastHidden); + pw.print(" mHaveFrame="); pw.print(mHaveFrame); + pw.print(" mObscured="); pw.println(mObscured); + if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || mAttachedHidden) { + pw.print(prefix); pw.print("mPolicyVisibility="); + pw.print(mPolicyVisibility); + pw.print(" mPolicyVisibilityAfterAnim="); + pw.print(mPolicyVisibilityAfterAnim); + pw.print(" mAttachedHidden="); pw.println(mAttachedHidden); + } + if (!mRelayoutCalled) { + pw.print(prefix); pw.print("mRelayoutCalled="); pw.println(mRelayoutCalled); + } + pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth); + pw.print(" h="); pw.print(mRequestedHeight); + pw.print(" mLayoutSeq="); pw.println(mLayoutSeq); + if (mXOffset != 0 || mYOffset != 0) { + pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset); + pw.print(" y="); pw.println(mYOffset); + } + pw.print(prefix); pw.print("mGivenContentInsets="); + mGivenContentInsets.printShortString(pw); + pw.print(" mGivenVisibleInsets="); + mGivenVisibleInsets.printShortString(pw); + pw.println(); + if (mTouchableInsets != 0 || mGivenInsetsPending) { + pw.print(prefix); pw.print("mTouchableInsets="); pw.print(mTouchableInsets); + pw.print(" mGivenInsetsPending="); pw.println(mGivenInsetsPending); + } + pw.print(prefix); pw.print("mConfiguration="); pw.println(mConfiguration); + pw.print(prefix); pw.print("mShownFrame="); + mShownFrame.printShortString(pw); + pw.print(" last="); mLastShownFrame.printShortString(pw); + pw.println(); + pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw); + pw.print(" last="); mLastFrame.printShortString(pw); + pw.println(); + pw.print(prefix); pw.print("mContainingFrame="); + mContainingFrame.printShortString(pw); + pw.print(" mParentFrame="); + mParentFrame.printShortString(pw); + pw.print(" mDisplayFrame="); + mDisplayFrame.printShortString(pw); + pw.println(); + pw.print(prefix); pw.print("mContentFrame="); mContentFrame.printShortString(pw); + pw.print(" mVisibleFrame="); mVisibleFrame.printShortString(pw); + pw.println(); + pw.print(prefix); pw.print("mContentInsets="); mContentInsets.printShortString(pw); + pw.print(" last="); mLastContentInsets.printShortString(pw); + pw.print(" mVisibleInsets="); mVisibleInsets.printShortString(pw); + pw.print(" last="); mLastVisibleInsets.printShortString(pw); + pw.println(); + if (mAnimating || mLocalAnimating || mAnimationIsEntrance + || mAnimation != null) { + pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating); + pw.print(" mLocalAnimating="); pw.print(mLocalAnimating); + pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance); + pw.print(" mAnimation="); pw.println(mAnimation); + } + if (mHasTransformation || mHasLocalTransformation) { + pw.print(prefix); pw.print("XForm: has="); + pw.print(mHasTransformation); + pw.print(" hasLocal="); pw.print(mHasLocalTransformation); + pw.print(" "); mTransformation.printShortString(pw); + pw.println(); + } + if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) { + pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha); + pw.print(" mAlpha="); pw.print(mAlpha); + pw.print(" mLastAlpha="); pw.println(mLastAlpha); + } + if (mHaveMatrix) { + pw.print(prefix); pw.print("mDsDx="); pw.print(mDsDx); + pw.print(" mDtDx="); pw.print(mDtDx); + pw.print(" mDsDy="); pw.print(mDsDy); + pw.print(" mDtDy="); pw.println(mDtDy); + } + pw.print(prefix); pw.print("mDrawPending="); pw.print(mDrawPending); + pw.print(" mCommitDrawPending="); pw.print(mCommitDrawPending); + pw.print(" mReadyToShow="); pw.print(mReadyToShow); + pw.print(" mHasDrawn="); pw.println(mHasDrawn); + if (mExiting || mRemoveOnExit || mDestroying || mRemoved) { + pw.print(prefix); pw.print("mExiting="); pw.print(mExiting); + pw.print(" mRemoveOnExit="); pw.print(mRemoveOnExit); + pw.print(" mDestroying="); pw.print(mDestroying); + pw.print(" mRemoved="); pw.println(mRemoved); + } + if (mOrientationChanging || mAppFreezing || mTurnOnScreen) { + pw.print(prefix); pw.print("mOrientationChanging="); + pw.print(mOrientationChanging); + pw.print(" mAppFreezing="); pw.print(mAppFreezing); + pw.print(" mTurnOnScreen="); pw.println(mTurnOnScreen); + } + if (mHScale != 1 || mVScale != 1) { + pw.print(prefix); pw.print("mHScale="); pw.print(mHScale); + pw.print(" mVScale="); pw.println(mVScale); + } + if (mWallpaperX != -1 || mWallpaperY != -1) { + pw.print(prefix); pw.print("mWallpaperX="); pw.print(mWallpaperX); + pw.print(" mWallpaperY="); pw.println(mWallpaperY); + } + if (mWallpaperXStep != -1 || mWallpaperYStep != -1) { + pw.print(prefix); pw.print("mWallpaperXStep="); pw.print(mWallpaperXStep); + pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep); + } + } + + String makeInputChannelName() { + return Integer.toHexString(System.identityHashCode(this)) + + " " + mAttrs.getTitle(); + } + + @Override + public String toString() { + if (mStringNameCache == null || mLastTitle != mAttrs.getTitle() + || mWasPaused != mToken.paused) { + mLastTitle = mAttrs.getTitle(); + mWasPaused = mToken.paused; + mStringNameCache = "Window{" + Integer.toHexString(System.identityHashCode(this)) + + " " + mLastTitle + " paused=" + mWasPaused + "}"; + } + return mStringNameCache; + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/wm/WindowToken.java b/services/java/com/android/server/wm/WindowToken.java new file mode 100644 index 000000000000..3cd256e85054 --- /dev/null +++ b/services/java/com/android/server/wm/WindowToken.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.os.IBinder; + +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Container of a set of related windows in the window manager. Often this + * is an AppWindowToken, which is the handle for an Activity that it uses + * to display windows. For nested windows, there is a WindowToken created for + * the parent window to manage its children. + */ +class WindowToken { + // The window manager! + final WindowManagerService service; + + // The actual token. + final IBinder token; + + // The type of window this token is for, as per WindowManager.LayoutParams. + final int windowType; + + // Set if this token was explicitly added by a client, so should + // not be removed when all windows are removed. + final boolean explicit; + + // For printing. + String stringName; + + // If this is an AppWindowToken, this is non-null. + AppWindowToken appWindowToken; + + // All of the windows associated with this token. + final ArrayList<WindowState> windows = new ArrayList<WindowState>(); + + // Is key dispatching paused for this token? + boolean paused = false; + + // Should this token's windows be hidden? + boolean hidden; + + // Temporary for finding which tokens no longer have visible windows. + boolean hasVisible; + + // Set to true when this token is in a pending transaction where it + // will be shown. + boolean waitingToShow; + + // Set to true when this token is in a pending transaction where it + // will be hidden. + boolean waitingToHide; + + // Set to true when this token is in a pending transaction where its + // windows will be put to the bottom of the list. + boolean sendingToBottom; + + // Set to true when this token is in a pending transaction where its + // windows will be put to the top of the list. + boolean sendingToTop; + + WindowToken(WindowManagerService _service, IBinder _token, int type, boolean _explicit) { + service = _service; + token = _token; + windowType = type; + explicit = _explicit; + } + + void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("token="); pw.println(token); + pw.print(prefix); pw.print("windows="); pw.println(windows); + pw.print(prefix); pw.print("windowType="); pw.print(windowType); + pw.print(" hidden="); pw.print(hidden); + pw.print(" hasVisible="); pw.println(hasVisible); + if (waitingToShow || waitingToHide || sendingToBottom || sendingToTop) { + pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow); + pw.print(" waitingToHide="); pw.print(waitingToHide); + pw.print(" sendingToBottom="); pw.print(sendingToBottom); + pw.print(" sendingToTop="); pw.println(sendingToTop); + } + } + + @Override + public String toString() { + if (stringName == null) { + StringBuilder sb = new StringBuilder(); + sb.append("WindowToken{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" token="); sb.append(token); sb.append('}'); + stringName = sb.toString(); + } + return stringName; + } +}
\ No newline at end of file diff --git a/services/jni/Android.mk b/services/jni/Android.mk index f5a5b4da5e80..be37d5d56d9b 100644 --- a/services/jni/Android.mk +++ b/services/jni/Android.mk @@ -12,6 +12,7 @@ LOCAL_SRC_FILES:= \ com_android_server_LightsService.cpp \ com_android_server_PowerManagerService.cpp \ com_android_server_SystemServer.cpp \ + com_android_server_UsbService.cpp \ com_android_server_VibratorService.cpp \ com_android_server_location_GpsLocationProvider.cpp \ onload.cpp diff --git a/services/jni/com_android_server_AlarmManagerService.cpp b/services/jni/com_android_server_AlarmManagerService.cpp index 0e162bda1c12..aa8c9b32c5bc 100644 --- a/services/jni/com_android_server_AlarmManagerService.cpp +++ b/services/jni/com_android_server_AlarmManagerService.cpp @@ -33,7 +33,7 @@ #include <errno.h> #include <unistd.h> -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS #include <linux/ioctl.h> #include <linux/android_alarm.h> #endif @@ -42,7 +42,7 @@ namespace android { static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv* env, jobject obj, jint fd, jint minswest) { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS struct timezone tz; tz.tz_minuteswest = minswest; @@ -64,7 +64,7 @@ static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv* env, jo static jint android_server_AlarmManagerService_init(JNIEnv* env, jobject obj) { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS return open("/dev/alarm", O_RDWR); #else return -1; @@ -73,14 +73,14 @@ static jint android_server_AlarmManagerService_init(JNIEnv* env, jobject obj) static void android_server_AlarmManagerService_close(JNIEnv* env, jobject obj, jint fd) { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS close(fd); #endif } static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong seconds, jlong nanoseconds) { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS struct timespec ts; ts.tv_sec = seconds; ts.tv_nsec = nanoseconds; @@ -95,7 +95,7 @@ static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jin static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv* env, jobject obj, jint fd) { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS int result = 0; do diff --git a/services/jni/com_android_server_BatteryService.cpp b/services/jni/com_android_server_BatteryService.cpp index d4513e934331..98d0d92b69e4 100644 --- a/services/jni/com_android_server_BatteryService.cpp +++ b/services/jni/com_android_server_BatteryService.cpp @@ -33,7 +33,7 @@ #include <unistd.h> #include <dirent.h> -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS #include <linux/ioctl.h> #endif diff --git a/services/jni/com_android_server_InputApplication.cpp b/services/jni/com_android_server_InputApplication.cpp index a46a162071e8..e64ec4ee4c8c 100644 --- a/services/jni/com_android_server_InputApplication.cpp +++ b/services/jni/com_android_server_InputApplication.cpp @@ -77,11 +77,11 @@ void android_server_InputApplication_toNative( LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputApplication(JNIEnv* env) { - FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/InputApplication"); + FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/wm/InputApplication"); GET_FIELD_ID(gInputApplicationClassInfo.inputApplicationHandle, gInputApplicationClassInfo.clazz, - "inputApplicationHandle", "Lcom/android/server/InputApplicationHandle;"); + "inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;"); GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz, "name", "Ljava/lang/String;"); diff --git a/services/jni/com_android_server_InputApplicationHandle.cpp b/services/jni/com_android_server_InputApplicationHandle.cpp index ab82635595d8..3a1214ff9655 100644 --- a/services/jni/com_android_server_InputApplicationHandle.cpp +++ b/services/jni/com_android_server_InputApplicationHandle.cpp @@ -106,11 +106,11 @@ static JNINativeMethod gInputApplicationHandleMethods[] = { LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputApplicationHandle(JNIEnv* env) { - int res = jniRegisterNativeMethods(env, "com/android/server/InputApplicationHandle", + int res = jniRegisterNativeMethods(env, "com/android/server/wm/InputApplicationHandle", gInputApplicationHandleMethods, NELEM(gInputApplicationHandleMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); - FIND_CLASS(gInputApplicationHandleClassInfo.clazz, "com/android/server/InputApplicationHandle"); + FIND_CLASS(gInputApplicationHandleClassInfo.clazz, "com/android/server/wm/InputApplicationHandle"); GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, gInputApplicationHandleClassInfo.clazz, "ptr", "I"); diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 5b329bb4eefc..0a50ff8b9bf2 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -87,7 +87,6 @@ static struct { jfieldID mName; jfieldID mSources; jfieldID mKeyboardType; - jfieldID mMotionRanges; } gInputDeviceClassInfo; static struct { @@ -1080,7 +1079,7 @@ static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) static JNINativeMethod gInputManagerMethods[] = { /* name, signature, funcPtr */ - { "nativeInit", "(Lcom/android/server/InputManager$Callbacks;)V", + { "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;)V", (void*) android_server_InputManager_nativeInit }, { "nativeStart", "()V", (void*) android_server_InputManager_nativeStart }, @@ -1097,15 +1096,15 @@ static JNINativeMethod gInputManagerMethods[] = { { "nativeHasKeys", "(II[I[Z)Z", (void*) android_server_InputManager_nativeHasKeys }, { "nativeRegisterInputChannel", - "(Landroid/view/InputChannel;Lcom/android/server/InputWindowHandle;Z)V", + "(Landroid/view/InputChannel;Lcom/android/server/wm/InputWindowHandle;Z)V", (void*) android_server_InputManager_nativeRegisterInputChannel }, { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V", (void*) android_server_InputManager_nativeUnregisterInputChannel }, { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIII)I", (void*) android_server_InputManager_nativeInjectInputEvent }, - { "nativeSetInputWindows", "([Lcom/android/server/InputWindow;)V", + { "nativeSetInputWindows", "([Lcom/android/server/wm/InputWindow;)V", (void*) android_server_InputManager_nativeSetInputWindows }, - { "nativeSetFocusedApplication", "(Lcom/android/server/InputApplication;)V", + { "nativeSetFocusedApplication", "(Lcom/android/server/wm/InputApplication;)V", (void*) android_server_InputManager_nativeSetFocusedApplication }, { "nativeSetInputDispatchMode", "(ZZ)V", (void*) android_server_InputManager_nativeSetInputDispatchMode }, @@ -1135,13 +1134,13 @@ static JNINativeMethod gInputManagerMethods[] = { LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputManager(JNIEnv* env) { - int res = jniRegisterNativeMethods(env, "com/android/server/InputManager", + int res = jniRegisterNativeMethods(env, "com/android/server/wm/InputManager", gInputManagerMethods, NELEM(gInputManagerMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); // Callbacks - FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/InputManager$Callbacks"); + FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/wm/InputManager$Callbacks"); GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, gCallbacksClassInfo.clazz, "notifyConfigurationChanged", "(J)V"); @@ -1150,22 +1149,22 @@ int register_android_server_InputManager(JNIEnv* env) { "notifyLidSwitchChanged", "(JZ)V"); GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz, - "notifyInputChannelBroken", "(Lcom/android/server/InputWindowHandle;)V"); + "notifyInputChannelBroken", "(Lcom/android/server/wm/InputWindowHandle;)V"); GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz, "notifyANR", - "(Lcom/android/server/InputApplicationHandle;Lcom/android/server/InputWindowHandle;)J"); + "(Lcom/android/server/wm/InputApplicationHandle;Lcom/android/server/wm/InputWindowHandle;)J"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz, "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;IZ)I"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz, "interceptKeyBeforeDispatching", - "(Lcom/android/server/InputWindowHandle;Landroid/view/KeyEvent;I)Z"); + "(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Z"); GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, gCallbacksClassInfo.clazz, "dispatchUnhandledKey", - "(Lcom/android/server/InputWindowHandle;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;"); + "(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;"); GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz, "checkInjectEventsPermission", "(II)Z"); @@ -1189,7 +1188,7 @@ int register_android_server_InputManager(JNIEnv* env) { "getPointerLayer", "()I"); GET_METHOD_ID(gCallbacksClassInfo.getPointerIcon, gCallbacksClassInfo.clazz, - "getPointerIcon", "()Lcom/android/server/InputManager$PointerIcon;"); + "getPointerIcon", "()Lcom/android/server/wm/InputManager$PointerIcon;"); // KeyEvent @@ -1221,9 +1220,6 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gInputDeviceClassInfo.mKeyboardType, gInputDeviceClassInfo.clazz, "mKeyboardType", "I"); - GET_FIELD_ID(gInputDeviceClassInfo.mMotionRanges, gInputDeviceClassInfo.clazz, - "mMotionRanges", "[Landroid/view/InputDevice$MotionRange;"); - // Configuration FIND_CLASS(gConfigurationClassInfo.clazz, "android/content/res/Configuration"); @@ -1239,7 +1235,7 @@ int register_android_server_InputManager(JNIEnv* env) { // PointerIcon - FIND_CLASS(gPointerIconClassInfo.clazz, "com/android/server/InputManager$PointerIcon"); + FIND_CLASS(gPointerIconClassInfo.clazz, "com/android/server/wm/InputManager$PointerIcon"); GET_FIELD_ID(gPointerIconClassInfo.bitmap, gPointerIconClassInfo.clazz, "bitmap", "Landroid/graphics/Bitmap;"); diff --git a/services/jni/com_android_server_InputWindow.cpp b/services/jni/com_android_server_InputWindow.cpp index 75154567c149..8548b47de606 100644 --- a/services/jni/com_android_server_InputWindow.cpp +++ b/services/jni/com_android_server_InputWindow.cpp @@ -144,10 +144,10 @@ void android_server_InputWindow_toNative( LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputWindow(JNIEnv* env) { - FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow"); + FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/wm/InputWindow"); GET_FIELD_ID(gInputWindowClassInfo.inputWindowHandle, gInputWindowClassInfo.clazz, - "inputWindowHandle", "Lcom/android/server/InputWindowHandle;"); + "inputWindowHandle", "Lcom/android/server/wm/InputWindowHandle;"); GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz, "inputChannel", "Landroid/view/InputChannel;"); diff --git a/services/jni/com_android_server_InputWindowHandle.cpp b/services/jni/com_android_server_InputWindowHandle.cpp index 4d66212087ca..5b74e435c7ee 100644 --- a/services/jni/com_android_server_InputWindowHandle.cpp +++ b/services/jni/com_android_server_InputWindowHandle.cpp @@ -116,18 +116,18 @@ static JNINativeMethod gInputWindowHandleMethods[] = { LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputWindowHandle(JNIEnv* env) { - int res = jniRegisterNativeMethods(env, "com/android/server/InputWindowHandle", + int res = jniRegisterNativeMethods(env, "com/android/server/wm/InputWindowHandle", gInputWindowHandleMethods, NELEM(gInputWindowHandleMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); - FIND_CLASS(gInputWindowHandleClassInfo.clazz, "com/android/server/InputWindowHandle"); + FIND_CLASS(gInputWindowHandleClassInfo.clazz, "com/android/server/wm/InputWindowHandle"); GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, gInputWindowHandleClassInfo.clazz, "ptr", "I"); GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle, gInputWindowHandleClassInfo.clazz, - "inputApplicationHandle", "Lcom/android/server/InputApplicationHandle;"); + "inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;"); return 0; } diff --git a/services/jni/com_android_server_UsbService.cpp b/services/jni/com_android_server_UsbService.cpp new file mode 100644 index 000000000000..192daafa1b2d --- /dev/null +++ b/services/jni/com_android_server_UsbService.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "UsbService" +#include "utils/Log.h" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" +#include "utils/Vector.h" + +#include <usbhost/usbhost.h> + +#include <stdio.h> +#include <asm/byteorder.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/usb/f_accessory.h> + +#define DRIVER_NAME "/dev/usb_accessory" + +namespace android +{ + +static struct file_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; + jfieldID mDescriptor; +} gFileDescriptorOffsets; + +static struct parcel_file_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; +} gParcelFileDescriptorOffsets; + +static jmethodID method_usbDeviceAdded; +static jmethodID method_usbDeviceRemoved; + +static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + LOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + } +} + +static int usb_device_added(const char *devname, void* client_data) { + struct usb_descriptor_header* desc; + struct usb_descriptor_iter iter; + + struct usb_device *device = usb_device_open(devname); + if (!device) { + LOGE("usb_device_open failed\n"); + return 0; + } + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject thiz = (jobject)client_data; + Vector<int> interfaceValues; + Vector<int> endpointValues; + const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device); + + uint16_t vendorId = usb_device_get_vendor_id(device); + uint16_t productId = usb_device_get_product_id(device); + uint8_t deviceClass = deviceDesc->bDeviceClass; + uint8_t deviceSubClass = deviceDesc->bDeviceSubClass; + uint8_t protocol = deviceDesc->bDeviceProtocol; + + usb_descriptor_iter_init(device, &iter); + + while ((desc = usb_descriptor_iter_next(&iter)) != NULL) { + if (desc->bDescriptorType == USB_DT_INTERFACE) { + struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc; + + // push class, subclass, protocol and number of endpoints into interfaceValues vector + interfaceValues.add(interface->bInterfaceNumber); + interfaceValues.add(interface->bInterfaceClass); + interfaceValues.add(interface->bInterfaceSubClass); + interfaceValues.add(interface->bInterfaceProtocol); + interfaceValues.add(interface->bNumEndpoints); + } else if (desc->bDescriptorType == USB_DT_ENDPOINT) { + struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc; + + // push address, attributes, max packet size and interval into endpointValues vector + endpointValues.add(endpoint->bEndpointAddress); + endpointValues.add(endpoint->bmAttributes); + endpointValues.add(__le16_to_cpu(endpoint->wMaxPacketSize)); + endpointValues.add(endpoint->bInterval); + } + } + + usb_device_close(device); + + // handle generic device notification + int length = interfaceValues.size(); + jintArray interfaceArray = env->NewIntArray(length); + env->SetIntArrayRegion(interfaceArray, 0, length, interfaceValues.array()); + + length = endpointValues.size(); + jintArray endpointArray = env->NewIntArray(length); + env->SetIntArrayRegion(endpointArray, 0, length, endpointValues.array()); + + env->CallVoidMethod(thiz, method_usbDeviceAdded, + env->NewStringUTF(devname), vendorId, productId, deviceClass, + deviceSubClass, protocol, interfaceArray, endpointArray); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + + return 0; +} + +static int usb_device_removed(const char *devname, void* client_data) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject thiz = (jobject)client_data; + + env->CallVoidMethod(thiz, method_usbDeviceRemoved, env->NewStringUTF(devname)); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return 0; +} + +static void android_server_UsbService_monitorUsbHostBus(JNIEnv *env, jobject thiz) +{ + struct usb_host_context* context = usb_host_init(); + if (!context) { + LOGE("usb_host_init failed"); + return; + } + // this will never return so it is safe to pass thiz directly + usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz); +} + +static jobject android_server_UsbService_openDevice(JNIEnv *env, jobject thiz, jstring deviceName) +{ + const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL); + struct usb_device* device = usb_device_open(deviceNameStr); + env->ReleaseStringUTFChars(deviceName, deviceNameStr); + + if (!device) + return NULL; + + int fd = usb_device_get_fd(device); + if (fd < 0) + return NULL; + int newFD = dup(fd); + usb_device_close(device); + + jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass, + gFileDescriptorOffsets.mConstructor); + if (fileDescriptor != NULL) { + env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, newFD); + } else { + return NULL; + } + return env->NewObject(gParcelFileDescriptorOffsets.mClass, + gParcelFileDescriptorOffsets.mConstructor, fileDescriptor); +} + +static void set_accessory_string(JNIEnv *env, int fd, int cmd, jobjectArray strArray, int index) +{ + char buffer[256]; + + buffer[0] = 0; + int length = ioctl(fd, cmd, buffer); + LOGD("ioctl returned %d", length); + if (buffer[0]) { + jstring obj = env->NewStringUTF(buffer); + env->SetObjectArrayElement(strArray, index, obj); + env->DeleteLocalRef(obj); + } +} + + +static jobjectArray android_server_UsbService_getAccessoryStrings(JNIEnv *env, jobject thiz) +{ + int fd = open(DRIVER_NAME, O_RDWR); + if (fd < 0) { + LOGE("could not open %s", DRIVER_NAME); + return NULL; + } + jclass stringClass = env->FindClass("java/lang/String"); + jobjectArray strArray = env->NewObjectArray(4, stringClass, NULL); + if (!strArray) goto out; + set_accessory_string(env, fd, ACCESSORY_GET_STRING_MANUFACTURER, strArray, 0); + set_accessory_string(env, fd, ACCESSORY_GET_STRING_MODEL, strArray, 1); + set_accessory_string(env, fd, ACCESSORY_GET_STRING_TYPE, strArray, 2); + set_accessory_string(env, fd, ACCESSORY_GET_STRING_VERSION, strArray, 3); + +out: + close(fd); + return strArray; +} + +static jobject android_server_UsbService_openAccessory(JNIEnv *env, jobject thiz) +{ + int fd = open(DRIVER_NAME, O_RDWR); + if (fd < 0) { + LOGE("could not open %s", DRIVER_NAME); + return NULL; + } + jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass, + gFileDescriptorOffsets.mConstructor); + if (fileDescriptor != NULL) { + env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, fd); + } else { + return NULL; + } + return env->NewObject(gParcelFileDescriptorOffsets.mClass, + gParcelFileDescriptorOffsets.mConstructor, fileDescriptor); +} + +static JNINativeMethod method_table[] = { + { "monitorUsbHostBus", "()V", (void*)android_server_UsbService_monitorUsbHostBus }, + { "nativeOpenDevice", "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", + (void*)android_server_UsbService_openDevice }, + { "nativeGetAccessoryStrings", "()[Ljava/lang/String;", + (void*)android_server_UsbService_getAccessoryStrings }, + { "nativeOpenAccessory","()Landroid/os/ParcelFileDescriptor;", + (void*)android_server_UsbService_openAccessory }, +}; + +int register_android_server_UsbService(JNIEnv *env) +{ + jclass clazz = env->FindClass("com/android/server/UsbService"); + if (clazz == NULL) { + LOGE("Can't find com/android/server/UsbService"); + return -1; + } + method_usbDeviceAdded = env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;IIIII[I[I)V"); + if (method_usbDeviceAdded == NULL) { + LOGE("Can't find usbDeviceAdded"); + return -1; + } + method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved", "(Ljava/lang/String;)V"); + if (method_usbDeviceRemoved == NULL) { + LOGE("Can't find usbDeviceRemoved"); + return -1; + } + + clazz = env->FindClass("java/io/FileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); + gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V"); + gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I"); + LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL, + "Unable to find descriptor field in java.io.FileDescriptor"); + + clazz = env->FindClass("android/os/ParcelFileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor"); + gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V"); + LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL, + "Unable to find constructor for android.os.ParcelFileDescriptor"); + + return jniRegisterNativeMethods(env, "com/android/server/UsbService", + method_table, NELEM(method_table)); +} + +}; diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp index bdd6d808ac42..37b520bf8d8d 100644 --- a/services/jni/onload.cpp +++ b/services/jni/onload.cpp @@ -13,6 +13,7 @@ int register_android_server_InputWindowHandle(JNIEnv* env); int register_android_server_InputManager(JNIEnv* env); int register_android_server_LightsService(JNIEnv* env); int register_android_server_PowerManagerService(JNIEnv* env); +int register_android_server_UsbService(JNIEnv* env); int register_android_server_VibratorService(JNIEnv* env); int register_android_server_SystemServer(JNIEnv* env); int register_android_server_location_GpsLocationProvider(JNIEnv* env); @@ -40,6 +41,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) register_android_server_LightsService(env); register_android_server_AlarmManagerService(env); register_android_server_BatteryService(env); + register_android_server_UsbService(env); register_android_server_VibratorService(env); register_android_server_SystemServer(env); register_android_server_location_GpsLocationProvider(env); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index f64fd7b59bbe..0f7d6392e496 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -56,6 +56,8 @@ Layer::Layer(SurfaceFlinger* flinger, mNeedsBlending(true), mNeedsDithering(false), mSecure(false), + mProtectedByApp(false), + mProtectedByDRM(false), mTextureManager(), mBufferManager(mTextureManager), mWidth(0), mHeight(0), mNeedsScaling(false), mFixedSize(false) @@ -140,7 +142,8 @@ void Layer::onRemoved() sp<LayerBaseClient::Surface> Layer::createSurface() const { - return mSurface; + sp<Surface> sur(new SurfaceLayer(mFlinger, const_cast<Layer *>(this))); + return sur; } status_t Layer::ditch() @@ -150,9 +153,6 @@ status_t Layer::ditch() // the layer is not on screen anymore. free as much resources as possible mFreezeLock.clear(); - // Free our own reference to ISurface - mSurface.clear(); - Mutex::Autolock _l(mLock); mWidth = mHeight = 0; return NO_ERROR; @@ -190,6 +190,8 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h, mReqHeight = h; mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; + mProtectedByApp = (flags & ISurfaceComposer::eProtectedByApp) ? true : false; + mProtectedByDRM = (flags & ISurfaceComposer::eProtectedByDRM) ? true : false; mNeedsBlending = (info.h_alpha - info.l_alpha) > 0 && (flags & ISurfaceComposer::eOpaque) == 0; @@ -198,7 +200,6 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h, int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED); mNeedsDithering = layerRedsize > displayRedSize; - mSurface = new SurfaceLayer(mFlinger, this); return NO_ERROR; } @@ -475,6 +476,10 @@ uint32_t Layer::getEffectiveUsage(uint32_t usage) const // request EGLImage for all buffers usage |= GraphicBuffer::USAGE_HW_TEXTURE; } + if (mProtectedByApp || mProtectedByDRM) { + // need a hardware-protected path to external video sink + usage |= GraphicBuffer::USAGE_PROTECTED; + } return usage; } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 290811982eac..2b3841466d70 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -79,6 +79,8 @@ public: virtual bool needsDithering() const { return mNeedsDithering; } virtual bool needsFiltering() const; virtual bool isSecure() const { return mSecure; } + virtual bool isProtectedByApp() const { return mProtectedByApp; } + virtual bool isProtectedByDRM() const { return mProtectedByDRM; } virtual sp<Surface> createSurface() const; virtual status_t ditch(); virtual void onRemoved(); @@ -211,14 +213,15 @@ private: ClientRef mUserClientRef; // constants - sp<Surface> mSurface; PixelFormat mFormat; const GLExtensions& mGLExtensions; bool mNeedsBlending; bool mNeedsDithering; // page-flip thread (currently main thread) - bool mSecure; + bool mSecure; // no screenshots + bool mProtectedByApp; // application requires protected path to external sink + bool mProtectedByDRM; // DRM agent requires protected path to external sink Region mPostedDirtyRegion; // page-flip thread and transaction thread (currently main thread) diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp index 86057f8cd5ed..6025ed4921cb 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -540,7 +540,9 @@ int32_t LayerBaseClient::sIdentity = 1; LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client) - : LayerBase(flinger, display), mClientRef(client), + : LayerBase(flinger, display), + mHasSurface(false), + mClientRef(client), mIdentity(uint32_t(android_atomic_inc(&sIdentity))) { } @@ -557,12 +559,13 @@ sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() { sp<Surface> s; Mutex::Autolock _l(mLock); - s = mClientSurface.promote(); - if (s == 0) { - s = createSurface(); - mClientSurface = s; - mClientSurfaceBinder = s->asBinder(); - } + + LOG_ALWAYS_FATAL_IF(mHasSurface, + "LayerBaseClient::getSurface() has already been called"); + + mHasSurface = true; + s = createSurface(); + mClientSurfaceBinder = s->asBinder(); return s; } diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h index 8ed474972b94..bfe92e63ddd6 100644 --- a/services/surfaceflinger/LayerBase.h +++ b/services/surfaceflinger/LayerBase.h @@ -196,6 +196,18 @@ public: */ virtual bool isSecure() const { return false; } + /** + * isProtectedByApp - true if application says this surface is protected, that + * is if it requires a hardware-protected data path to an external sink. + */ + virtual bool isProtectedByApp() const { return false; } + + /** + * isProtectedByDRM - true if DRM agent says this surface is protected, that + * is if it requires a hardware-protected data path to an external sink. + */ + virtual bool isProtectedByDRM() const { return false; } + /** Called from the main thread, when the surface is removed from the * draw list */ virtual status_t ditch() { return NO_ERROR; } @@ -325,7 +337,7 @@ protected: private: mutable Mutex mLock; - mutable wp<Surface> mClientSurface; + mutable bool mHasSurface; wp<IBinder> mClientSurfaceBinder; const wp<Client> mClientRef; // only read diff --git a/services/surfaceflinger/LayerDim.h b/services/surfaceflinger/LayerDim.h index 5631c0a417b5..75f9a8928287 100644 --- a/services/surfaceflinger/LayerDim.h +++ b/services/surfaceflinger/LayerDim.h @@ -37,8 +37,10 @@ public: virtual ~LayerDim(); virtual void onDraw(const Region& clip) const; - virtual bool needsBlending() const { return true; } - virtual bool isSecure() const { return false; } + virtual bool needsBlending() const { return true; } + virtual bool isSecure() const { return false; } + virtual bool isProtectedByApp() const { return false; } + virtual bool isProtectedByDRM() const { return false; } virtual const char* getTypeId() const { return "LayerDim"; } }; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 821e39f4dd52..23b67e3f27fa 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -427,6 +427,9 @@ public class TelephonyManager { public static final int NETWORK_TYPE_LTE = 13; /** Current network is eHRPD */ public static final int NETWORK_TYPE_EHRPD = 14; + /** Current network is HSPA+ + * @hide */ + public static final int NETWORK_TYPE_HSPAP = 15; /** * Returns a constant indicating the radio technology (network type) diff --git a/telephony/java/com/android/internal/telephony/ApnSetting.java b/telephony/java/com/android/internal/telephony/ApnSetting.java index 5af8e355ae57..20dbaf3a2353 100644 --- a/telephony/java/com/android/internal/telephony/ApnSetting.java +++ b/telephony/java/com/android/internal/telephony/ApnSetting.java @@ -21,6 +21,8 @@ package com.android.internal.telephony; */ public class ApnSetting { + static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*"; + public String carrier; public String apn; public String proxy; @@ -34,11 +36,14 @@ public class ApnSetting { public String[] types; public int id; public String numeric; + public String protocol; + public String roamingProtocol; - - public ApnSetting(int id, String numeric, String carrier, String apn, String proxy, String port, + public ApnSetting(int id, String numeric, String carrier, String apn, + String proxy, String port, String mmsc, String mmsProxy, String mmsPort, - String user, String password, int authType, String[] types) { + String user, String password, int authType, String[] types, + String protocol, String roamingProtocol) { this.id = id; this.numeric = numeric; this.carrier = carrier; @@ -52,40 +57,81 @@ public class ApnSetting { this.password = password; this.authType = authType; this.types = types; + this.protocol = protocol; + this.roamingProtocol = roamingProtocol; } - // data[0] = name - // data[1] = apn - // data[2] = proxy - // data[3] = port - // data[4] = username - // data[5] = password - // data[6] = server - // data[7] = mmsc - // data[8] = mmsproxy - // data[9] = mmsport - // data[10] = mcc - // data[11] = mnc - // data[12] = auth - // data[13] = first type... + /** + * Creates an ApnSetting object from a string. + * + * @param data the string to read. + * + * The string must be in one of two formats (newlines added for clarity, + * spaces are optional): + * + * v1 format: + * <carrier>, <apn>, <proxy>, <port>, <mmsc>, <mmsproxy>, + * <mmsport>, <user>, <password>, <authtype>, <mcc>,<mnc>, + * <type>[, <type>...] + * + * v2 format: + * [ApnSettingV2] <carrier>, <apn>, <proxy>, <port>, <mmsc>, <mmsproxy>, + * <mmsport>, <user>, <password, <authtype>, <mcc>, <mnc>, + * <type>[| <type>...], <protocol>, <roaming_protocol> + * + * Note that the strings generated by toString() do not contain the username + * and password and thus cannot be read by this method. + * + * @see ApnSettingTest + */ public static ApnSetting fromString(String data) { if (data == null) return null; + + int version; + // matches() operates on the whole string, so append .* to the regex. + if (data.matches(V2_FORMAT_REGEX + ".*")) { + version = 2; + data = data.replaceFirst(V2_FORMAT_REGEX, ""); + } else { + version = 1; + } + String[] a = data.split("\\s*,\\s*"); - if (a.length < 14) return null; - int authType = 0; + if (a.length < 14) { + return null; + } + + int authType; try { authType = Integer.parseInt(a[12]); } catch (Exception e) { + authType = 0; } - String[] typeArray = new String[a.length - 13]; - System.arraycopy(a, 13, typeArray, 0, a.length - 13); + + String[] typeArray; + String protocol, roamingProtocol; + if (version == 1) { + typeArray = new String[a.length - 13]; + System.arraycopy(a, 13, typeArray, 0, a.length - 13); + protocol = RILConstants.SETUP_DATA_PROTOCOL_IP; + roamingProtocol = RILConstants.SETUP_DATA_PROTOCOL_IP; + } else { + if (a.length < 16) { + return null; + } + typeArray = a[13].split("\\s*\\|\\s*"); + protocol = a[14]; + roamingProtocol = a[15]; + } + return new ApnSetting(-1,a[10]+a[11],a[0],a[1],a[2],a[3],a[7],a[8], - a[9],a[4],a[5],authType,typeArray); + a[9],a[4],a[5],authType,typeArray,protocol,roamingProtocol); } public String toString() { StringBuilder sb = new StringBuilder(); - sb.append(carrier) + sb.append("[ApnSettingV2] ") + .append(carrier) .append(", ").append(id) .append(", ").append(numeric) .append(", ").append(apn) @@ -94,10 +140,15 @@ public class ApnSetting { .append(", ").append(mmsProxy) .append(", ").append(mmsPort) .append(", ").append(port) - .append(", ").append(authType); - for (String t : types) { - sb.append(", ").append(t); + .append(", ").append(authType).append(", "); + for (int i = 0; i < types.length; i++) { + sb.append(types[i]); + if (i < types.length - 1) { + sb.append(" | "); + } } + sb.append(", ").append(protocol); + sb.append(", ").append(roamingProtocol); return sb.toString(); } diff --git a/telephony/java/com/android/internal/telephony/DataCallState.java b/telephony/java/com/android/internal/telephony/DataCallState.java index d0f3d24f53b3..df12153eba75 100644 --- a/telephony/java/com/android/internal/telephony/DataCallState.java +++ b/telephony/java/com/android/internal/telephony/DataCallState.java @@ -17,16 +17,43 @@ package com.android.internal.telephony; +/** + * This is RIL_Data_Call_Response_v5 from ril.h + * TODO: Rename to DataCallResponse. + */ public class DataCallState { - public int cid; - public int active; - public String type; - public String apn; - public String address; + public int version = 0; + public int status = 0; + public int cid = 0; + public int active = 0; + public String type = ""; + public String ifname = ""; + public String [] addresses = new String[0]; + public String [] dnses = new String[0]; @Override public String toString() { - return "DataCallState: {" + " cid: " + cid + ", active: " + active + ", type: " + type - + ", apn: " + apn + ", address: " + address + " }"; + StringBuffer sb = new StringBuffer(); + sb.append("DataCallState: {") + .append("version=").append(version) + .append(" status=").append(status) + .append(" cid=").append(cid) + .append(" active=").append(active) + .append(" type=").append(type) + .append("' ifname='").append(ifname); + sb.append("' addresses=["); + for (String addr : addresses) { + sb.append(addr); + sb.append(","); + } + if (addresses.length > 0) sb.deleteCharAt(sb.length()-1); + sb.append("] dnses=["); + for (String addr : dnses) { + sb.append(addr); + sb.append(","); + } + if (dnses.length > 0) sb.deleteCharAt(sb.length()-1); + sb.append("]}"); + return sb.toString(); } } diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java index a3d3781d085a..aa9c4a37c08d 100644 --- a/telephony/java/com/android/internal/telephony/DataConnection.java +++ b/telephony/java/com/android/internal/telephony/DataConnection.java @@ -27,14 +27,11 @@ import android.os.AsyncResult; import android.os.Message; import android.os.SystemProperties; import android.text.TextUtils; -import android.util.EventLog; import java.net.InetAddress; import java.net.Inet4Address; -import java.net.InterfaceAddress; -import java.net.NetworkInterface; -import java.net.SocketException; import java.net.UnknownHostException; +import java.util.HashMap; /** * {@hide} @@ -56,48 +53,6 @@ import java.net.UnknownHostException; * <code>AsyncResult.exception = new Exception()</code>. * * The other public methods are provided for debugging. - * - * Below is the state machine description for this class. - * - * DataConnection { - * + mDefaultState { - * EVENT_RESET { clearSettings, notifiyDisconnectCompleted, >mInactiveState }. - * EVENT_CONNECT { notifyConnectCompleted(FailCause.UNKNOWN) }. - * EVENT_DISCONNECT { notifyDisconnectCompleted }. - * - * // Ignored messages - * EVENT_SETUP_DATA_CONNECTION_DONE, - * EVENT_GET_LAST_FAIL_DONE, - * EVENT_DEACTIVATE_DONE. - * } - * ++ # mInactiveState - * e(doNotifications) - * x(clearNotifications) { - * EVENT_RESET { notifiyDisconnectCompleted }. - * EVENT_CONNECT {startConnecting, >mActivatingState }. - * } - * ++ mActivatingState { - * EVENT_DISCONNECT { %EVENT_DISCONNECT }. - * EVENT_SETUP_DATA_CONNECTION_DONE { - * if (SUCCESS) { notifyConnectCompleted(FailCause.NONE), >mActiveState }. - * if (ERR_BadCommand) { - * notifyConnectCompleted(FailCause.UNKNOWN), >mInactiveState }. - * if (ERR_BadDns) { tearDownData($DEACTIVATE_DONE), >mDisconnectingBadDnsState }. - * if (ERR_Other) { getLastDataCallFailCause($EVENT_GET_LAST_FAIL_DONE) }. - * if (ERR_Stale) {}. - * } - * EVENT_GET_LAST_FAIL_DONE { notifyConnectCompleted(result), >mInactive }. - * } - * ++ mActiveState { - * EVENT_DISCONNECT { tearDownData($EVENT_DEACTIVATE_DONE), >mDisconnecting }. - * } - * ++ mDisconnectingState { - * EVENT_DEACTIVATE_DONE { notifyDisconnectCompleted, >mInactiveState }. - * } - * ++ mDisconnectingBadDnsState { - * EVENT_DEACTIVATE_DONE { notifyConnectComplete(FailCause.UNKNOWN), >mInactiveState }. - * } - * } */ public abstract class DataConnection extends HierarchicalStateMachine { protected static final boolean DBG = true; @@ -109,24 +64,22 @@ public abstract class DataConnection extends HierarchicalStateMachine { * Class returned by onSetupConnectionCompleted. */ protected enum SetupResult { + SUCCESS, ERR_BadCommand, - ERR_BadDns, - ERR_Other, + ERR_UnacceptableParameter, + ERR_GetLastErrorFromRil, ERR_Stale, - SUCCESS; + ERR_RilError; public FailCause mFailCause; + SetupResult() { + mFailCause = FailCause.fromInt(0); + } + @Override public String toString() { - switch (this) { - case ERR_BadCommand: return "Bad Command"; - case ERR_BadDns: return "Bad DNS"; - case ERR_Other: return "Other error"; - case ERR_Stale: return "Stale command"; - case SUCCESS: return "SUCCESS"; - default: return "unknown"; - } + return name() + " SetupResult.mFailCause=" + mFailCause; } } @@ -168,31 +121,66 @@ public abstract class DataConnection extends HierarchicalStateMachine { } /** - * Returned as the reason for a connection failure. + * Returned as the reason for a connection failure as defined + * by RIL_DataCallFailCause in ril.h and some local errors. */ public enum FailCause { - NONE, - OPERATOR_BARRED, - INSUFFICIENT_RESOURCES, - MISSING_UNKNOWN_APN, - UNKNOWN_PDP_ADDRESS, - USER_AUTHENTICATION, - ACTIVATION_REJECT_GGSN, - ACTIVATION_REJECT_UNSPECIFIED, - SERVICE_OPTION_NOT_SUPPORTED, - SERVICE_OPTION_NOT_SUBSCRIBED, - SERVICE_OPTION_OUT_OF_ORDER, - NSAPI_IN_USE, - PROTOCOL_ERRORS, - REGISTRATION_FAIL, - GPRS_REGISTRATION_FAIL, - UNKNOWN, - - RADIO_NOT_AVAILABLE; + NONE(0), + + // This series of errors as specified by the standards + // specified in ril.h + OPERATOR_BARRED(0x08), + INSUFFICIENT_RESOURCES(0x1A), + MISSING_UNKNOWN_APN(0x1B), + UNKNOWN_PDP_ADDRESS_TYPE(0x1C), + USER_AUTHENTICATION(0x1D), + ACTIVATION_REJECT_GGSN(0x1E), + ACTIVATION_REJECT_UNSPECIFIED(0x1F), + SERVICE_OPTION_NOT_SUPPORTED(0x20), + SERVICE_OPTION_NOT_SUBSCRIBED(0x21), + SERVICE_OPTION_OUT_OF_ORDER(0x22), + NSAPI_IN_USE(0x23), + ONLY_IPV4_ALLOWED(0x32), + ONLY_IPV6_ALLOWED(0x33), + ONLY_SINGLE_BEARER_ALLOWED(0x34), + PROTOCOL_ERRORS(0x6F), + + // Local errors generated by Vendor RIL + // specified in ril.h + REGISTRATION_FAIL(-1), + GPRS_REGISTRATION_FAIL(-2), + SIGNAL_LOST(-3), + PREF_RADIO_TECH_CHANGED(-4), + RADIO_POWER_OFF(-5), + TETHERED_CALL_ACTIVE(-6), + ERROR_UNSPECIFIED(0xFFFF), + + // Errors generated by the Framework + // specified here + UNKNOWN(0x10000), + RADIO_NOT_AVAILABLE(0x10001), + UNACCEPTABLE_NETWORK_PARAMETER(0x10002); + + private final int mErrorCode; + private static final HashMap<Integer, FailCause> sErrorCodeToFailCauseMap; + static { + sErrorCodeToFailCauseMap = new HashMap<Integer, FailCause>(); + for (FailCause fc : values()) { + sErrorCodeToFailCauseMap.put(fc.ordinal(), fc); + } + } + + FailCause(int errorCode) { + mErrorCode = errorCode; + } + + int getErrorCode() { + return mErrorCode; + } public boolean isPermanentFail() { return (this == OPERATOR_BARRED) || (this == MISSING_UNKNOWN_APN) || - (this == UNKNOWN_PDP_ADDRESS) || (this == USER_AUTHENTICATION) || + (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) || (this == SERVICE_OPTION_NOT_SUPPORTED) || (this == SERVICE_OPTION_NOT_SUBSCRIBED) || (this == NSAPI_IN_USE) || (this == PROTOCOL_ERRORS); @@ -200,52 +188,21 @@ public abstract class DataConnection extends HierarchicalStateMachine { public boolean isEventLoggable() { return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) || - (this == UNKNOWN_PDP_ADDRESS) || (this == USER_AUTHENTICATION) || + (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) || (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) || (this == SERVICE_OPTION_NOT_SUBSCRIBED) || (this == SERVICE_OPTION_NOT_SUPPORTED) || (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) || - (this == PROTOCOL_ERRORS); + (this == PROTOCOL_ERRORS) || + (this == UNACCEPTABLE_NETWORK_PARAMETER); } - @Override - public String toString() { - switch (this) { - case NONE: - return "No Error"; - case OPERATOR_BARRED: - return "Operator Barred"; - case INSUFFICIENT_RESOURCES: - return "Insufficient Resources"; - case MISSING_UNKNOWN_APN: - return "Missing / Unknown APN"; - case UNKNOWN_PDP_ADDRESS: - return "Unknown PDP Address"; - case USER_AUTHENTICATION: - return "Error User Authentication"; - case ACTIVATION_REJECT_GGSN: - return "Activation Reject GGSN"; - case ACTIVATION_REJECT_UNSPECIFIED: - return "Activation Reject unspecified"; - case SERVICE_OPTION_NOT_SUPPORTED: - return "Data Not Supported"; - case SERVICE_OPTION_NOT_SUBSCRIBED: - return "Data Not subscribed"; - case SERVICE_OPTION_OUT_OF_ORDER: - return "Data Services Out of Order"; - case NSAPI_IN_USE: - return "NSAPI in use"; - case PROTOCOL_ERRORS: - return "Protocol Errors"; - case REGISTRATION_FAIL: - return "Network Registration Failure"; - case GPRS_REGISTRATION_FAIL: - return "Data Network Registration Failure"; - case RADIO_NOT_AVAILABLE: - return "Radio Not Available"; - default: - return "Unknown Data Error"; + public static FailCause fromInt(int errorCode) { + FailCause fc = sErrorCodeToFailCauseMap.get(errorCode); + if (fc == null) { + fc = UNKNOWN; } + return fc; } } @@ -275,12 +232,11 @@ public abstract class DataConnection extends HierarchicalStateMachine { Object userData; //***** Abstract methods + @Override public abstract String toString(); protected abstract void onConnect(ConnectionParams cp); - protected abstract FailCause getFailCauseFromRequest(int rilCause); - protected abstract boolean isDnsOk(String[] domainNameServers); protected abstract void log(String s); @@ -301,7 +257,7 @@ public abstract class DataConnection extends HierarchicalStateMachine { addState(mActivatingState, mDefaultState); addState(mActiveState, mDefaultState); addState(mDisconnectingState, mDefaultState); - addState(mDisconnectingBadDnsState, mDefaultState); + addState(mDisconnectingErrorCreatingConnection, mDefaultState); setInitialState(mInactiveState); if (DBG) log("DataConnection constructor X"); } @@ -409,20 +365,26 @@ public abstract class DataConnection extends HierarchicalStateMachine { * @return SetupResult. */ private SetupResult onSetupConnectionCompleted(AsyncResult ar) { - SetupResult result; - String[] response = ((String[]) ar.result); + DataCallState response = (DataCallState) ar.result; ConnectionParams cp = (ConnectionParams) ar.userObj; + SetupResult result; if (ar.exception != null) { - if (DBG) log("DataConnection Init failed " + ar.exception); + if (DBG) { + log("onSetupConnectionCompleted failed, ar.exception=" + ar.exception + + " response=" + response); + } if (ar.exception instanceof CommandException && ((CommandException) (ar.exception)).getCommandError() == CommandException.Error.RADIO_NOT_AVAILABLE) { result = SetupResult.ERR_BadCommand; result.mFailCause = FailCause.RADIO_NOT_AVAILABLE; + } else if ((response == null) || (response.version < 4)) { + result = SetupResult.ERR_GetLastErrorFromRil; } else { - result = SetupResult.ERR_Other; + result = SetupResult.ERR_RilError; + result.mFailCause = FailCause.fromInt(response.status); } } else if (cp.tag != mTag) { if (DBG) { @@ -430,36 +392,21 @@ public abstract class DataConnection extends HierarchicalStateMachine { } result = SetupResult.ERR_Stale; } else { -// log("onSetupConnectionCompleted received " + response.length + " response strings:"); -// for (int i = 0; i < response.length; i++) { -// log(" response[" + i + "]='" + response[i] + "'"); -// } + log("onSetupConnectionCompleted received DataCallState: " + response); // Start with clean network properties and if we have // a failure we'll clear again at the bottom of this code. LinkProperties linkProperties = new LinkProperties(); - if (response.length >= 2) { - cid = Integer.parseInt(response[0]); - String interfaceName = response[1]; - result = SetupResult.SUCCESS; - + if (response.status == FailCause.NONE.getErrorCode()) { try { - String prefix = "net." + interfaceName + "."; - - NetworkInterface networkInterface = NetworkInterface.getByName(interfaceName); - linkProperties.setInterfaceName(interfaceName); - - if (response.length >= 5) { - log("response.length >=5 using response for ip='" + response[2] + - "' dns='" + response[3] + "' gateway='" + response[4] + "'"); - String [] addresses = response[2].split(" "); - String [] dnses = response[3].split(" "); - String gateway = response[4]; - for (String addr : addresses) { + cid = response.cid; + linkProperties.setInterfaceName(response.ifname); + if (response.addresses != null && response.addresses.length > 0) { + for (String addr : response.addresses) { LinkAddress la; if (!InetAddress.isNumeric(addr)) { - throw new RuntimeException( - "Vendor ril bug: Non-numeric ip addr=" + addr); + EventLogTags.writeBadIpAddress(addr); + throw new UnknownHostException("Non-numeric ip addr=" + addr); } InetAddress ia = InetAddress.getByName(addr); if (ia instanceof Inet4Address) { @@ -469,73 +416,73 @@ public abstract class DataConnection extends HierarchicalStateMachine { } linkProperties.addLinkAddress(la); } - - if (dnses.length != 0) { - for (String addr : dnses) { - if (!InetAddress.isNumeric(addr)) { - throw new RuntimeException( - "Vendor ril bug: Non-numeric dns addr=" + addr); - } - InetAddress ia = InetAddress.getByName(addr); - linkProperties.addDns(ia); + } else { + EventLogTags.writeBadIpAddress("no address for ifname=" + response.ifname); + throw new UnknownHostException("no address for ifname=" + response.ifname); + } + if (response.dnses != null && response.dnses.length > 0) { + for (String addr : response.dnses) { + if (!InetAddress.isNumeric(addr)) { + EventLogTags.writePdpBadDnsAddress("dns=" + addr); + throw new UnknownHostException("Non-numeric dns addr=" + addr); } - result = SetupResult.SUCCESS; - } else { - result = SetupResult.ERR_BadDns; - } - - if (!InetAddress.isNumeric(gateway)) { - throw new RuntimeException( - "Vendor ril bug: Non-numeric gateway addr=" + gateway); + InetAddress ia = InetAddress.getByName(addr); + linkProperties.addDns(ia); } - linkProperties.setGateway(InetAddress.getByName(gateway)); - + result = SetupResult.SUCCESS; } else { - log("response.length < 5 using properties for dns and gateway"); - for (InterfaceAddress addr : networkInterface.getInterfaceAddresses()) { - linkProperties.addLinkAddress(new LinkAddress(addr)); - } - - String gatewayAddress = SystemProperties.get(prefix + "gw"); - linkProperties.setGateway(InetAddress.getByName(gatewayAddress)); + String prefix = "net." + response.ifname + "."; String dnsServers[] = new String[2]; dnsServers[0] = SystemProperties.get(prefix + "dns1"); dnsServers[1] = SystemProperties.get(prefix + "dns2"); if (isDnsOk(dnsServers)) { - linkProperties.addDns(InetAddress.getByName(dnsServers[0])); - linkProperties.addDns(InetAddress.getByName(dnsServers[1])); + for (String dnsAddr : dnsServers) { + if (!InetAddress.isNumeric(dnsAddr)) { + EventLogTags.writePdpBadDnsAddress("dnsAddr=" + dnsAddr); + throw new UnknownHostException("Non-numeric dns addr=" + + dnsAddr); + } + InetAddress ia = InetAddress.getByName(dnsAddr); + linkProperties.addDns(ia); + } + result = SetupResult.SUCCESS; } else { - result = SetupResult.ERR_BadDns; + StringBuilder sb = new StringBuilder(); + for (String dnsAddr : dnsServers) { + sb.append(dnsAddr); + sb.append(" "); + } + EventLogTags.writePdpBadDnsAddress("Unacceptable dns addresses=" + sb); + throw new UnknownHostException("Unacceptable dns addresses=" + sb); } } - } catch (UnknownHostException e1) { - log("onSetupCompleted: UnknowHostException " + e1); - e1.printStackTrace(); - result = SetupResult.ERR_Other; - } catch (SocketException e2) { - log("onSetupCompleted: SocketException " + e2); - e2.printStackTrace(); - result = SetupResult.ERR_Other; + } catch (UnknownHostException e) { + log("onSetupCompleted: UnknownHostException " + e); + e.printStackTrace(); + result = SetupResult.ERR_UnacceptableParameter; } } else { - log("onSetupCompleted: error; expected number of responses >= 2 was " + - response.length); - result = SetupResult.ERR_Other; + if (response.version < 4) { + result = SetupResult.ERR_GetLastErrorFromRil; + } else { + result = SetupResult.ERR_RilError; + } } // An error occurred so clear properties if (result != SetupResult.SUCCESS) { - log("onSetupCompleted with an error clearing LinkProperties"); + log("onSetupConnectionCompleted with an error, clearing LinkProperties"); linkProperties.clear(); } mLinkProperties = linkProperties; } if (DBG) { - log("DataConnection setup result='" + result + "' on cid=" + cid); + log("onSetupConnectionCompleted: DataConnection setup result='" + + result + "' on cid=" + cid); if (result == SetupResult.SUCCESS) { - log("LinkProperties: " + mLinkProperties.toString()); + log("onSetupConnectionCompleted: LinkProperties: " + mLinkProperties.toString()); } } return result; @@ -684,6 +631,7 @@ public abstract class DataConnection extends HierarchicalStateMachine { cp = (ConnectionParams) ar.userObj; SetupResult result = onSetupConnectionCompleted(ar); + if (DBG) log("DcActivatingState onSetupConnectionCompleted result=" + result); switch (result) { case SUCCESS: // All is well @@ -697,26 +645,21 @@ public abstract class DataConnection extends HierarchicalStateMachine { mInactiveState.setEnterNotificationParams(cp, result.mFailCause); transitionTo(mInactiveState); break; - case ERR_BadDns: - // Connection succeeded but DNS info is bad so disconnect - StringBuilder dnsAddressesSb = new StringBuilder(); - for (InetAddress addr : mLinkProperties.getDnses()) { - if (dnsAddressesSb.length() != 0) dnsAddressesSb.append(" "); - dnsAddressesSb.append(addr.toString()); - } - if (dnsAddressesSb.length() == 0) { - dnsAddressesSb.append("no-dns-addresses"); - } - EventLog.writeEvent(EventLogTags.PDP_BAD_DNS_ADDRESS, - dnsAddressesSb.toString()); + case ERR_UnacceptableParameter: + // The addresses given from the RIL are bad tearDownData(cp); - transitionTo(mDisconnectingBadDnsState); + transitionTo(mDisconnectingErrorCreatingConnection); break; - case ERR_Other: - // Request the failure cause and process in this state + case ERR_GetLastErrorFromRil: + // Request failed and this is an old RIL phone.mCM.getLastDataCallFailCause( obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp)); break; + case ERR_RilError: + // Request failed and mFailCause has the reason + mInactiveState.setEnterNotificationParams(cp, result.mFailCause); + transitionTo(mInactiveState); + break; case ERR_Stale: // Request is stale, ignore. break; @@ -735,7 +678,7 @@ public abstract class DataConnection extends HierarchicalStateMachine { if (DBG) log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"); if (ar.exception == null) { int rilFailCause = ((int[]) (ar.result))[0]; - cause = getFailCauseFromRequest(rilFailCause); + cause = FailCause.fromInt(rilFailCause); } // Transition to inactive but send notifications after // we've entered the mInactive state. @@ -852,10 +795,9 @@ public abstract class DataConnection extends HierarchicalStateMachine { private DcDisconnectingState mDisconnectingState = new DcDisconnectingState(); /** - * The state machine is disconnecting after a bad dns setup - * was found in mInactivatingState. + * The state machine is disconnecting after an creating a connection. */ - private class DcDisconnectingBadDnsState extends HierarchicalState { + private class DcDisconnectionErrorCreatingConnection extends HierarchicalState { @Override protected boolean processMessage(Message msg) { boolean retVal; @@ -864,27 +806,38 @@ public abstract class DataConnection extends HierarchicalStateMachine { AsyncResult ar = (AsyncResult) msg.obj; ConnectionParams cp = (ConnectionParams) ar.userObj; if (cp.tag == mTag) { - if (DBG) log("DcDisconnectingBadDnsState msg.what=EVENT_DEACTIVATE_DONE"); + if (DBG) { + log("DcDisconnectionErrorCreatingConnection" + + " msg.what=EVENT_DEACTIVATE_DONE"); + } + // Transition to inactive but send notifications after // we've entered the mInactive state. - mInactiveState.setEnterNotificationParams(cp, FailCause.UNKNOWN); + mInactiveState.setEnterNotificationParams(cp, + FailCause.UNACCEPTABLE_NETWORK_PARAMETER); transitionTo(mInactiveState); } else { - if (DBG) log("DcDisconnectingBadDnsState EVENT_DEACTIVE_DONE stale dp.tag=" - + cp.tag + ", mTag=" + mTag); + if (DBG) { + log("DcDisconnectionErrorCreatingConnection EVENT_DEACTIVATE_DONE" + + " stale dp.tag=" + cp.tag + ", mTag=" + mTag); + } } retVal = true; break; default: - if (DBG) log("DcDisconnectingBadDnsState not handled msg.what=" + msg.what); + if (DBG) { + log("DcDisconnectionErrorCreatingConnection not handled msg.what=" + + msg.what); + } retVal = false; break; } return retVal; } } - private DcDisconnectingBadDnsState mDisconnectingBadDnsState = new DcDisconnectingBadDnsState(); + private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection = + new DcDisconnectionErrorCreatingConnection(); // ******* public interface diff --git a/telephony/java/com/android/internal/telephony/EventLogTags.logtags b/telephony/java/com/android/internal/telephony/EventLogTags.logtags index b06c27d215f8..9be7b800cf69 100644 --- a/telephony/java/com/android/internal/telephony/EventLogTags.logtags +++ b/telephony/java/com/android/internal/telephony/EventLogTags.logtags @@ -53,3 +53,6 @@ option java_package com.android.internal.telephony; # CDMA service state transition 50116 cdma_service_state_change (oldState|1|5), (oldDataState|1|5), (newState|1|5), (newDataState|1|5) + +# Bad IP address +50117 bad_ip_address (ip_address|3) diff --git a/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.aidl b/telephony/java/com/android/internal/telephony/OperatorInfo.aidl index d88d0b721f47..3ded8dd3bcd7 100644 --- a/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.aidl +++ b/telephony/java/com/android/internal/telephony/OperatorInfo.aidl @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm; +package com.android.internal.telephony; /** - * Used to indicate that the NetworkInfo object is parcelable to aidl. - * This is a simple effort to make NetworkInfo parcelable rather than + * Used to indicate that the OperatorInfo object is parcelable to aidl. + * This is a simple effort to make OperatorInfo parcelable rather than * trying to make the conventional containing object (AsyncResult), * implement parcelable. This functionality is needed for the * NetworkQueryService to fix 1128695 */ -parcelable NetworkInfo; +parcelable OperatorInfo; diff --git a/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java index 04fd13e715a7..1999cb3ada4f 100644 --- a/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.java +++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm; +package com.android.internal.telephony; import android.os.Parcel; import android.os.Parcelable; @@ -22,7 +22,7 @@ import android.os.Parcelable; /** * {@hide} */ -public class NetworkInfo implements Parcelable { +public class OperatorInfo implements Parcelable { public enum State { UNKNOWN, AVAILABLE, @@ -30,11 +30,11 @@ public class NetworkInfo implements Parcelable { FORBIDDEN; } - String operatorAlphaLong; - String operatorAlphaShort; - String operatorNumeric; + private String operatorAlphaLong; + private String operatorAlphaShort; + private String operatorNumeric; - State state = State.UNKNOWN; + private State state = State.UNKNOWN; public String @@ -57,7 +57,7 @@ public class NetworkInfo implements Parcelable { return state; } - NetworkInfo(String operatorAlphaLong, + OperatorInfo(String operatorAlphaLong, String operatorAlphaShort, String operatorNumeric, State state) { @@ -70,7 +70,7 @@ public class NetworkInfo implements Parcelable { } - public NetworkInfo(String operatorAlphaLong, + public OperatorInfo(String operatorAlphaLong, String operatorAlphaShort, String operatorNumeric, String stateString) { @@ -98,7 +98,7 @@ public class NetworkInfo implements Parcelable { public String toString() { - return "NetworkInfo " + operatorAlphaLong + return "OperatorInfo " + operatorAlphaLong + "/" + operatorAlphaShort + "/" + operatorNumeric + "/" + state; @@ -106,7 +106,7 @@ public class NetworkInfo implements Parcelable { /** * Parcelable interface implemented below. - * This is a simple effort to make NetworkInfo parcelable rather than + * This is a simple effort to make OperatorInfo parcelable rather than * trying to make the conventional containing object (AsyncResult), * implement parcelable. This functionality is needed for the * NetworkQueryService to fix 1128695. @@ -118,7 +118,7 @@ public class NetworkInfo implements Parcelable { /** * Implement the Parcelable interface. - * Method to serialize a NetworkInfo object. + * Method to serialize a OperatorInfo object. */ public void writeToParcel(Parcel dest, int flags) { dest.writeString(operatorAlphaLong); @@ -129,21 +129,21 @@ public class NetworkInfo implements Parcelable { /** * Implement the Parcelable interface - * Method to deserialize a NetworkInfo object, or an array thereof. + * Method to deserialize a OperatorInfo object, or an array thereof. */ - public static final Creator<NetworkInfo> CREATOR = - new Creator<NetworkInfo>() { - public NetworkInfo createFromParcel(Parcel in) { - NetworkInfo netInfo = new NetworkInfo( + public static final Creator<OperatorInfo> CREATOR = + new Creator<OperatorInfo>() { + public OperatorInfo createFromParcel(Parcel in) { + OperatorInfo opInfo = new OperatorInfo( in.readString(), /*operatorAlphaLong*/ in.readString(), /*operatorAlphaShort*/ in.readString(), /*operatorNumeric*/ (State) in.readSerializable()); /*state*/ - return netInfo; + return opInfo; } - public NetworkInfo[] newArray(int size) { - return new NetworkInfo[size]; + public OperatorInfo[] newArray(int size) { + return new OperatorInfo[size]; } }; } diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index ef9e783a7dd4..acb86d4027af 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -27,7 +27,6 @@ import android.telephony.ServiceState; import android.telephony.SignalStrength; import com.android.internal.telephony.DataConnection; -import com.android.internal.telephony.gsm.NetworkInfo; import com.android.internal.telephony.test.SimulatedRadioControl; import java.util.List; @@ -1056,7 +1055,7 @@ public interface Phone { * one of the following members:.<p> *<ul> * <li><code>response.obj.result</code> will be a <code>List</code> of - * <code>com.android.internal.telephony.gsm.NetworkInfo</code> objects, or</li> + * <code>OperatorInfo</code> objects, or</li> * <li><code>response.obj.exception</code> will be set with an exception * on failure.</li> * </ul> @@ -1070,8 +1069,7 @@ public interface Phone { * @param response The message to dispatch when the network selection * is complete. * - * @see #selectNetworkManually(com.android.internal.telephony.gsm.NetworkInfo, - * android.os.Message ) + * @see #selectNetworkManually(OperatorInfo, android.os.Message ) */ void setNetworkSelectionModeAutomatic(Message response); @@ -1083,7 +1081,7 @@ public interface Phone { * * @see #setNetworkSelectionModeAutomatic(Message) */ - void selectNetworkManually(NetworkInfo network, + void selectNetworkManually(OperatorInfo network, Message response); /** diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java index 219efbb9e50e..15b23bbe3d2a 100644 --- a/telephony/java/com/android/internal/telephony/PhoneProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java @@ -32,7 +32,6 @@ import android.util.Log; import com.android.internal.telephony.cdma.CDMAPhone; import com.android.internal.telephony.gsm.GSMPhone; -import com.android.internal.telephony.gsm.NetworkInfo; import com.android.internal.telephony.test.SimulatedRadioControl; import java.util.List; @@ -550,7 +549,7 @@ public class PhoneProxy extends Handler implements Phone { mActivePhone.setNetworkSelectionModeAutomatic(response); } - public void selectNetworkManually(NetworkInfo network, Message response) { + public void selectNetworkManually(OperatorInfo network, Message response) { mActivePhone.selectNetworkManually(network, response); } diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java index 91b19a97f17a..3ef1924c55a2 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -44,18 +44,19 @@ import android.telephony.NeighboringCellInfo; import android.telephony.PhoneNumberUtils; import android.telephony.SmsManager; import android.telephony.SmsMessage; +import android.text.TextUtils; import android.util.Config; import android.util.Log; import com.android.internal.telephony.CallForwardInfo; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.DataCallState; -import com.android.internal.telephony.gsm.NetworkInfo; import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; import com.android.internal.telephony.gsm.SuppServiceNotification; import com.android.internal.telephony.IccCardApplication; import com.android.internal.telephony.IccCardStatus; import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.OperatorInfo; import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.SmsResponse; import com.android.internal.telephony.cdma.CdmaCallWaitingNotification; @@ -2177,7 +2178,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_REQUEST_DTMF: ret = responseVoid(p); break; case RIL_REQUEST_SEND_SMS: ret = responseSMS(p); break; case RIL_REQUEST_SEND_SMS_EXPECT_MORE: ret = responseSMS(p); break; - case RIL_REQUEST_SETUP_DATA_CALL: ret = responseStrings(p); break; + case RIL_REQUEST_SETUP_DATA_CALL: ret = responseSetupDataCall(p); break; case RIL_REQUEST_SIM_IO: ret = responseICC_IO(p); break; case RIL_REQUEST_SEND_USSD: ret = responseVoid(p); break; case RIL_REQUEST_CANCEL_USSD: ret = responseVoid(p); break; @@ -2198,7 +2199,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: ret = responseInts(p); break; case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: ret = responseVoid(p); break; case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: ret = responseVoid(p); break; - case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : ret = responseNetworkInfos(p); break; + case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : ret = responseOperatorInfos(p); break; case RIL_REQUEST_DTMF_START: ret = responseVoid(p); break; case RIL_REQUEST_DTMF_STOP: ret = responseVoid(p); break; case RIL_REQUEST_BASEBAND_VERSION: ret = responseString(p); break; @@ -2935,37 +2936,100 @@ public final class RIL extends BaseCommands implements CommandsInterface { return response; } + private DataCallState getDataCallState(Parcel p, int version) { + DataCallState dataCall = new DataCallState(); + + dataCall.version = version; + if (version < 5) { + dataCall.cid = p.readInt(); + dataCall.active = p.readInt(); + dataCall.type = p.readString(); + p.readString(); // Ignore apn + String addresses = p.readString(); + if (TextUtils.isEmpty(addresses)) { + dataCall.addresses = addresses.split(" "); + } + } else { + dataCall.status = p.readInt(); + dataCall.cid = p.readInt(); + dataCall.active = p.readInt(); + dataCall.type = p.readString(); + dataCall.ifname = p.readString(); + if (TextUtils.isEmpty(dataCall.ifname)) { + throw new RuntimeException("getDataCallState, no ifname"); + } + String addresses = p.readString(); + if (!TextUtils.isEmpty(addresses)) { + dataCall.addresses = addresses.split(" "); + } + String dnses = p.readString(); + if (!TextUtils.isEmpty(dnses)) { + dataCall.dnses = dnses.split(" "); + } + } + return dataCall; + } + private Object responseDataCallList(Parcel p) { - int num; ArrayList<DataCallState> response; - num = p.readInt(); - response = new ArrayList<DataCallState>(num); + int ver = p.readInt(); + int num = p.readInt(); + Log.d(LOG_TAG, "responseDataCallList ver=" + ver + " num=" + num); + response = new ArrayList<DataCallState>(num); for (int i = 0; i < num; i++) { - DataCallState dataCall = new DataCallState(); + response.add(getDataCallState(p, ver)); + } - dataCall.cid = p.readInt(); - dataCall.active = p.readInt(); - dataCall.type = p.readString(); - dataCall.apn = p.readString(); - String address = p.readString(); - if (address != null) { - address = address.split(" ")[0]; - } - dataCall.address = address; + return response; + } - response.add(dataCall); + private Object + responseSetupDataCall(Parcel p) { + int ver = p.readInt(); + int num = p.readInt(); + Log.d(LOG_TAG, "responseSetupDataCall ver=" + ver + " num=" + num); + + DataCallState dataCall; + + if (ver < 5) { + dataCall = new DataCallState(); + dataCall.version = ver; + dataCall.cid = Integer.parseInt(p.readString()); + dataCall.ifname = p.readString(); + if (TextUtils.isEmpty(dataCall.ifname)) { + throw new RuntimeException( + "RIL_REQUEST_SETUP_DATA_CALL response, no ifname"); + } + String addresses = p.readString(); + if (!TextUtils.isEmpty(addresses)) { + dataCall.addresses = addresses.split(" "); + } + if (num >= 4) { + String dnses = p.readString(); + Log.d(LOG_TAG, "responseSetupDataCall got dnses=" + dnses); + if (!TextUtils.isEmpty(dnses)) { + dataCall.dnses = dnses.split(" "); + } + } + } else { + if (num != 1) { + throw new RuntimeException( + "RIL_REQUEST_SETUP_DATA_CALL response expecting 1 RIL_Data_Call_response_v5" + + " got " + num); + } + dataCall = getDataCallState(p, ver); } - return response; + return dataCall; } private Object - responseNetworkInfos(Parcel p) { + responseOperatorInfos(Parcel p) { String strings[] = (String [])responseStrings(p); - ArrayList<NetworkInfo> ret; + ArrayList<OperatorInfo> ret; if (strings.length % 4 != 0) { throw new RuntimeException( @@ -2973,11 +3037,11 @@ public final class RIL extends BaseCommands implements CommandsInterface { + strings.length + " strings, expected multible of 4"); } - ret = new ArrayList<NetworkInfo>(strings.length / 4); + ret = new ArrayList<OperatorInfo>(strings.length / 4); for (int i = 0 ; i < strings.length ; i += 4) { ret.add ( - new NetworkInfo( + new OperatorInfo( strings[i+0], strings[i+1], strings[i+2], diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java index bd00a0bdac76..0849f76aaa86 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -56,6 +56,7 @@ import com.android.internal.telephony.IccFileHandler; import com.android.internal.telephony.IccPhoneBookInterfaceManager; import com.android.internal.telephony.IccSmsInterfaceManager; import com.android.internal.telephony.MmiCode; +import com.android.internal.telephony.OperatorInfo; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneBase; import com.android.internal.telephony.PhoneNotifier; @@ -136,6 +137,7 @@ public class CDMAPhone extends PhoneBase { Registrant mPostDialHandler; + static String PROPERTY_CDMA_HOME_OPERATOR_NUMERIC = "ro.cdma.home.operator.numeric"; // Constructors public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier) { @@ -195,7 +197,7 @@ public class CDMAPhone extends PhoneBase { setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, operatorAlpha); // Sets operator numeric property by retrieving from build-time system property - String operatorNumeric = SystemProperties.get("ro.cdma.home.operator.numeric"); + String operatorNumeric = SystemProperties.get(PROPERTY_CDMA_HOME_OPERATOR_NUMERIC); setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operatorNumeric); // Sets iso country property by retrieving from build-time system property @@ -483,7 +485,7 @@ public class CDMAPhone extends PhoneBase { } public void - selectNetworkManually(com.android.internal.telephony.gsm.NetworkInfo network, + selectNetworkManually(OperatorInfo network, Message response) { Log.e(LOG_TAG, "selectNetworkManually: not possible in CDMA"); } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java index 1b2c9183daef..1a0dbc2f0ca3 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java @@ -20,6 +20,7 @@ import android.os.Message; import android.util.Log; import com.android.internal.telephony.DataConnection; +import com.android.internal.telephony.DataConnection.FailCause; import com.android.internal.telephony.Phone; import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.RetryManager; @@ -31,13 +32,6 @@ public class CdmaDataConnection extends DataConnection { private static final String LOG_TAG = "CDMA"; - /** Fail cause of last Data Call activate from RIL_LastDataCallActivateFailCause */ - private final static int PS_NET_DOWN_REASON_OPERATOR_DETERMINED_BARRING = 8; - private final static int PS_NET_DOWN_REASON_AUTH_FAILED = 29; - private final static int PS_NET_DOWN_REASON_OPTION_NOT_SUPPORTED = 32; - private final static int PS_NET_DOWN_REASON_OPTION_UNSUBSCRIBED = 33; - - // ***** Constructor private CdmaDataConnection(CDMAPhone phone, String name, RetryManager rm) { super(phone, name, rm); @@ -104,29 +98,6 @@ public class CdmaDataConnection extends DataConnection { } @Override - protected FailCause getFailCauseFromRequest(int rilCause) { - FailCause cause; - - switch (rilCause) { - case PS_NET_DOWN_REASON_OPERATOR_DETERMINED_BARRING: - cause = FailCause.OPERATOR_BARRED; - break; - case PS_NET_DOWN_REASON_AUTH_FAILED: - cause = FailCause.USER_AUTHENTICATION; - break; - case PS_NET_DOWN_REASON_OPTION_NOT_SUPPORTED: - cause = FailCause.SERVICE_OPTION_NOT_SUPPORTED; - break; - case PS_NET_DOWN_REASON_OPTION_UNSUBSCRIBED: - cause = FailCause.SERVICE_OPTION_NOT_SUBSCRIBED; - break; - default: - cause = FailCause.UNKNOWN; - } - return cause; - } - - @Override protected boolean isDnsOk(String[] domainNameServers) { if ((NULL_IP.equals(domainNameServers[0]) && NULL_IP.equals(domainNameServers[1]) diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java index 60df7dfe0f45..c324a717207f 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java @@ -336,7 +336,8 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { types = mDefaultApnTypes; apnId = mDefaultApnId; } - mActiveApn = new ApnSetting(apnId, "", "", "", "", "", "", "", "", "", "", 0, types); + mActiveApn = new ApnSetting(apnId, "", "", "", "", "", "", "", "", "", + "", 0, types, "IP", "IP"); if (DBG) log("setupData: mActiveApn=" + mActiveApn); Message msg = obtainMessage(); diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java index a7f786664fec..b217f07b9c1e 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java @@ -16,6 +16,8 @@ package com.android.internal.telephony.cdma; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC; + import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.DataConnectionTracker; @@ -794,6 +796,18 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { String opNames[] = (String[])ar.result; if (opNames != null && opNames.length >= 3) { + // If the NUMERIC field isn't valid use PROPERTY_CDMA_HOME_OPERATOR_NUMERIC + if ((opNames[2] == null) || (opNames[2].length() < 5) + || ("00000".equals(opNames[2]))) { + opNames[2] = SystemProperties.get( + CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC, "00000"); + if (DBG) { + log("RIL_REQUEST_OPERATOR.response[2], the numeric, " + + " is bad. Using SystemProperties '" + + CDMAPhone.PROPERTY_CDMA_HOME_OPERATOR_NUMERIC + + "'= " + opNames[2]); + } + } if (cm.getRadioState().isNVReady()) { // In CDMA in case on NV, the ss.mOperatorAlphaLong is set later with the // ERI text, so here it is ignored what is coming from the modem. diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index e8ad0f21cf02..c17197e46f79 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -62,6 +62,7 @@ import com.android.internal.telephony.IccFileHandler; import com.android.internal.telephony.IccPhoneBookInterfaceManager; import com.android.internal.telephony.IccSmsInterfaceManager; import com.android.internal.telephony.MmiCode; +import com.android.internal.telephony.OperatorInfo; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneBase; import com.android.internal.telephony.PhoneNotifier; @@ -1025,19 +1026,19 @@ public class GSMPhone extends PhoneBase { } public void - selectNetworkManually(com.android.internal.telephony.gsm.NetworkInfo network, + selectNetworkManually(OperatorInfo network, Message response) { // wrap the response message in our own message along with // the operator's id. NetworkSelectMessage nsm = new NetworkSelectMessage(); nsm.message = response; - nsm.operatorNumeric = network.operatorNumeric; - nsm.operatorAlphaLong = network.operatorAlphaLong; + nsm.operatorNumeric = network.getOperatorNumeric(); + nsm.operatorAlphaLong = network.getOperatorAlphaLong(); // get the message Message msg = obtainMessage(EVENT_SET_NETWORK_MANUAL_COMPLETE, nsm); - mCM.setNetworkSelectionModeManual(network.operatorNumeric, msg); + mCM.setNetworkSelectionModeManual(network.getOperatorNumeric(), msg); } public void diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java index b94702060387..4689b2d38630 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java @@ -33,24 +33,6 @@ public class GsmDataConnection extends DataConnection { private static final String LOG_TAG = "GSM"; - /** Fail cause of last PDP activate, from RIL_LastPDPActivateFailCause */ - private static final int PDP_FAIL_OPERATOR_BARRED = 0x08; - private static final int PDP_FAIL_INSUFFICIENT_RESOURCES = 0x1A; - private static final int PDP_FAIL_MISSING_UKNOWN_APN = 0x1B; - private static final int PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE = 0x1C; - private static final int PDP_FAIL_USER_AUTHENTICATION = 0x1D; - private static final int PDP_FAIL_ACTIVATION_REJECT_GGSN = 0x1E; - private static final int PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED = 0x1F; - private static final int PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED = 0x20; - private static final int PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED = 0x21; - private static final int PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER = 0x22; - private static final int PDP_FAIL_NSAPI_IN_USE = 0x23; - private static final int PDP_FAIL_PROTOCOL_ERRORS = 0x6F; - private static final int PDP_FAIL_ERROR_UNSPECIFIED = 0xffff; - - private static final int PDP_FAIL_REGISTRATION_FAIL = -1; - private static final int PDP_FAIL_GPRS_REGISTRATION_FAIL = -2; - //***** Instance Variables private ApnSetting apn; @@ -110,12 +92,19 @@ public class GsmDataConnection extends DataConnection { authType = (apn.user != null) ? RILConstants.SETUP_DATA_AUTH_PAP_CHAP : RILConstants.SETUP_DATA_AUTH_NONE; } + + String protocol; + if (phone.getServiceState().getRoaming()) { + protocol = apn.roamingProtocol; + } else { + protocol = apn.protocol; + } + phone.mCM.setupDataCall( Integer.toString(RILConstants.SETUP_DATA_TECH_GSM), Integer.toString(RILConstants.DATA_PROFILE_DEFAULT), - apn.apn, apn.user, apn.password, - Integer.toString(authType), - RILConstants.SETUP_DATA_PROTOCOL_IP, msg); + apn.apn, apn.user, apn.password, Integer.toString(authType), + protocol, msg); } @Override @@ -132,62 +121,6 @@ public class GsmDataConnection extends DataConnection { } @Override - protected FailCause getFailCauseFromRequest(int rilCause) { - FailCause cause; - - switch (rilCause) { - case PDP_FAIL_OPERATOR_BARRED: - cause = FailCause.OPERATOR_BARRED; - break; - case PDP_FAIL_INSUFFICIENT_RESOURCES: - cause = FailCause.INSUFFICIENT_RESOURCES; - break; - case PDP_FAIL_MISSING_UKNOWN_APN: - cause = FailCause.MISSING_UNKNOWN_APN; - break; - case PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE: - cause = FailCause.UNKNOWN_PDP_ADDRESS; - break; - case PDP_FAIL_USER_AUTHENTICATION: - cause = FailCause.USER_AUTHENTICATION; - break; - case PDP_FAIL_ACTIVATION_REJECT_GGSN: - cause = FailCause.ACTIVATION_REJECT_GGSN; - break; - case PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED: - cause = FailCause.ACTIVATION_REJECT_UNSPECIFIED; - break; - case PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER: - cause = FailCause.SERVICE_OPTION_OUT_OF_ORDER; - break; - case PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED: - cause = FailCause.SERVICE_OPTION_NOT_SUPPORTED; - break; - case PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED: - cause = FailCause.SERVICE_OPTION_NOT_SUBSCRIBED; - break; - case PDP_FAIL_NSAPI_IN_USE: - cause = FailCause.NSAPI_IN_USE; - break; - case PDP_FAIL_PROTOCOL_ERRORS: - cause = FailCause.PROTOCOL_ERRORS; - break; - case PDP_FAIL_ERROR_UNSPECIFIED: - cause = FailCause.UNKNOWN; - break; - case PDP_FAIL_REGISTRATION_FAIL: - cause = FailCause.REGISTRATION_FAIL; - break; - case PDP_FAIL_GPRS_REGISTRATION_FAIL: - cause = FailCause.GPRS_REGISTRATION_FAIL; - break; - default: - cause = FailCause.UNKNOWN; - } - return cause; - } - - @Override protected boolean isDnsOk(String[] domainNameServers) { if (NULL_IP.equals(domainNameServers[0]) && NULL_IP.equals(domainNameServers[1]) && !((GSMPhone) phone).isDnsCheckDisabled()) { diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index cd0d9e392a87..f2cdf0c3066a 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -432,7 +432,10 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)), cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)), cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)), - types); + types, + cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)), + cursor.getString(cursor.getColumnIndexOrThrow( + Telephony.Carriers.ROAMING_PROTOCOL))); result.add(apn); } while (cursor.moveToNext()); } diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java index db9e35ab678c..9dfc0158a26a 100755 --- a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java +++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java @@ -38,6 +38,7 @@ import com.android.internal.telephony.IccFileHandler; import com.android.internal.telephony.IccPhoneBookInterfaceManager; import com.android.internal.telephony.IccSmsInterfaceManager; import com.android.internal.telephony.MmiCode; +import com.android.internal.telephony.OperatorInfo; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneBase; import com.android.internal.telephony.PhoneNotifier; @@ -347,7 +348,7 @@ abstract class SipPhoneBase extends PhoneBase { } public void selectNetworkManually( - com.android.internal.telephony.gsm.NetworkInfo network, + OperatorInfo network, Message response) { } diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java new file mode 100644 index 000000000000..10320743e253 --- /dev/null +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import junit.framework.TestCase; + +import android.test.suitebuilder.annotation.SmallTest; + +public class ApnSettingTest extends TestCase { + + public static final String[] TYPES = {"default", "*"}; + + public static void assertApnSettingEqual(ApnSetting a1, ApnSetting a2) { + assertEquals(a1.carrier, a2.carrier); + assertEquals(a1.apn, a2.apn); + assertEquals(a1.proxy, a2.proxy); + assertEquals(a1.port, a2.port); + assertEquals(a1.mmsc, a2.mmsc); + assertEquals(a1.mmsProxy, a2.mmsProxy); + assertEquals(a1.mmsPort, a2.mmsPort); + assertEquals(a1.user, a2.user); + assertEquals(a1.password, a2.password); + assertEquals(a1.authType, a2.authType); + assertEquals(a1.id, a2.id); + assertEquals(a1.numeric, a2.numeric); + assertEquals(a1.protocol, a2.protocol); + assertEquals(a1.roamingProtocol, a2.roamingProtocol); + assertEquals(a1.types.length, a2.types.length); + int i; + for (i = 0; i < a1.types.length; i++) { + assertEquals(a1.types[i], a2.types[i]); + } + } + + @SmallTest + public void testFromString() throws Exception { + String[] dunTypes = {"DUN"}; + String[] mmsTypes = {"mms", "*"}; + + ApnSetting expected_apn; + String testString; + + // A real-world v1 example string. + testString = "Vodafone IT,web.omnitel.it,,,,,,,,,222,10,,DUN"; + expected_apn = new ApnSetting( + -1, "22210", "Vodafone IT", "web.omnitel.it", "", "", + "", "", "", "", "", 0, dunTypes, "IP", "IP"); + assertApnSettingEqual(expected_apn, ApnSetting.fromString(testString)); + + // A v2 string. + testString = "[ApnSettingV2] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP"; + expected_apn = new ApnSetting( + -1, "12345", "Name", "apn", "", "", + "", "", "", "", "", 0, mmsTypes, "IPV6", "IP"); + assertApnSettingEqual(expected_apn, ApnSetting.fromString(testString)); + + // A v2 string with spaces. + testString = "[ApnSettingV2] Name,apn, ,,,,,,,,123,45,,mms|*,IPV4V6, IP"; + expected_apn = new ApnSetting( + -1, "12345", "Name", "apn", "", "", + "", "", "", "", "", 0, mmsTypes, "IPV4V6", "IP"); + assertApnSettingEqual(expected_apn, ApnSetting.fromString(testString)); + + // Return null if insufficient fields given. + testString = "[ApnSettingV2] Name,apn,,,,,,,,,123, 45,,mms|*"; + assertEquals(null, ApnSetting.fromString(testString)); + + testString = "Name,apn,,,,,,,,,123, 45,"; + assertEquals(null, ApnSetting.fromString(testString)); + + // Parse (incorrect) V2 format without the tag as V1. + testString = "Name,apn,,,,,,,,,123, 45,,mms|*,IPV6"; + String[] incorrectTypes = {"mms|*", "IPV6"}; + expected_apn = new ApnSetting( + -1, "12345", "Name", "apn", "", "", + "", "", "", "", "", 0, incorrectTypes, "IP", "IP"); + assertApnSettingEqual(expected_apn, ApnSetting.fromString(testString)); + } + + + @SmallTest + public void testToString() throws Exception { + String[] types = {"default", "*"}; + ApnSetting apn = new ApnSetting( + 99, "12345", "Name", "apn", "proxy", "port", + "mmsc", "mmsproxy", "mmsport", "user", "password", 0, + types, "IPV4V6", "IP"); + String expected = "[ApnSettingV2] Name, 99, 12345, apn, proxy, " + + "mmsc, mmsproxy, mmsport, port, 0, default | *, " + + "IPV4V6, IP"; + assertEquals(expected, apn.toString()); + } +} + diff --git a/tests/CoreTests/android/core/DatabaseSessionCache.java b/tests/CoreTests/android/core/DatabaseSessionCache.java deleted file mode 100644 index 040a13ed5255..000000000000 --- a/tests/CoreTests/android/core/DatabaseSessionCache.java +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2009 The Android Open Source Project - -package android.core; - -import android.database.Cursor; -import android.database.SQLException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; -import android.content.ContentValues; -import android.content.Context; - -import org.apache.commons.codec.binary.Base64; -import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; - -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.net.ssl.SSLSession; - -/** - * Hook into harmony SSL cache to persist the SSL sessions. - * - * Current implementation is suitable for saving a small number of hosts - - * like google services. It can be extended with expiration and more features - * to support more hosts. - * - * {@hide} - */ -public class DatabaseSessionCache implements SSLClientSessionCache { - private static final String TAG = "SslSessionCache"; - static DatabaseHelper sDefaultDatabaseHelper; - - private DatabaseHelper mDatabaseHelper; - - /** - * Table where sessions are stored. - */ - public static final String SSL_CACHE_TABLE = "ssl_sessions"; - - private static final String SSL_CACHE_ID = "_id"; - - /** - * Key is host:port - port is not optional. - */ - private static final String SSL_CACHE_HOSTPORT = "hostport"; - - /** - * Base64-encoded DER value of the session. - */ - private static final String SSL_CACHE_SESSION = "session"; - - /** - * Time when the record was added - should be close to the time - * of the initial session negotiation. - */ - private static final String SSL_CACHE_TIME_SEC = "time_sec"; - - public static final String DATABASE_NAME = "ssl_sessions.db"; - - public static final int DATABASE_VERSION = 1; - - /** public for testing - */ - public static final int SSL_CACHE_ID_COL = 0; - public static final int SSL_CACHE_HOSTPORT_COL = 1; - public static final int SSL_CACHE_SESSION_COL = 2; - public static final int SSL_CACHE_TIME_SEC_COL = 3; - - private static final String SAVE_ON_ADD = "save_on_add"; - - static boolean sHookInitializationDone = false; - - public static final int MAX_CACHE_SIZE = 256; - - private static final Map<String, byte[]> mExternalCache = - new LinkedHashMap<String, byte[]>(MAX_CACHE_SIZE, 0.75f, true) { - @Override - public boolean removeEldestEntry( - Map.Entry<String, byte[]> eldest) { - boolean shouldDelete = this.size() > MAX_CACHE_SIZE; - - // TODO: delete from DB - return shouldDelete; - } - }; - static boolean mNeedsCacheLoad = true; - - public static final String[] PROJECTION = new String[] { - SSL_CACHE_ID, - SSL_CACHE_HOSTPORT, - SSL_CACHE_SESSION, - SSL_CACHE_TIME_SEC - }; - - /** - * This class needs to be installed as a hook, if the security property - * is set. Getting the right classloader may be fun since we don't use - * Provider to get its classloader, but in android this is in same - * loader with AndroidHttpClient. - * - * This constructor will use the default database. You must - * call init() before to specify the context used for the database and - * check settings. - */ - public DatabaseSessionCache() { - Log.v(TAG, "Instance created."); - // May be null if caching is disabled - no sessions will be persisted. - this.mDatabaseHelper = sDefaultDatabaseHelper; - } - - /** - * Create a SslSessionCache instance, using the specified context to - * initialize the database. - * - * This constructor will use the default database - created the first - * time. - * - * @param activityContext - */ - public DatabaseSessionCache(Context activityContext) { - // Static init - only one initialization will happen. - // Each SslSessionCache is using the same DB. - init(activityContext); - // May be null if caching is disabled - no sessions will be persisted. - this.mDatabaseHelper = sDefaultDatabaseHelper; - } - - /** - * Create a SslSessionCache that uses a specific database. - * - * @param database - */ - public DatabaseSessionCache(DatabaseHelper database) { - this.mDatabaseHelper = database; - } - -// public static boolean enabled(Context androidContext) { -// String sslCache = Settings.Secure.getString(androidContext.getContentResolver(), -// Settings.Secure.SSL_SESSION_CACHE); -// -// if (Log.isLoggable(TAG, Log.DEBUG)) { -// Log.d(TAG, "enabled " + sslCache + " " + androidContext.getPackageName()); -// } -// -// return SAVE_ON_ADD.equals(sslCache); -// } - - /** - * You must call this method to enable SSL session caching for an app. - */ - public synchronized static void init(Context activityContext) { - // It is possible that multiple provider will try to install this hook. - // We want a single db per VM. - if (sHookInitializationDone) { - return; - } - - -// // More values can be added in future to provide different -// // behaviours, like 'batch save'. -// if (enabled(activityContext)) { - Context appContext = activityContext.getApplicationContext(); - sDefaultDatabaseHelper = new DatabaseHelper(appContext); - - // Set default SSLSocketFactory - // The property is defined in the javadocs for javax.net.SSLSocketFactory - // (no constant defined there) - // This should cover all code using SSLSocketFactory.getDefault(), - // including native http client and apache httpclient. - // MCS is using its own custom factory - will need special code. -// Security.setProperty("ssl.SocketFactory.provider", -// SslSocketFactoryWithCache.class.getName()); -// } - - // Won't try again. - sHookInitializationDone = true; - } - - public void putSessionData(SSLSession session, byte[] der) { - if (mDatabaseHelper == null) { - return; - } - if (mExternalCache.size() > MAX_CACHE_SIZE) { - // remove oldest. - Cursor byTime = mDatabaseHelper.getWritableDatabase().query(SSL_CACHE_TABLE, - PROJECTION, null, null, null, null, SSL_CACHE_TIME_SEC); - byTime.moveToFirst(); - // TODO: can I do byTime.deleteRow() ? - String hostPort = byTime.getString(SSL_CACHE_HOSTPORT_COL); - - mDatabaseHelper.getWritableDatabase().delete(SSL_CACHE_TABLE, - SSL_CACHE_HOSTPORT + "= ?" , new String[] { hostPort }); - } - // Serialize native session to standard DER encoding - long t0 = System.currentTimeMillis(); - - String b64 = new String(Base64.encodeBase64(der)); - String key = session.getPeerHost() + ":" + session.getPeerPort(); - - ContentValues values = new ContentValues(); - values.put(SSL_CACHE_HOSTPORT, key); - values.put(SSL_CACHE_SESSION, b64); - values.put(SSL_CACHE_TIME_SEC, System.currentTimeMillis() / 1000); - - synchronized (this.getClass()) { - mExternalCache.put(key, der); - - try { - mDatabaseHelper.getWritableDatabase().insert(SSL_CACHE_TABLE, null /*nullColumnHack */ , values); - } catch(SQLException ex) { - // Ignore - nothing we can do to recover, and caller shouldn't - // be affected. - Log.w(TAG, "Ignoring SQL exception when caching session", ex); - } - } - if (Log.isLoggable(TAG, Log.DEBUG)) { - long t1 = System.currentTimeMillis(); - Log.d(TAG, "New SSL session " + session.getPeerHost() + - " DER len: " + der.length + " " + (t1 - t0)); - } - - } - - public byte[] getSessionData(String host, int port) { - // Current (simple) implementation does a single lookup to DB, then saves - // all entries to the cache. - - // This works for google services - i.e. small number of certs. - // If we extend this to all processes - we should hold a separate cache - // or do lookups to DB each time. - if (mDatabaseHelper == null) { - return null; - } - synchronized(this.getClass()) { - if (mNeedsCacheLoad) { - // Don't try to load again, if something is wrong on the first - // request it'll likely be wrong each time. - mNeedsCacheLoad = false; - long t0 = System.currentTimeMillis(); - - Cursor cur = null; - try { - cur = mDatabaseHelper.getReadableDatabase().query(SSL_CACHE_TABLE, PROJECTION, null, - null, null, null, null); - if (cur.moveToFirst()) { - do { - String hostPort = cur.getString(SSL_CACHE_HOSTPORT_COL); - String value = cur.getString(SSL_CACHE_SESSION_COL); - - if (hostPort == null || value == null) { - continue; - } - // TODO: blob support ? - byte[] der = Base64.decodeBase64(value.getBytes()); - mExternalCache.put(hostPort, der); - } while (cur.moveToNext()); - - } - } catch (SQLException ex) { - Log.d(TAG, "Error loading SSL cached entries ", ex); - } finally { - if (cur != null) { - cur.close(); - } - if (Log.isLoggable(TAG, Log.DEBUG)) { - long t1 = System.currentTimeMillis(); - Log.d(TAG, "LOADED CACHED SSL " + (t1 - t0) + " ms"); - } - } - } - - String key = host + ":" + port; - - return mExternalCache.get(key); - } - } - - public byte[] getSessionData(byte[] id) { - // We support client side only - the cache will do nothing on client. - return null; - } - - /** Visible for testing. - */ - public static class DatabaseHelper extends SQLiteOpenHelper { - - public DatabaseHelper(Context context) { - super(context, DATABASE_NAME, null /* factory */, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + SSL_CACHE_TABLE + " (" + - SSL_CACHE_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - SSL_CACHE_HOSTPORT + " TEXT UNIQUE ON CONFLICT REPLACE," + - SSL_CACHE_SESSION + " TEXT," + - SSL_CACHE_TIME_SEC + " INTEGER" + - ");"); - db.execSQL("CREATE INDEX ssl_sessions_idx1 ON ssl_sessions (" + - SSL_CACHE_HOSTPORT + ");"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - db.execSQL("DROP TABLE IF EXISTS " + SSL_CACHE_TABLE ); - onCreate(db); - } - - } - -} diff --git a/tests/CoreTests/android/core/SSLPerformanceTest.java b/tests/CoreTests/android/core/SSLPerformanceTest.java deleted file mode 100644 index 5b5be0acf2ad..000000000000 --- a/tests/CoreTests/android/core/SSLPerformanceTest.java +++ /dev/null @@ -1,432 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.core; - -import android.test.AndroidTestCase; -import android.os.Debug; -import org.apache.harmony.xnet.provider.jsse.FileClientSessionCache; -import org.apache.harmony.xnet.provider.jsse.OpenSSLContextImpl; -import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.ssl.SSLSocketFactory; -import org.apache.http.impl.conn.SingleClientConnManager; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.ResponseHandler; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.HttpResponse; - -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSessionContext; -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; -import java.security.cert.Certificate; -import java.security.Principal; -import java.security.KeyManagementException; -import java.util.Arrays; - -public class SSLPerformanceTest extends AndroidTestCase { - - static final byte[] SESSION_DATA = new byte[6000]; - static { - for (int i = 0; i < SESSION_DATA.length; i++) { - SESSION_DATA[i] = (byte) i; - } - } - - static final File dataDir = new File("/data/data/android.core/"); - static final File filesDir = new File(dataDir, "files"); - static final File dbDir = new File(dataDir, "databases"); - - static final String CACHE_DIR - = SSLPerformanceTest.class.getName() + "/cache"; - - static final int ITERATIONS = 10; - - public void testCreateNewEmptyDatabase() { - deleteDatabase(); - - Stopwatch stopwatch = new Stopwatch(); - - DatabaseSessionCache cache = new DatabaseSessionCache(getContext()); - cache.getSessionData("crazybob.org", 443); - - stopwatch.stop(); - } - - public void testCreateNewEmptyDirectory() throws IOException { - deleteDirectory(); - - Stopwatch stopwatch = new Stopwatch(); - - SSLClientSessionCache cache = FileClientSessionCache.usingDirectory( - getCacheDirectory()); - cache.getSessionData("crazybob.org", 443); - - stopwatch.stop(); - } - - public void testOpenDatabaseWith10Sessions() { - deleteDatabase(); - - DatabaseSessionCache cache = new DatabaseSessionCache(getContext()); - putSessionsIn(cache); - closeDatabase(); - - System.err.println("Size of ssl_sessions.db w/ 10 sessions: " - + new File(dbDir, "ssl_sessions.db").length()); - - Stopwatch stopwatch = new Stopwatch(); - - cache = new DatabaseSessionCache(getContext()); - cache.getSessionData("crazybob.org", 443); - - stopwatch.stop(); - } - - public void testOpenDirectoryWith10Sessions() throws IOException { - deleteDirectory(); - - SSLClientSessionCache cache = FileClientSessionCache.usingDirectory( - getCacheDirectory()); - putSessionsIn(cache); - closeDirectoryCache(); - - Stopwatch stopwatch = new Stopwatch(); - - cache = FileClientSessionCache.usingDirectory( - getCacheDirectory()); - cache.getSessionData("crazybob.org", 443); - - stopwatch.stop(); - } - - public void testGetSessionFromDatabase() { - deleteDatabase(); - - DatabaseSessionCache cache = new DatabaseSessionCache(getContext()); - cache.putSessionData(new FakeSession("foo"), SESSION_DATA); - closeDatabase(); - - cache = new DatabaseSessionCache(getContext()); - cache.getSessionData("crazybob.org", 443); - - Stopwatch stopwatch = new Stopwatch(); - - byte[] sessionData = cache.getSessionData("foo", 443); - - stopwatch.stop(); - - assertTrue(Arrays.equals(SESSION_DATA, sessionData)); - } - - public void testGetSessionFromDirectory() throws IOException { - deleteDirectory(); - - SSLClientSessionCache cache = FileClientSessionCache.usingDirectory( - getCacheDirectory()); - cache.putSessionData(new FakeSession("foo"), SESSION_DATA); - closeDirectoryCache(); - - cache = FileClientSessionCache.usingDirectory( - getCacheDirectory()); - cache.getSessionData("crazybob.org", 443); - - Stopwatch stopwatch = new Stopwatch(); - - byte[] sessionData = cache.getSessionData("foo", 443); - - stopwatch.stop(); - - assertTrue(Arrays.equals(SESSION_DATA, sessionData)); - } - - public void testPutSessionIntoDatabase() { - deleteDatabase(); - - DatabaseSessionCache cache = new DatabaseSessionCache(getContext()); - cache.getSessionData("crazybob.org", 443); - - Stopwatch stopwatch = new Stopwatch(); - - cache.putSessionData(new FakeSession("foo"), SESSION_DATA); - - stopwatch.stop(); - } - - public void testPutSessionIntoDirectory() throws IOException { - deleteDirectory(); - - SSLClientSessionCache cache = FileClientSessionCache.usingDirectory( - getCacheDirectory()); - cache.getSessionData("crazybob.org", 443); - - Stopwatch stopwatch = new Stopwatch(); - - cache.putSessionData(new FakeSession("foo"), SESSION_DATA); - - stopwatch.stop(); - } - - public void testEngineInit() throws IOException, KeyManagementException { - Stopwatch stopwatch = new Stopwatch(); - - new OpenSSLContextImpl().engineInit(null, null, null); - - stopwatch.stop(); - } - - public void testWebRequestWithoutCache() throws IOException, - KeyManagementException { - OpenSSLContextImpl sslContext = new OpenSSLContextImpl(); - sslContext.engineInit(null, null, null); - - Stopwatch stopwatch = new Stopwatch(); - - getVerisignDotCom(sslContext); - - stopwatch.stop(); - } - - public void testWebRequestWithFileCache() throws IOException, - KeyManagementException { - deleteDirectory(); - - OpenSSLContextImpl sslContext = new OpenSSLContextImpl(); - sslContext.engineInit(null, null, null); - sslContext.engineGetClientSessionContext().setPersistentCache( - FileClientSessionCache.usingDirectory(getCacheDirectory())); - - // Make sure www.google.com is in the cache. - getVerisignDotCom(sslContext); - - // Re-initialize so we hit the file cache. - sslContext.engineInit(null, null, null); - sslContext.engineGetClientSessionContext().setPersistentCache( - FileClientSessionCache.usingDirectory(getCacheDirectory())); - - Stopwatch stopwatch = new Stopwatch(); - - getVerisignDotCom(sslContext); - - stopwatch.stop(); - } - - public void testWebRequestWithInMemoryCache() throws IOException, - KeyManagementException { - deleteDirectory(); - - OpenSSLContextImpl sslContext = new OpenSSLContextImpl(); - sslContext.engineInit(null, null, null); - - // Make sure www.google.com is in the cache. - getVerisignDotCom(sslContext); - - Stopwatch stopwatch = new Stopwatch(); - - getVerisignDotCom(sslContext); - - stopwatch.stop(); - } - - private void getVerisignDotCom(OpenSSLContextImpl sslContext) - throws IOException { - SchemeRegistry schemeRegistry = new SchemeRegistry(); - schemeRegistry.register(new Scheme("https", - new SSLSocketFactory(sslContext.engineGetSocketFactory()), - 443)); - - ClientConnectionManager manager = - new SingleClientConnManager(null, schemeRegistry); - - new DefaultHttpClient(manager, null).execute( - new HttpGet("https://www.verisign.com"), - new ResponseHandler<Object>() { - public Object handleResponse(HttpResponse response) - throws ClientProtocolException, IOException { - return null; - } - }); - } - - private void putSessionsIn(SSLClientSessionCache cache) { - for (int i = 0; i < 10; i++) { - cache.putSessionData(new FakeSession("host" + i), SESSION_DATA); - } - } - - private void deleteDatabase() { - closeDatabase(); - if (!new File(dbDir, "ssl_sessions.db").delete()) { - System.err.println("Failed to delete database."); - } - } - - private void closeDatabase() { - if (DatabaseSessionCache.sDefaultDatabaseHelper != null) { - DatabaseSessionCache.sDefaultDatabaseHelper.close(); - } - DatabaseSessionCache.sDefaultDatabaseHelper = null; - DatabaseSessionCache.sHookInitializationDone = false; - DatabaseSessionCache.mNeedsCacheLoad = true; - } - - private void deleteDirectory() { - closeDirectoryCache(); - - File dir = getCacheDirectory(); - if (!dir.exists()) { - return; - } - for (File file : dir.listFiles()) { - file.delete(); - } - if (!dir.delete()) { - System.err.println("Failed to delete directory."); - } - } - - private void closeDirectoryCache() { - try { - Method reset = FileClientSessionCache.class - .getDeclaredMethod("reset"); - reset.setAccessible(true); - reset.invoke(null); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - } - - private File getCacheDirectory() { - return new File(getContext().getFilesDir(), CACHE_DIR); - } - - class Stopwatch { - { - Debug.startAllocCounting(); - } - long start = System.nanoTime(); - - void stop() { - long elapsed = (System.nanoTime() - start) / 1000; - Debug.stopAllocCounting(); - System.err.println(getName() + ": " + elapsed + "us, " - + Debug.getThreadAllocCount() + " allocations, " - + Debug.getThreadAllocSize() + " bytes"); - } - } -} - -class FakeSession implements SSLSession { - final String host; - - FakeSession(String host) { - this.host = host; - } - - public int getApplicationBufferSize() { - throw new UnsupportedOperationException(); - } - - public String getCipherSuite() { - throw new UnsupportedOperationException(); - } - - public long getCreationTime() { - throw new UnsupportedOperationException(); - } - - public byte[] getId() { - return host.getBytes(); - } - - public long getLastAccessedTime() { - throw new UnsupportedOperationException(); - } - - public Certificate[] getLocalCertificates() { - throw new UnsupportedOperationException(); - } - - public Principal getLocalPrincipal() { - throw new UnsupportedOperationException(); - } - - public int getPacketBufferSize() { - throw new UnsupportedOperationException(); - } - - public javax.security.cert.X509Certificate[] getPeerCertificateChain() { - throw new UnsupportedOperationException(); - } - - public Certificate[] getPeerCertificates() { - throw new UnsupportedOperationException(); - } - - public String getPeerHost() { - return host; - } - - public int getPeerPort() { - return 443; - } - - public Principal getPeerPrincipal() { - throw new UnsupportedOperationException(); - } - - public String getProtocol() { - throw new UnsupportedOperationException(); - } - - public SSLSessionContext getSessionContext() { - throw new UnsupportedOperationException(); - } - - public Object getValue(String name) { - throw new UnsupportedOperationException(); - } - - public String[] getValueNames() { - throw new UnsupportedOperationException(); - } - - public void invalidate() { - throw new UnsupportedOperationException(); - } - - public boolean isValid() { - throw new UnsupportedOperationException(); - } - - public void putValue(String name, Object value) { - throw new UnsupportedOperationException(); - } - - public void removeValue(String name) { - throw new UnsupportedOperationException(); - } -} diff --git a/tests/CoreTests/android/core/Sha1Test.java b/tests/CoreTests/android/core/Sha1Test.java index dcd4c046cdb7..8ed120542a44 100644 --- a/tests/CoreTests/android/core/Sha1Test.java +++ b/tests/CoreTests/android/core/Sha1Test.java @@ -16,8 +16,8 @@ package android.core; -import android.security.MessageDigest; import android.test.suitebuilder.annotation.SmallTest; +import java.security.MessageDigest; import junit.framework.TestCase; /** diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java index 39a4614ed46b..4294254ae5b8 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java @@ -79,7 +79,6 @@ public class FileFilter { "fast/js/regexp-overflow.html", // Result is too large, causing OOM when reading by DRT, http://b/2697589 "fast/regex/test1.html", // Causes DumpRenderTree to hang with V8 "fast/regex/slow.html", // Causes DumpRenderTree to hang with V8 - "http/tests/xmlhttprequest/simple-cross-origin-progress-events.html", // runs webcore into bad state, http://b/2929261 }; static void fillIgnoreResultList() { diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java index 3ea491133ccc..8d3fd1d722d0 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java @@ -36,6 +36,7 @@ import android.os.Message; import android.util.Log; import android.view.ViewGroup; import android.view.Window; +import android.webkit.CookieManager; import android.webkit.ConsoleMessage; import android.webkit.CookieManager; import android.webkit.GeolocationPermissions; @@ -141,6 +142,7 @@ public class TestShellActivity extends Activity implements LayoutTestController contentView.setOrientation(LinearLayout.VERTICAL); setContentView(contentView); + CookieManager.setAcceptFileSchemeCookies(true); mWebView = new WebView(this); mEventSender = new WebViewEventSender(mWebView); mCallbackProxy = new CallbackProxy(mEventSender, this); @@ -941,7 +943,7 @@ public class TestShellActivity extends Activity implements LayoutTestController private boolean mDumpWebKitData = false; static final String TIMEOUT_STR = "**Test timeout"; - static final long DUMP_TIMEOUT_MS = 20000; //20s timeout for dumping webview content + static final long DUMP_TIMEOUT_MS = 100000; // 100s timeout for dumping webview content static final int MSG_TIMEOUT = 0; static final int MSG_WEBKIT_DATA = 1; diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index f72de127d338..ae7ec45903cf 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -32,7 +32,16 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> - + + <activity + android:name="BitmapMeshLayerActivity" + android:label="_BitmapMeshLayer"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity android:name="MarqueeActivity" android:label="_Marquee"> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshActivity.java index 8f98cbb2df32..8cc224651836 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshActivity.java @@ -31,7 +31,6 @@ public class BitmapMeshActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final BitmapMeshView view = new BitmapMeshView(this); - view.setDrawingCacheEnabled(true); setContentView(view); } diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshLayerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshLayerActivity.java new file mode 100644 index 000000000000..ac59a4bcca19 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshLayerActivity.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.hwui; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.os.Bundle; +import android.view.View; + +@SuppressWarnings({"UnusedDeclaration"}) +public class BitmapMeshLayerActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final BitmapMeshView view = new BitmapMeshView(this); + view.setLayerType(View.LAYER_TYPE_HARDWARE, null); + setContentView(view); + } + + static class BitmapMeshView extends View { + private Paint mBitmapPaint; + private final Bitmap mBitmap1; + private float[] mVertices; + private int[] mColors; + + BitmapMeshView(Context c) { + super(c); + + mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1); + + final float width = mBitmap1.getWidth() / 3.0f; + final float height = mBitmap1.getHeight() / 3.0f; + + mVertices = new float[] { + 0.0f, 0.0f, width, 0.0f, width * 2, 0.0f, width * 3, 0.0f, + 0.0f, height, width, height, width * 2, height, width * 4, height, + 0.0f, height * 2, width, height * 2, width * 2, height * 2, width * 3, height * 2, + 0.0f, height * 4, width, height * 4, width * 2, height * 4, width * 4, height * 4, + }; + + mColors = new int[] { + 0xffff0000, 0xff00ff00, 0xff0000ff, 0xffff0000, + 0xff0000ff, 0xffff0000, 0xff00ff00, 0xff00ff00, + 0xff00ff00, 0xff0000ff, 0xffff0000, 0xff00ff00, + 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00ff0000, + }; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + canvas.translate(100, 100); + canvas.drawBitmapMesh(mBitmap1, 3, 3, mVertices, 0, null, 0, null); + + canvas.translate(400, 0); + canvas.drawBitmapMesh(mBitmap1, 3, 3, mVertices, 0, mColors, 0, null); + } + } +} diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath index 2eaf8e3e7ab5..5cfda29ae2e9 100644 --- a/tools/layoutlib/bridge/.classpath +++ b/tools/layoutlib/bridge/.classpath @@ -8,6 +8,6 @@ <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/ninepatch/ninepatch-prebuilt.jar"/> - <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/common/common-prebuilt.jar"/> + <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/tools-common/tools-common-prebuilt.jar"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk index ca7db8ccd63e..7e70f330b7d9 100644 --- a/tools/layoutlib/bridge/Android.mk +++ b/tools/layoutlib/bridge/Android.mk @@ -23,7 +23,7 @@ LOCAL_JAVA_RESOURCE_DIRS := resources LOCAL_JAVA_LIBRARIES := \ kxml2-2.3.0 \ layoutlib_api-prebuilt \ - common-prebuilt + tools-common-prebuilt LOCAL_STATIC_JAVA_LIBRARIES := \ temp_layoutlib \ diff --git a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java index 7d41d1c90d5e..7b444aadb303 100644 --- a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java +++ b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java @@ -17,6 +17,7 @@ package android.animation; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.animation.PropertyValuesHolder @@ -34,20 +35,24 @@ import com.android.layoutlib.bridge.impl.DelegateManager; */ /*package*/ class PropertyValuesHolder_Delegate { + @LayoutlibDelegate /*package*/ static int nGetIntMethod(Class<?> targetClass, String methodName) { // return 0 to force PropertyValuesHolder to use Java reflection. return 0; } + @LayoutlibDelegate /*package*/ static int nGetFloatMethod(Class<?> targetClass, String methodName) { // return 0 to force PropertyValuesHolder to use Java reflection. return 0; } + @LayoutlibDelegate /*package*/ static void nCallIntMethod(Object target, int methodID, int arg) { // do nothing } + @LayoutlibDelegate /*package*/ static void nCallFloatMethod(Object target, int methodID, float arg) { // do nothing } diff --git a/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java b/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java index 60ad645c8967..aabd3f1fbd4e 100644 --- a/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java +++ b/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java @@ -17,6 +17,7 @@ package android.app; import com.android.ide.common.rendering.api.IProjectCallback; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.content.Context; import android.os.Bundle; @@ -48,6 +49,7 @@ public class Fragment_Delegate { * Like {@link #instantiate(Context, String, Bundle)} but with a null * argument Bundle. */ + @LayoutlibDelegate /*package*/ static Fragment instantiate(Context context, String fname) { return instantiate(context, fname, null); } @@ -66,6 +68,7 @@ public class Fragment_Delegate { * the given fragment class. This is a runtime exception; it is not * normally expected to happen. */ + @LayoutlibDelegate /*package*/ static Fragment instantiate(Context context, String fname, Bundle args) { try { if (sProjectCallback != null) { diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java index 03f39801d3b1..413894b02667 100644 --- a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java +++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java @@ -17,6 +17,7 @@ package android.content.res; import com.android.layoutlib.bridge.impl.RenderSessionImpl; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.content.res.Resources.NotFoundException; import android.content.res.Resources.Theme; @@ -32,12 +33,14 @@ import android.util.TypedValue; */ public class Resources_Theme_Delegate { + @LayoutlibDelegate /*package*/ static TypedArray obtainStyledAttributes( Resources thisResources, Theme thisTheme, int[] attrs) { return RenderSessionImpl.getCurrentContext().obtainStyledAttributes(attrs); } + @LayoutlibDelegate /*package*/ static TypedArray obtainStyledAttributes( Resources thisResources, Theme thisTheme, int resid, int[] attrs) @@ -45,6 +48,7 @@ public class Resources_Theme_Delegate { return RenderSessionImpl.getCurrentContext().obtainStyledAttributes(resid, attrs); } + @LayoutlibDelegate /*package*/ static TypedArray obtainStyledAttributes( Resources thisResources, Theme thisTheme, AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) { @@ -52,6 +56,7 @@ public class Resources_Theme_Delegate { set, attrs, defStyleAttr, defStyleRes); } + @LayoutlibDelegate /*package*/ static boolean resolveAttribute( Resources thisResources, Theme thisTheme, int resid, TypedValue outValue, diff --git a/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java index 190eb37d15bb..e1934774f672 100644 --- a/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/AvoidXfermode_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.awt.Composite; @@ -59,6 +60,7 @@ public class AvoidXfermode_Delegate extends Xfermode_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreate(int opColor, int tolerance, int nativeMode) { AvoidXfermode_Delegate newDelegate = new AvoidXfermode_Delegate(); return sManager.addDelegate(newDelegate); diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java index c4fffc86ae36..080b85f743d4 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java @@ -21,6 +21,7 @@ import com.android.layoutlib.bridge.android.BridgeResources.NinePatchInputStream import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.ninepatch.NinePatchChunk; import com.android.resources.Density; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.BitmapFactory.Options; @@ -42,6 +43,7 @@ import java.io.InputStream; // ------ Java delegates ------ + @LayoutlibDelegate /*package*/ static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) { if (bm == null || opts == null) { return bm; @@ -82,10 +84,12 @@ import java.io.InputStream; // ------ Native Delegates ------ + @LayoutlibDelegate /*package*/ static void nativeSetDefaultConfig(int nativeConfig) { // pass } + @LayoutlibDelegate /*package*/ static Bitmap nativeDecodeStream(InputStream is, byte[] storage, Rect padding, Options opts) { Bitmap bm = null; @@ -129,29 +133,34 @@ import java.io.InputStream; return bm; } + @LayoutlibDelegate /*package*/ static Bitmap nativeDecodeFileDescriptor(FileDescriptor fd, Rect padding, Options opts) { opts.inBitmap = null; return null; } + @LayoutlibDelegate /*package*/ static Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts) { opts.inBitmap = null; return null; } + @LayoutlibDelegate /*package*/ static Bitmap nativeDecodeByteArray(byte[] data, int offset, int length, Options opts) { opts.inBitmap = null; return null; } + @LayoutlibDelegate /*package*/ static byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad) { // don't scale for now. This should not be called anyway since we re-implement // BitmapFactory.finishDecode(); return chunk; } + @LayoutlibDelegate /*package*/ static boolean nativeIsSeekable(FileDescriptor fd) { return true; } diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java index 73c5a1aceab4..c6fde7be289b 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java @@ -19,6 +19,7 @@ package android.graphics; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Shader.TileMode; @@ -63,6 +64,7 @@ public class BitmapShader_Delegate extends Shader_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreate(int native_bitmap, int shaderTileModeX, int shaderTileModeY) { Bitmap_Delegate bitmap = Bitmap_Delegate.getDelegate(native_bitmap); @@ -77,6 +79,7 @@ public class BitmapShader_Delegate extends Shader_Delegate { return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static int nativePostCreate(int native_shader, int native_bitmap, int shaderTileModeX, int shaderTileModeY) { // pass, not needed. diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java index 3e8061407017..0c8776628646 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java @@ -20,6 +20,7 @@ import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.resources.Density; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; @@ -196,6 +197,7 @@ public final class Bitmap_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width, int height, int nativeConfig, boolean mutable) { int imageType = getBufferedImageType(nativeConfig); @@ -213,6 +215,7 @@ public final class Bitmap_Delegate { return createBitmap(delegate, mutable, Bitmap.getDefaultDensity()); } + @LayoutlibDelegate /*package*/ static Bitmap nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable) { Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap); if (srcBmpDelegate == null) { @@ -240,14 +243,17 @@ public final class Bitmap_Delegate { return createBitmap(delegate, isMutable, Bitmap.getDefaultDensity()); } + @LayoutlibDelegate /*package*/ static void nativeDestructor(int nativeBitmap) { sManager.removeDelegate(nativeBitmap); } + @LayoutlibDelegate /*package*/ static void nativeRecycle(int nativeBitmap) { sManager.removeDelegate(nativeBitmap); } + @LayoutlibDelegate /*package*/ static boolean nativeCompress(int nativeBitmap, int format, int quality, OutputStream stream, byte[] tempStorage) { Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, @@ -255,6 +261,7 @@ public final class Bitmap_Delegate { return true; } + @LayoutlibDelegate /*package*/ static void nativeErase(int nativeBitmap, int color) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); @@ -274,6 +281,7 @@ public final class Bitmap_Delegate { } } + @LayoutlibDelegate /*package*/ static int nativeWidth(int nativeBitmap) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); @@ -284,6 +292,7 @@ public final class Bitmap_Delegate { return delegate.mImage.getWidth(); } + @LayoutlibDelegate /*package*/ static int nativeHeight(int nativeBitmap) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); @@ -294,6 +303,7 @@ public final class Bitmap_Delegate { return delegate.mImage.getHeight(); } + @LayoutlibDelegate /*package*/ static int nativeRowBytes(int nativeBitmap) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); @@ -304,6 +314,7 @@ public final class Bitmap_Delegate { return delegate.mImage.getWidth(); } + @LayoutlibDelegate /*package*/ static int nativeConfig(int nativeBitmap) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); @@ -314,6 +325,7 @@ public final class Bitmap_Delegate { return delegate.mConfig.nativeInt; } + @LayoutlibDelegate /*package*/ static boolean nativeHasAlpha(int nativeBitmap) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); @@ -324,6 +336,7 @@ public final class Bitmap_Delegate { return delegate.mHasAlpha; } + @LayoutlibDelegate /*package*/ static int nativeGetPixel(int nativeBitmap, int x, int y) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); @@ -334,6 +347,7 @@ public final class Bitmap_Delegate { return delegate.mImage.getRGB(x, y); } + @LayoutlibDelegate /*package*/ static void nativeGetPixels(int nativeBitmap, int[] pixels, int offset, int stride, int x, int y, int width, int height) { Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); @@ -345,6 +359,7 @@ public final class Bitmap_Delegate { } + @LayoutlibDelegate /*package*/ static void nativeSetPixel(int nativeBitmap, int x, int y, int color) { Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); if (delegate == null) { @@ -354,6 +369,7 @@ public final class Bitmap_Delegate { delegate.getImage().setRGB(x, y, color); } + @LayoutlibDelegate /*package*/ static void nativeSetPixels(int nativeBitmap, int[] colors, int offset, int stride, int x, int y, int width, int height) { Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); @@ -364,16 +380,21 @@ public final class Bitmap_Delegate { delegate.getImage().setRGB(x, y, width, height, colors, offset, stride); } + @LayoutlibDelegate /*package*/ static void nativeCopyPixelsToBuffer(int nativeBitmap, Buffer dst) { // FIXME implement native delegate - throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeCopyPixelsToBuffer"); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Bitmap.copyPixelsToBuffer is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void nativeCopyPixelsFromBuffer(int nb, Buffer src) { // FIXME implement native delegate - throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeCopyPixelsFromBuffer"); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Bitmap.copyPixelsFromBuffer is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static int nativeGenerationId(int nativeBitmap) { Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); if (delegate == null) { @@ -383,6 +404,7 @@ public final class Bitmap_Delegate { return delegate.mGenerationId; } + @LayoutlibDelegate /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) { // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only // used during aidl call so really this should not be called. @@ -392,6 +414,7 @@ public final class Bitmap_Delegate { return null; } + @LayoutlibDelegate /*package*/ static boolean nativeWriteToParcel(int nativeBitmap, boolean isMutable, int density, Parcel p) { // This is only called when sending a bitmap through aidl, so really this should not @@ -402,6 +425,7 @@ public final class Bitmap_Delegate { return false; } + @LayoutlibDelegate /*package*/ static Bitmap nativeExtractAlpha(int nativeBitmap, int nativePaint, int[] offsetXY) { Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap); @@ -429,10 +453,12 @@ public final class Bitmap_Delegate { Density.DEFAULT_DENSITY /*density*/); } + @LayoutlibDelegate /*package*/ static void nativePrepareToDraw(int nativeBitmap) { // nothing to be done here. } + @LayoutlibDelegate /*package*/ static void nativeSetHasAlpha(int nativeBitmap, boolean hasAlpha) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); @@ -443,6 +469,7 @@ public final class Bitmap_Delegate { delegate.mHasAlpha = hasAlpha; } + @LayoutlibDelegate /*package*/ static boolean nativeSameAs(int nb0, int nb1) { Bitmap_Delegate delegate1 = sManager.getDelegate(nb0); if (delegate1 == null) { diff --git a/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java index 34824b425fbf..92d0d0af5d3f 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BlurMaskFilter_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.graphics.BlurMaskFilter @@ -53,6 +54,7 @@ public class BlurMaskFilter_Delegate extends MaskFilter_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeConstructor(float radius, int style) { BlurMaskFilter_Delegate newDelegate = new BlurMaskFilter_Delegate(); return sManager.addDelegate(newDelegate); diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 5a6902c074dc..e8a99b5d6711 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -20,6 +20,7 @@ import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.layoutlib.bridge.impl.GcSnapshot; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Bitmap.Config; import android.graphics.Paint_Delegate.FontInfo; @@ -99,6 +100,7 @@ public final class Canvas_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static boolean isOpaque(Canvas thisCanvas) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); @@ -109,6 +111,7 @@ public final class Canvas_Delegate { return canvasDelegate.mBitmap.getConfig() == Config.RGB_565; } + @LayoutlibDelegate /*package*/ static int getWidth(Canvas thisCanvas) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); @@ -119,6 +122,7 @@ public final class Canvas_Delegate { return canvasDelegate.mBitmap.getImage().getWidth(); } + @LayoutlibDelegate /*package*/ static int getHeight(Canvas thisCanvas) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); @@ -129,7 +133,8 @@ public final class Canvas_Delegate { return canvasDelegate.mBitmap.getImage().getHeight(); } - /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) { + @LayoutlibDelegate + /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); if (canvasDelegate == null) { @@ -139,6 +144,7 @@ public final class Canvas_Delegate { canvasDelegate.getSnapshot().translate(dx, dy); } + @LayoutlibDelegate /*package*/ static void rotate(Canvas thisCanvas, float degrees) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); @@ -149,7 +155,8 @@ public final class Canvas_Delegate { canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees)); } - /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) { + @LayoutlibDelegate + /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); if (canvasDelegate == null) { @@ -159,7 +166,8 @@ public final class Canvas_Delegate { canvasDelegate.getSnapshot().scale(sx, sy); } - /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) { + @LayoutlibDelegate + /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); if (canvasDelegate == null) { @@ -182,15 +190,18 @@ public final class Canvas_Delegate { g.setTransform(currentTx); } + @LayoutlibDelegate /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) { return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom); } + @LayoutlibDelegate /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) { return clipRect(thisCanvas, (float) rect.left, (float) rect.top, (float) rect.right, (float) rect.bottom); } + @LayoutlibDelegate /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right, float bottom) { // get the delegate from the native int. @@ -202,16 +213,19 @@ public final class Canvas_Delegate { return canvasDelegate.clipRect(left, top, right, bottom, Region.Op.INTERSECT.nativeInt); } + @LayoutlibDelegate /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right, int bottom) { return clipRect(thisCanvas, (float) left, (float) top, (float) right, (float) bottom); } + @LayoutlibDelegate /*package*/ static int save(Canvas thisCanvas) { return save(thisCanvas, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG); } + @LayoutlibDelegate /*package*/ static int save(Canvas thisCanvas, int saveFlags) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); @@ -222,6 +236,7 @@ public final class Canvas_Delegate { return canvasDelegate.save(saveFlags); } + @LayoutlibDelegate /*package*/ static void restore(Canvas thisCanvas) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); @@ -232,6 +247,7 @@ public final class Canvas_Delegate { canvasDelegate.restore(); } + @LayoutlibDelegate /*package*/ static int getSaveCount(Canvas thisCanvas) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); @@ -242,6 +258,7 @@ public final class Canvas_Delegate { return canvasDelegate.getSnapshot().size(); } + @LayoutlibDelegate /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); @@ -252,17 +269,22 @@ public final class Canvas_Delegate { canvasDelegate.restoreTo(saveCount); } + @LayoutlibDelegate /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count, Paint paint) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Canvas.drawPoint is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Canvas.drawPoint is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void drawLines(Canvas thisCanvas, final float[] pts, final int offset, final int count, Paint paint) { @@ -277,10 +299,12 @@ public final class Canvas_Delegate { }); } + @LayoutlibDelegate /*package*/ static void freeCaches() { // nothing to be done here. } + @LayoutlibDelegate /*package*/ static int initRaster(int nativeBitmapOrZero) { if (nativeBitmapOrZero > 0) { // get the Bitmap from the int @@ -298,6 +322,7 @@ public final class Canvas_Delegate { } } + @LayoutlibDelegate /*package*/ static void native_setBitmap(int nativeCanvas, int bitmap) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); @@ -314,6 +339,7 @@ public final class Canvas_Delegate { canvasDelegate.setBitmap(bitmapDelegate); } + @LayoutlibDelegate /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds, int paint, int layerFlags) { // get the delegate from the native int. @@ -330,6 +356,7 @@ public final class Canvas_Delegate { return canvasDelegate.saveLayer(bounds, paintDelegate, layerFlags); } + @LayoutlibDelegate /*package*/ static int native_saveLayer(int nativeCanvas, float l, float t, float r, float b, int paint, int layerFlags) { @@ -348,6 +375,7 @@ public final class Canvas_Delegate { paintDelegate, layerFlags); } + @LayoutlibDelegate /*package*/ static int native_saveLayerAlpha(int nativeCanvas, RectF bounds, int alpha, int layerFlags) { @@ -360,6 +388,7 @@ public final class Canvas_Delegate { return canvasDelegate.saveLayerAlpha(bounds, alpha, layerFlags); } + @LayoutlibDelegate /*package*/ static int native_saveLayerAlpha(int nativeCanvas, float l, float t, float r, float b, int alpha, int layerFlags) { @@ -373,6 +402,7 @@ public final class Canvas_Delegate { } + @LayoutlibDelegate /*package*/ static void native_concat(int nCanvas, int nMatrix) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); @@ -400,6 +430,7 @@ public final class Canvas_Delegate { snapshot.setTransform(currentTx); } + @LayoutlibDelegate /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); @@ -429,6 +460,7 @@ public final class Canvas_Delegate { } } + @LayoutlibDelegate /*package*/ static boolean native_clipRect(int nCanvas, float left, float top, float right, float bottom, @@ -443,6 +475,7 @@ public final class Canvas_Delegate { return canvasDelegate.clipRect(left, top, right, bottom, regionOp); } + @LayoutlibDelegate /*package*/ static boolean native_clipPath(int nativeCanvas, int nativePath, int regionOp) { @@ -459,6 +492,7 @@ public final class Canvas_Delegate { return canvasDelegate.mSnapshot.clip(pathDelegate.getJavaShape(), regionOp); } + @LayoutlibDelegate /*package*/ static boolean native_clipRegion(int nativeCanvas, int nativeRegion, int regionOp) { @@ -475,6 +509,7 @@ public final class Canvas_Delegate { return canvasDelegate.mSnapshot.clip(region.getJavaArea(), regionOp); } + @LayoutlibDelegate /*package*/ static void nativeSetDrawFilter(int nativeCanvas, int nativeFilter) { Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); @@ -498,6 +533,7 @@ public final class Canvas_Delegate { } } + @LayoutlibDelegate /*package*/ static boolean native_getClipBounds(int nativeCanvas, Rect bounds) { // get the delegate from the native int. @@ -518,6 +554,7 @@ public final class Canvas_Delegate { return false; } + @LayoutlibDelegate /*package*/ static void native_getCTM(int canvas, int matrix) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas); @@ -534,6 +571,7 @@ public final class Canvas_Delegate { matrixDelegate.set(Matrix_Delegate.makeValues(transform)); } + @LayoutlibDelegate /*package*/ static boolean native_quickReject(int nativeCanvas, RectF rect, int native_edgeType) { @@ -541,6 +579,7 @@ public final class Canvas_Delegate { return false; } + @LayoutlibDelegate /*package*/ static boolean native_quickReject(int nativeCanvas, int path, int native_edgeType) { @@ -548,6 +587,7 @@ public final class Canvas_Delegate { return false; } + @LayoutlibDelegate /*package*/ static boolean native_quickReject(int nativeCanvas, float left, float top, float right, float bottom, @@ -556,21 +596,25 @@ public final class Canvas_Delegate { return false; } + @LayoutlibDelegate /*package*/ static void native_drawRGB(int nativeCanvas, int r, int g, int b) { native_drawColor(nativeCanvas, 0xFF000000 | r << 16 | (g&0xFF) << 8 | (b&0xFF), PorterDuff.Mode.SRC_OVER.nativeInt); } + @LayoutlibDelegate /*package*/ static void native_drawARGB(int nativeCanvas, int a, int r, int g, int b) { native_drawColor(nativeCanvas, a << 24 | (r&0xFF) << 16 | (g&0xFF) << 8 | (b&0xFF), PorterDuff.Mode.SRC_OVER.nativeInt); } + @LayoutlibDelegate /*package*/ static void native_drawColor(int nativeCanvas, int color) { native_drawColor(nativeCanvas, color, PorterDuff.Mode.SRC_OVER.nativeInt); } + @LayoutlibDelegate /*package*/ static void native_drawColor(int nativeCanvas, final int color, final int mode) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); @@ -600,11 +644,14 @@ public final class Canvas_Delegate { }); } + @LayoutlibDelegate /*package*/ static void native_drawPaint(int nativeCanvas, int paint) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Canvas.drawPaint is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_drawLine(int nativeCanvas, final float startX, final float startY, final float stopX, final float stopY, int paint) { @@ -617,11 +664,13 @@ public final class Canvas_Delegate { }); } + @LayoutlibDelegate /*package*/ static void native_drawRect(int nativeCanvas, RectF rect, int paint) { native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint); } + @LayoutlibDelegate /*package*/ static void native_drawRect(int nativeCanvas, final float left, final float top, final float right, final float bottom, int paint) { @@ -646,6 +695,7 @@ public final class Canvas_Delegate { }); } + @LayoutlibDelegate /*package*/ static void native_drawOval(int nativeCanvas, final RectF oval, int paint) { if (oval.right > oval.left && oval.bottom > oval.top) { draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, @@ -670,21 +720,23 @@ public final class Canvas_Delegate { } } - /*package*/ static void native_drawCircle(int nativeCanvas, float cx, - float cy, float radius, - int paint) { + @LayoutlibDelegate + /*package*/ static void native_drawCircle(int nativeCanvas, + float cx, float cy, float radius, int paint) { native_drawOval(nativeCanvas, new RectF(cx - radius, cy - radius, radius*2, radius*2), paint); } - /*package*/ static void native_drawArc(int nativeCanvas, RectF oval, - float startAngle, float sweep, - boolean useCenter, int paint) { + @LayoutlibDelegate + /*package*/ static void native_drawArc(int nativeCanvas, + RectF oval, float startAngle, float sweep, boolean useCenter, int paint) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Canvas.drawArc is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_drawRoundRect(int nativeCanvas, final RectF rect, final float rx, final float ry, int paint) { @@ -713,8 +765,8 @@ public final class Canvas_Delegate { }); } - /*package*/ static void native_drawPath(int nativeCanvas, int path, - int paint) { + @LayoutlibDelegate + /*package*/ static void native_drawPath(int nativeCanvas, int path, int paint) { final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path); if (pathDelegate == null) { return; @@ -739,6 +791,7 @@ public final class Canvas_Delegate { }); } + @LayoutlibDelegate /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, float left, float top, int nativePaintOrZero, @@ -760,6 +813,7 @@ public final class Canvas_Delegate { (int)left, (int)top, (int)right, (int)bottom); } + @LayoutlibDelegate /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, Rect src, RectF dst, int nativePaintOrZero, @@ -784,6 +838,7 @@ public final class Canvas_Delegate { } } + @LayoutlibDelegate /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap, Rect src, Rect dst, int nativePaintOrZero, @@ -808,6 +863,7 @@ public final class Canvas_Delegate { } } + @LayoutlibDelegate /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors, int offset, int stride, final float x, final float y, int width, int height, @@ -832,6 +888,7 @@ public final class Canvas_Delegate { }); } + @LayoutlibDelegate /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap, int nMatrix, int nPaint) { // get the delegate from the native int. @@ -871,22 +928,28 @@ public final class Canvas_Delegate { }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/); } + @LayoutlibDelegate /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap, - int meshWidth, int meshHeight, - float[] verts, int vertOffset, - int[] colors, int colorOffset, int nPaint) { + int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, + int colorOffset, int nPaint) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Canvas.drawBitmapMesh is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n, - float[] verts, int vertOffset, float[] texs, int texOffset, - int[] colors, int colorOffset, short[] indices, - int indexOffset, int indexCount, int nPaint) { + float[] verts, int vertOffset, + float[] texs, int texOffset, + int[] colors, int colorOffset, + short[] indices, int indexOffset, + int indexCount, int nPaint) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Canvas.drawVertices is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_drawText(int nativeCanvas, final char[] text, final int index, final int count, final float startX, final float startY, int flags, int paint) { @@ -986,6 +1049,7 @@ public final class Canvas_Delegate { }); } + @LayoutlibDelegate /*package*/ static void native_drawText(int nativeCanvas, String text, int start, int end, float x, float y, int flags, int paint) { @@ -996,6 +1060,7 @@ public final class Canvas_Delegate { native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint); } + @LayoutlibDelegate /*package*/ static void native_drawTextRun(int nativeCanvas, String text, int start, int end, int contextStart, int contextEnd, float x, float y, int flags, int paint) { @@ -1006,27 +1071,33 @@ public final class Canvas_Delegate { native_drawText(nativeCanvas, buffer, start, end, x, y, flags, paint); } + @LayoutlibDelegate /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text, int start, int count, int contextStart, int contextCount, float x, float y, int flags, int paint) { native_drawText(nativeCanvas, text, start, count, x, y, flags, paint); } + @LayoutlibDelegate /*package*/ static void native_drawPosText(int nativeCanvas, char[] text, int index, int count, float[] pos, int paint) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Canvas.drawPosText is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_drawPosText(int nativeCanvas, String text, float[] pos, int paint) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Canvas.drawPosText is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_drawTextOnPath(int nativeCanvas, char[] text, int index, int count, int path, @@ -1034,24 +1105,30 @@ public final class Canvas_Delegate { float vOffset, int bidiFlags, int paint) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Canvas.drawTextOnPath is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_drawTextOnPath(int nativeCanvas, String text, int path, float hOffset, float vOffset, int flags, int paint) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Canvas.drawTextOnPath is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_drawPicture(int nativeCanvas, int nativePicture) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Canvas.drawPicture is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void finalizer(int nativeCanvas) { // get the delegate from the native int so that it can be disposed. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java index 3df170f284e0..789c6e6904bf 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.graphics.ColorFilter @@ -54,6 +55,7 @@ public abstract class ColorFilter_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static void finalizer(int native_instance, int nativeColorFilter) { sManager.removeDelegate(native_instance); } diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java index 42843279ef0b..462b1e640725 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.graphics.ColorMatrixColorFilter @@ -53,11 +54,13 @@ public class ColorMatrixColorFilter_Delegate extends ColorFilter_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeColorMatrixFilter(float[] array) { ColorMatrixColorFilter_Delegate newDelegate = new ColorMatrixColorFilter_Delegate(); return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static int nColorMatrixFilter(int nativeFilter, float[] array) { // pass return 0; diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java index 39cbbc6122b9..2bdaa5bdbf5c 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ComposePathEffect_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.awt.Stroke; @@ -60,6 +61,7 @@ public class ComposePathEffect_Delegate extends PathEffect_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreate(int outerpe, int innerpe) { ComposePathEffect_Delegate newDelegate = new ComposePathEffect_Delegate(); return sManager.addDelegate(newDelegate); diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java index b4baa6f44f7d..a2ecb8f6a1a6 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.awt.Paint; @@ -61,6 +62,7 @@ public class ComposeShader_Delegate extends Shader_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreate1(int native_shaderA, int native_shaderB, int native_mode) { // FIXME not supported yet. @@ -68,6 +70,7 @@ public class ComposeShader_Delegate extends Shader_Delegate { return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static int nativeCreate2(int native_shaderA, int native_shaderB, int porterDuffMode) { // FIXME not supported yet. @@ -75,19 +78,20 @@ public class ComposeShader_Delegate extends Shader_Delegate { return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static int nativePostCreate1(int native_shader, int native_skiaShaderA, int native_skiaShaderB, int native_mode) { // pass, not needed. return 0; } + @LayoutlibDelegate /*package*/ static int nativePostCreate2(int native_shader, int native_skiaShaderA, int native_skiaShaderB, int porterDuffMode) { // pass, not needed. return 0; } - // ---- Private delegate/helper methods ---- } diff --git a/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java index 0307cfb77ea5..c677de83ba7f 100644 --- a/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/CornerPathEffect_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.awt.Stroke; @@ -60,6 +61,7 @@ public class CornerPathEffect_Delegate extends PathEffect_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreate(float radius) { CornerPathEffect_Delegate newDelegate = new CornerPathEffect_Delegate(); return sManager.addDelegate(newDelegate); diff --git a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java index 5a704a70e958..12a4d4a348d9 100644 --- a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.awt.BasicStroke; import java.awt.Stroke; @@ -71,6 +72,7 @@ public final class DashPathEffect_Delegate extends PathEffect_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreate(float intervals[], float phase) { DashPathEffect_Delegate newDelegate = new DashPathEffect_Delegate(intervals, phase); return sManager.addDelegate(newDelegate); diff --git a/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java index 04d71700a5b9..ac6971284583 100644 --- a/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/DiscretePathEffect_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.awt.Stroke; @@ -60,6 +61,7 @@ public class DiscretePathEffect_Delegate extends PathEffect_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreate(float length, float deviation) { DiscretePathEffect_Delegate newDelegate = new DiscretePathEffect_Delegate(); return sManager.addDelegate(newDelegate); diff --git a/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java index ddf20b6247fb..a98f0a941090 100644 --- a/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/DrawFilter_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.graphics.DrawFilter @@ -54,6 +55,7 @@ public abstract class DrawFilter_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static void nativeDestructor(int nativeDrawFilter) { sManager.removeDelegate(nativeDrawFilter); } diff --git a/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java index 82f1da3033f5..31f8bbfb1f8e 100644 --- a/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.graphics.EmbossMaskFilter @@ -53,6 +54,7 @@ public class EmbossMaskFilter_Delegate extends MaskFilter_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeConstructor(float[] direction, float ambient, float specular, float blurRadius) { EmbossMaskFilter_Delegate newDelegate = new EmbossMaskFilter_Delegate(); diff --git a/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java index 132004f97d54..fcb62a8a9592 100644 --- a/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/LayerRasterizer_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.graphics.LayerRasterizer @@ -53,11 +54,13 @@ public class LayerRasterizer_Delegate extends Rasterizer_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeConstructor() { LayerRasterizer_Delegate newDelegate = new LayerRasterizer_Delegate(); return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static void nativeAddLayer(int native_layer, int native_paint, float dx, float dy) { } diff --git a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java index ba2cfad83840..b2725343b791 100644 --- a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.graphics.LightingColorFilter @@ -53,11 +54,13 @@ public class LightingColorFilter_Delegate extends ColorFilter_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int native_CreateLightingFilter(int mul, int add) { LightingColorFilter_Delegate newDelegate = new LightingColorFilter_Delegate(); return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static int nCreateLightingFilter(int nativeFilter, int mul, int add) { // pass return 0; diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java index 9525dcf8e049..80605779fcf9 100644 --- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java @@ -19,6 +19,7 @@ package android.graphics; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Shader.TileMode; @@ -52,6 +53,7 @@ public final class LinearGradient_Delegate extends Gradient_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreate1(LinearGradient thisGradient, float x0, float y0, float x1, float y1, int colors[], float positions[], int tileMode) { @@ -59,6 +61,8 @@ public final class LinearGradient_Delegate extends Gradient_Delegate { colors, positions, Shader_Delegate.getTileMode(tileMode)); return sManager.addDelegate(newDelegate); } + + @LayoutlibDelegate /*package*/ static int nativeCreate2(LinearGradient thisGradient, float x0, float y0, float x1, float y1, int color0, int color1, int tileMode) { @@ -66,12 +70,16 @@ public final class LinearGradient_Delegate extends Gradient_Delegate { x0, y0, x1, y1, new int[] { color0, color1}, null /*positions*/, tileMode); } + + @LayoutlibDelegate /*package*/ static int nativePostCreate1(LinearGradient thisGradient, int native_shader, float x0, float y0, float x1, float y1, int colors[], float positions[], int tileMode) { // nothing to be done here. return 0; } + + @LayoutlibDelegate /*package*/ static int nativePostCreate2(LinearGradient thisGradient, int native_shader, float x0, float y0, float x1, float y1, int color0, int color1, int tileMode) { diff --git a/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java index c582a91f9262..4adca276a7d8 100644 --- a/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/MaskFilter_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.graphics.MaskFilter @@ -54,6 +55,7 @@ public abstract class MaskFilter_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static void nativeDestructor(int native_filter) { sManager.removeDelegate(native_filter); } diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java index 2d77d40f6f54..68a476f170ef 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java @@ -20,6 +20,7 @@ package android.graphics; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Matrix.ScaleToFit; @@ -172,6 +173,7 @@ public final class Matrix_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int native_create(int native_src_or_zero) { // create the delegate Matrix_Delegate newDelegate = new Matrix_Delegate(); @@ -190,6 +192,7 @@ public final class Matrix_Delegate { return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static boolean native_isIdentity(int native_object) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -199,6 +202,7 @@ public final class Matrix_Delegate { return d.isIdentity(); } + @LayoutlibDelegate /*package*/ static boolean native_rectStaysRect(int native_object) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -208,6 +212,7 @@ public final class Matrix_Delegate { return (d.computeTypeMask() & kRectStaysRect_Mask) != 0; } + @LayoutlibDelegate /*package*/ static void native_reset(int native_object) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -217,6 +222,7 @@ public final class Matrix_Delegate { reset(d.mValues); } + @LayoutlibDelegate /*package*/ static void native_set(int native_object, int other) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -231,6 +237,7 @@ public final class Matrix_Delegate { System.arraycopy(src.mValues, 0, d.mValues, 0, MATRIX_SIZE); } + @LayoutlibDelegate /*package*/ static void native_setTranslate(int native_object, float dx, float dy) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -240,6 +247,7 @@ public final class Matrix_Delegate { setTranslate(d.mValues, dx, dy); } + @LayoutlibDelegate /*package*/ static void native_setScale(int native_object, float sx, float sy, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); @@ -250,6 +258,7 @@ public final class Matrix_Delegate { d.mValues = getScale(sx, sy, px, py); } + @LayoutlibDelegate /*package*/ static void native_setScale(int native_object, float sx, float sy) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -267,6 +276,7 @@ public final class Matrix_Delegate { d.mValues[8] = 1; } + @LayoutlibDelegate /*package*/ static void native_setRotate(int native_object, float degrees, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -276,6 +286,7 @@ public final class Matrix_Delegate { d.mValues = getRotate(degrees, px, py); } + @LayoutlibDelegate /*package*/ static void native_setRotate(int native_object, float degrees) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -285,6 +296,7 @@ public final class Matrix_Delegate { setRotate(d.mValues, degrees); } + @LayoutlibDelegate /*package*/ static void native_setSinCos(int native_object, float sinValue, float cosValue, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); @@ -303,6 +315,7 @@ public final class Matrix_Delegate { d.postTransform(getTranslate(px, py)); } + @LayoutlibDelegate /*package*/ static void native_setSinCos(int native_object, float sinValue, float cosValue) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -312,6 +325,7 @@ public final class Matrix_Delegate { setRotate(d.mValues, sinValue, cosValue); } + @LayoutlibDelegate /*package*/ static void native_setSkew(int native_object, float kx, float ky, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); @@ -322,6 +336,7 @@ public final class Matrix_Delegate { d.mValues = getSkew(kx, ky, px, py); } + @LayoutlibDelegate /*package*/ static void native_setSkew(int native_object, float kx, float ky) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -339,6 +354,7 @@ public final class Matrix_Delegate { d.mValues[8] = 1; } + @LayoutlibDelegate /*package*/ static boolean native_setConcat(int native_object, int a, int b) { if (a == native_object) { return native_preConcat(native_object, b); @@ -366,6 +382,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_preTranslate(int native_object, float dx, float dy) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -376,6 +393,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_preScale(int native_object, float sx, float sy, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); @@ -387,6 +405,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_preScale(int native_object, float sx, float sy) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -397,6 +416,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_preRotate(int native_object, float degrees, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); @@ -408,6 +428,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_preRotate(int native_object, float degrees) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -422,6 +443,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_preSkew(int native_object, float kx, float ky, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); @@ -433,6 +455,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_preSkew(int native_object, float kx, float ky) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -443,6 +466,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_preConcat(int native_object, int other_matrix) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -458,6 +482,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_postTranslate(int native_object, float dx, float dy) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -468,6 +493,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_postScale(int native_object, float sx, float sy, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); @@ -479,6 +505,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_postScale(int native_object, float sx, float sy) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -489,6 +516,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_postRotate(int native_object, float degrees, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); @@ -500,6 +528,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_postRotate(int native_object, float degrees) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -510,6 +539,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_postSkew(int native_object, float kx, float ky, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); @@ -521,6 +551,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_postSkew(int native_object, float kx, float ky) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -531,6 +562,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_postConcat(int native_object, int other_matrix) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -546,6 +578,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_setRectToRect(int native_object, RectF src, RectF dst, int stf) { Matrix_Delegate d = sManager.getDelegate(native_object); @@ -610,6 +643,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean native_setPolyToPoly(int native_object, float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount) { // FIXME @@ -619,6 +653,7 @@ public final class Matrix_Delegate { return false; } + @LayoutlibDelegate /*package*/ static boolean native_invert(int native_object, int inverse) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -646,6 +681,7 @@ public final class Matrix_Delegate { } } + @LayoutlibDelegate /*package*/ static void native_mapPoints(int native_object, float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount, boolean isPts) { Matrix_Delegate d = sManager.getDelegate(native_object); @@ -660,6 +696,7 @@ public final class Matrix_Delegate { } } + @LayoutlibDelegate /*package*/ static boolean native_mapRect(int native_object, RectF dst, RectF src) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -669,6 +706,7 @@ public final class Matrix_Delegate { return d.mapRect(dst, src); } + @LayoutlibDelegate /*package*/ static float native_mapRadius(int native_object, float radius) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -684,6 +722,7 @@ public final class Matrix_Delegate { return (float) Math.sqrt(l1 * l2); } + @LayoutlibDelegate /*package*/ static void native_getValues(int native_object, float[] values) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -693,6 +732,7 @@ public final class Matrix_Delegate { System.arraycopy(d.mValues, 0, d.mValues, 0, MATRIX_SIZE); } + @LayoutlibDelegate /*package*/ static void native_setValues(int native_object, float[] values) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { @@ -702,6 +742,7 @@ public final class Matrix_Delegate { System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE); } + @LayoutlibDelegate /*package*/ static boolean native_equals(int native_a, int native_b) { Matrix_Delegate a = sManager.getDelegate(native_a); if (a == null) { @@ -722,6 +763,7 @@ public final class Matrix_Delegate { return true; } + @LayoutlibDelegate /*package*/ static void finalizer(int native_instance) { sManager.removeDelegate(native_instance); } diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java index 61ed71ee36a6..5e882ce44390 100644 --- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java @@ -21,6 +21,7 @@ import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.layoutlib.bridge.impl.GcSnapshot; import com.android.ninepatch.NinePatchChunk; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.drawable.NinePatchDrawable; @@ -137,6 +138,7 @@ public final class NinePatch_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static boolean isNinePatchChunk(byte[] chunk) { NinePatchChunk chunkObject = getChunk(chunk); if (chunkObject != null) { @@ -146,12 +148,14 @@ public final class NinePatch_Delegate { return false; } + @LayoutlibDelegate /*package*/ static void validateNinePatchChunk(int bitmap, byte[] chunk) { // the default JNI implementation only checks that the byte[] has the same // size as the C struct it represent. Since we cannot do the same check (serialization // will return different size depending on content), we do nothing. } + @LayoutlibDelegate /*package*/ static void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance, byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) { draw(canvas_instance, @@ -160,6 +164,7 @@ public final class NinePatch_Delegate { destDensity, srcDensity); } + @LayoutlibDelegate /*package*/ static void nativeDraw(int canvas_instance, Rect loc, int bitmap_instance, byte[] c, int paint_instance_or_null, int destDensity, int srcDensity) { draw(canvas_instance, @@ -168,54 +173,53 @@ public final class NinePatch_Delegate { destDensity, srcDensity); } - private static void draw(int canvas_instance, - final int left, final int top, final int right, final int bottom, - int bitmap_instance, byte[] c, int paint_instance_or_null, - final int destDensity, final int srcDensity) { - // get the delegate from the native int. - final Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance); - if (bitmap_delegate == null) { - return; - } - - if (c == null) { - // not a 9-patch? - BufferedImage image = bitmap_delegate.getImage(); - Canvas_Delegate.native_drawBitmap(canvas_instance, bitmap_instance, - new Rect(0, 0, image.getWidth(), image.getHeight()), - new Rect(left, top, right, bottom), - paint_instance_or_null, destDensity, srcDensity); - return; - } - - final NinePatchChunk chunkObject = getChunk(c); - assert chunkObject != null; - if (chunkObject == null) { - return; - } - - Canvas_Delegate canvas_delegate = Canvas_Delegate.getDelegate(canvas_instance); - if (canvas_delegate == null) { - return; - } - - // this one can be null - Paint_Delegate paint_delegate = Paint_Delegate.getDelegate(paint_instance_or_null); - - canvas_delegate.getSnapshot().draw(new GcSnapshot.Drawable() { - public void draw(Graphics2D graphics, Paint_Delegate paint) { - chunkObject.draw(bitmap_delegate.getImage(), graphics, - left, top, right - left, bottom - top, destDensity, srcDensity); - } - }, paint_delegate, true /*compositeOnly*/, false /*forceSrcMode*/); - - } - + @LayoutlibDelegate /*package*/ static int nativeGetTransparentRegion(int bitmap, byte[] chunk, Rect location) { return 0; } // ---- Private Helper methods ---- + private static void draw(int canvas_instance, + final int left, final int top, final int right, final int bottom, + int bitmap_instance, byte[] c, int paint_instance_or_null, + final int destDensity, final int srcDensity) { + // get the delegate from the native int. + final Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance); + if (bitmap_delegate == null) { + return; + } + + if (c == null) { + // not a 9-patch? + BufferedImage image = bitmap_delegate.getImage(); + Canvas_Delegate.native_drawBitmap(canvas_instance, bitmap_instance, + new Rect(0, 0, image.getWidth(), image.getHeight()), + new Rect(left, top, right, bottom), + paint_instance_or_null, destDensity, srcDensity); + return; + } + + final NinePatchChunk chunkObject = getChunk(c); + assert chunkObject != null; + if (chunkObject == null) { + return; + } + + Canvas_Delegate canvas_delegate = Canvas_Delegate.getDelegate(canvas_instance); + if (canvas_delegate == null) { + return; + } + + // this one can be null + Paint_Delegate paint_delegate = Paint_Delegate.getDelegate(paint_instance_or_null); + + canvas_delegate.getSnapshot().draw(new GcSnapshot.Drawable() { + public void draw(Graphics2D graphics, Paint_Delegate paint) { + chunkObject.draw(bitmap_delegate.getImage(), graphics, + left, top, right - left, bottom - top, destDensity, srcDensity); + } + }, paint_delegate, true /*compositeOnly*/, false /*forceSrcMode*/); + } } diff --git a/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java index ec92507f303e..dfcb5916b8cc 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.graphics.PaintFlagsDrawFilter @@ -53,6 +54,7 @@ public class PaintFlagsDrawFilter_Delegate extends DrawFilter_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeConstructor(int clearBits, int setBits) { PaintFlagsDrawFilter_Delegate newDelegate = new PaintFlagsDrawFilter_Delegate(); return sManager.addDelegate(newDelegate); diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index 87164fb42e0b..f5d2547799d8 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -19,6 +19,7 @@ package android.graphics; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Paint.FontMetrics; import android.graphics.Paint.FontMetricsInt; @@ -241,6 +242,7 @@ public class Paint_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int getFlags(Paint thisPaint) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -251,6 +253,7 @@ public class Paint_Delegate { return delegate.mFlags; } + @LayoutlibDelegate /*package*/ static void setFlags(Paint thisPaint, int flags) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -261,38 +264,47 @@ public class Paint_Delegate { delegate.mFlags = flags; } + @LayoutlibDelegate /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) { setFlag(thisPaint, Paint.FILTER_BITMAP_FLAG, filter); } + @LayoutlibDelegate /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) { setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa); } + @LayoutlibDelegate /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) { setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText); } + @LayoutlibDelegate /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) { setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText); } + @LayoutlibDelegate /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) { setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText); } + @LayoutlibDelegate /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) { setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText); } + @LayoutlibDelegate /*package*/ static void setDither(Paint thisPaint, boolean dither) { setFlag(thisPaint, Paint.DITHER_FLAG, dither); } + @LayoutlibDelegate /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) { setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText); } + @LayoutlibDelegate /*package*/ static int getColor(Paint thisPaint) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -303,6 +315,7 @@ public class Paint_Delegate { return delegate.mColor; } + @LayoutlibDelegate /*package*/ static void setColor(Paint thisPaint, int color) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -313,6 +326,7 @@ public class Paint_Delegate { delegate.mColor = color; } + @LayoutlibDelegate /*package*/ static int getAlpha(Paint thisPaint) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -323,6 +337,7 @@ public class Paint_Delegate { return delegate.getAlpha(); } + @LayoutlibDelegate /*package*/ static void setAlpha(Paint thisPaint, int a) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -333,6 +348,7 @@ public class Paint_Delegate { delegate.setAlpha(a); } + @LayoutlibDelegate /*package*/ static float getStrokeWidth(Paint thisPaint) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -343,6 +359,7 @@ public class Paint_Delegate { return delegate.mStrokeWidth; } + @LayoutlibDelegate /*package*/ static void setStrokeWidth(Paint thisPaint, float width) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -353,6 +370,7 @@ public class Paint_Delegate { delegate.mStrokeWidth = width; } + @LayoutlibDelegate /*package*/ static float getStrokeMiter(Paint thisPaint) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -363,6 +381,7 @@ public class Paint_Delegate { return delegate.mStrokeMiter; } + @LayoutlibDelegate /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -373,6 +392,7 @@ public class Paint_Delegate { delegate.mStrokeMiter = miter; } + @LayoutlibDelegate /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy, int color) { // FIXME @@ -380,6 +400,7 @@ public class Paint_Delegate { "Paint.setShadowLayer is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static float getTextSize(Paint thisPaint) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -390,6 +411,7 @@ public class Paint_Delegate { return delegate.mTextSize; } + @LayoutlibDelegate /*package*/ static void setTextSize(Paint thisPaint, float textSize) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -401,6 +423,7 @@ public class Paint_Delegate { delegate.updateFontObject(); } + @LayoutlibDelegate /*package*/ static float getTextScaleX(Paint thisPaint) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -411,6 +434,7 @@ public class Paint_Delegate { return delegate.mTextScaleX; } + @LayoutlibDelegate /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -422,6 +446,7 @@ public class Paint_Delegate { delegate.updateFontObject(); } + @LayoutlibDelegate /*package*/ static float getTextSkewX(Paint thisPaint) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -432,6 +457,7 @@ public class Paint_Delegate { return delegate.mTextSkewX; } + @LayoutlibDelegate /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -443,6 +469,7 @@ public class Paint_Delegate { delegate.updateFontObject(); } + @LayoutlibDelegate /*package*/ static float ascent(Paint thisPaint) { // get the delegate Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -459,6 +486,7 @@ public class Paint_Delegate { return 0; } + @LayoutlibDelegate /*package*/ static float descent(Paint thisPaint) { // get the delegate Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -475,6 +503,7 @@ public class Paint_Delegate { } + @LayoutlibDelegate /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) { // get the delegate Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -485,6 +514,7 @@ public class Paint_Delegate { return delegate.getFontMetrics(metrics); } + @LayoutlibDelegate /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) { // get the delegate Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -509,6 +539,7 @@ public class Paint_Delegate { return 0; } + @LayoutlibDelegate /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index, int count) { // WARNING: the logic in this method is similar to Canvas.drawText. @@ -523,32 +554,41 @@ public class Paint_Delegate { return delegate.measureText(text, index, count); } + @LayoutlibDelegate /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) { return native_measureText(thisPaint, text.toCharArray(), start, end - start); } + @LayoutlibDelegate /*package*/ static float native_measureText(Paint thisPaint, String text) { return native_measureText(thisPaint, text.toCharArray(), 0, text.length()); } + @LayoutlibDelegate /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count, float maxWidth, float[] measuredWidth) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.native_breakText is not supported.", null, null /*data*/); + return 0; } + @LayoutlibDelegate /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards, float maxWidth, float[] measuredWidth) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.native_breakText is not supported.", null, null /*data*/); + return 0; } - + @LayoutlibDelegate /*package*/ static int native_init() { Paint_Delegate newDelegate = new Paint_Delegate(); return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static int native_initWithPaint(int paint) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(paint); @@ -560,6 +600,7 @@ public class Paint_Delegate { return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static void native_reset(int native_object) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -570,6 +611,7 @@ public class Paint_Delegate { delegate.reset(); } + @LayoutlibDelegate /*package*/ static void native_set(int native_dst, int native_src) { // get the delegate from the native int. Paint_Delegate delegate_dst = sManager.getDelegate(native_dst); @@ -586,6 +628,7 @@ public class Paint_Delegate { delegate_dst.set(delegate_src); } + @LayoutlibDelegate /*package*/ static int native_getStyle(int native_object) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -596,6 +639,7 @@ public class Paint_Delegate { return delegate.mStyle; } + @LayoutlibDelegate /*package*/ static void native_setStyle(int native_object, int style) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -606,6 +650,7 @@ public class Paint_Delegate { delegate.mStyle = style; } + @LayoutlibDelegate /*package*/ static int native_getStrokeCap(int native_object) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -616,6 +661,7 @@ public class Paint_Delegate { return delegate.mCap; } + @LayoutlibDelegate /*package*/ static void native_setStrokeCap(int native_object, int cap) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -626,6 +672,7 @@ public class Paint_Delegate { delegate.mCap = cap; } + @LayoutlibDelegate /*package*/ static int native_getStrokeJoin(int native_object) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -636,6 +683,7 @@ public class Paint_Delegate { return delegate.mJoin; } + @LayoutlibDelegate /*package*/ static void native_setStrokeJoin(int native_object, int join) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -646,6 +694,7 @@ public class Paint_Delegate { delegate.mJoin = join; } + @LayoutlibDelegate /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) { Paint_Delegate paint = sManager.getDelegate(native_object); if (paint == null) { @@ -671,6 +720,7 @@ public class Paint_Delegate { return true; } + @LayoutlibDelegate /*package*/ static int native_setShader(int native_object, int shader) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -681,6 +731,7 @@ public class Paint_Delegate { return delegate.mShader = shader; } + @LayoutlibDelegate /*package*/ static int native_setColorFilter(int native_object, int filter) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -700,6 +751,7 @@ public class Paint_Delegate { return filter; } + @LayoutlibDelegate /*package*/ static int native_setXfermode(int native_object, int xfermode) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -710,6 +762,7 @@ public class Paint_Delegate { return delegate.mXfermode = xfermode; } + @LayoutlibDelegate /*package*/ static int native_setPathEffect(int native_object, int effect) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -720,6 +773,7 @@ public class Paint_Delegate { return delegate.mPathEffect = effect; } + @LayoutlibDelegate /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -739,6 +793,7 @@ public class Paint_Delegate { return maskfilter; } + @LayoutlibDelegate /*package*/ static int native_setTypeface(int native_object, int typeface) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -751,6 +806,7 @@ public class Paint_Delegate { return delegate.mTypeface; } + @LayoutlibDelegate /*package*/ static int native_setRasterizer(int native_object, int rasterizer) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -770,6 +826,7 @@ public class Paint_Delegate { return rasterizer; } + @LayoutlibDelegate /*package*/ static int native_getTextAlign(int native_object) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -780,6 +837,7 @@ public class Paint_Delegate { return delegate.mTextAlign; } + @LayoutlibDelegate /*package*/ static void native_setTextAlign(int native_object, int align) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -790,6 +848,7 @@ public class Paint_Delegate { delegate.mTextAlign = align; } + @LayoutlibDelegate /*package*/ static float native_getFontMetrics(int native_paint, FontMetrics metrics) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_paint); @@ -800,18 +859,25 @@ public class Paint_Delegate { return delegate.getFontMetrics(metrics); } + @LayoutlibDelegate /*package*/ static int native_getTextWidths(int native_object, char[] text, int index, int count, float[] widths) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.getTextWidths is not supported.", null, null /*data*/); + return 0; } + @LayoutlibDelegate /*package*/ static int native_getTextWidths(int native_object, String text, int start, int end, float[] widths) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.getTextWidths is not supported.", null, null /*data*/); + return 0; } + @LayoutlibDelegate /*package*/ static float native_getTextRunAdvances(int native_object, char[] text, int index, int count, int contextIndex, int contextCount, int flags, float[] advances, int advancesIndex) { @@ -855,6 +921,7 @@ public class Paint_Delegate { } + @LayoutlibDelegate /*package*/ static float native_getTextRunAdvances(int native_object, String text, int start, int end, int contextStart, int contextEnd, int flags, float[] advances, int advancesIndex) { @@ -867,42 +934,57 @@ public class Paint_Delegate { contextEnd - contextStart, flags, advances, advancesIndex); } + @LayoutlibDelegate /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text, int contextStart, int contextLength, int flags, int offset, int cursorOpt) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.getTextRunCursor is not supported.", null, null /*data*/); + return 0; } + @LayoutlibDelegate /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text, int contextStart, int contextEnd, int flags, int offset, int cursorOpt) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.getTextRunCursor is not supported.", null, null /*data*/); + return 0; } + @LayoutlibDelegate /*package*/ static void native_getTextPath(int native_object, int bidiFlags, char[] text, int index, int count, float x, float y, int path) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.getTextPath is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_getTextPath(int native_object, int bidiFlags, String text, int start, int end, float x, float y, int path) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.getTextPath is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start, int end, Rect bounds) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.getStringBounds is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index, int count, Rect bounds) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.getCharArrayBounds is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void finalizer(int nativePaint) { sManager.removeDelegate(nativePaint); } diff --git a/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java index d12bfeaae2ea..98a5386067a7 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PathDashPathEffect_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.awt.Stroke; @@ -60,6 +61,7 @@ public class PathDashPathEffect_Delegate extends PathEffect_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreate(int native_path, float advance, float phase, int native_style) { PathDashPathEffect_Delegate newDelegate = new PathDashPathEffect_Delegate(); diff --git a/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java index c5884236d67e..bbbebdd8df2f 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.awt.Stroke; @@ -58,6 +59,7 @@ public abstract class PathEffect_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static void nativeDestructor(int native_patheffect) { sManager.removeDelegate(native_patheffect); } diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index a4e43c111879..9510ce00e214 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -19,6 +19,7 @@ package android.graphics; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Path.Direction; import android.graphics.Path.FillType; @@ -84,6 +85,7 @@ public final class Path_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int init1() { // create the delegate Path_Delegate newDelegate = new Path_Delegate(); @@ -91,6 +93,7 @@ public final class Path_Delegate { return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static int init2(int nPath) { // create the delegate Path_Delegate newDelegate = new Path_Delegate(); @@ -104,6 +107,7 @@ public final class Path_Delegate { return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static void native_reset(int nPath) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -113,12 +117,14 @@ public final class Path_Delegate { pathDelegate.mPath.reset(); } + @LayoutlibDelegate /*package*/ static void native_rewind(int nPath) { // call out to reset since there's nothing to optimize in // terms of data structs. native_reset(nPath); } + @LayoutlibDelegate /*package*/ static void native_set(int native_dst, int native_src) { Path_Delegate pathDstDelegate = sManager.getDelegate(native_dst); if (pathDstDelegate == null) { @@ -133,6 +139,7 @@ public final class Path_Delegate { pathDstDelegate.set(pathSrcDelegate); } + @LayoutlibDelegate /*package*/ static int native_getFillType(int nPath) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -142,6 +149,7 @@ public final class Path_Delegate { return pathDelegate.mFillType.nativeInt; } + @LayoutlibDelegate /*package*/ static void native_setFillType(int nPath, int ft) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -151,6 +159,7 @@ public final class Path_Delegate { pathDelegate.mFillType = Path.sFillTypeArray[ft]; } + @LayoutlibDelegate /*package*/ static boolean native_isEmpty(int nPath) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -160,6 +169,7 @@ public final class Path_Delegate { return pathDelegate.isEmpty(); } + @LayoutlibDelegate /*package*/ static boolean native_isRect(int nPath, RectF rect) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -179,6 +189,7 @@ public final class Path_Delegate { return false; } + @LayoutlibDelegate /*package*/ static void native_computeBounds(int nPath, RectF bounds) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -188,11 +199,13 @@ public final class Path_Delegate { pathDelegate.fillBounds(bounds); } + @LayoutlibDelegate /*package*/ static void native_incReserve(int nPath, int extraPtCount) { // since we use a java2D path, there's no way to pre-allocate new points, // so we do nothing. } + @LayoutlibDelegate /*package*/ static void native_moveTo(int nPath, float x, float y) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -202,6 +215,7 @@ public final class Path_Delegate { pathDelegate.moveTo(x, y); } + @LayoutlibDelegate /*package*/ static void native_rMoveTo(int nPath, float dx, float dy) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -211,6 +225,7 @@ public final class Path_Delegate { pathDelegate.rMoveTo(dx, dy); } + @LayoutlibDelegate /*package*/ static void native_lineTo(int nPath, float x, float y) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -220,6 +235,7 @@ public final class Path_Delegate { pathDelegate.lineTo(x, y); } + @LayoutlibDelegate /*package*/ static void native_rLineTo(int nPath, float dx, float dy) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -229,6 +245,7 @@ public final class Path_Delegate { pathDelegate.rLineTo(dx, dy); } + @LayoutlibDelegate /*package*/ static void native_quadTo(int nPath, float x1, float y1, float x2, float y2) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -238,6 +255,7 @@ public final class Path_Delegate { pathDelegate.quadTo(x1, y1, x2, y2); } + @LayoutlibDelegate /*package*/ static void native_rQuadTo(int nPath, float dx1, float dy1, float dx2, float dy2) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -247,6 +265,7 @@ public final class Path_Delegate { pathDelegate.rQuadTo(dx1, dy1, dx2, dy2); } + @LayoutlibDelegate /*package*/ static void native_cubicTo(int nPath, float x1, float y1, float x2, float y2, float x3, float y3) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); @@ -257,6 +276,7 @@ public final class Path_Delegate { pathDelegate.cubicTo(x1, y1, x2, y2, x3, y3); } + @LayoutlibDelegate /*package*/ static void native_rCubicTo(int nPath, float x1, float y1, float x2, float y2, float x3, float y3) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); @@ -267,6 +287,7 @@ public final class Path_Delegate { pathDelegate.rCubicTo(x1, y1, x2, y2, x3, y3); } + @LayoutlibDelegate /*package*/ static void native_arcTo(int nPath, RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); @@ -277,6 +298,7 @@ public final class Path_Delegate { pathDelegate.arcTo(oval, startAngle, sweepAngle, forceMoveTo); } + @LayoutlibDelegate /*package*/ static void native_close(int nPath) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -286,6 +308,7 @@ public final class Path_Delegate { pathDelegate.close(); } + @LayoutlibDelegate /*package*/ static void native_addRect(int nPath, RectF rect, int dir) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -295,6 +318,7 @@ public final class Path_Delegate { pathDelegate.addRect(rect.left, rect.top, rect.right, rect.bottom, dir); } + @LayoutlibDelegate /*package*/ static void native_addRect(int nPath, float left, float top, float right, float bottom, int dir) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); @@ -305,47 +329,63 @@ public final class Path_Delegate { pathDelegate.addRect(left, top, right, bottom, dir); } + @LayoutlibDelegate /*package*/ static void native_addOval(int nPath, RectF oval, int dir) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Path.addOval is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_addCircle(int nPath, float x, float y, float radius, int dir) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Path.addCircle is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_addArc(int nPath, RectF oval, float startAngle, float sweepAngle) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Path.addArc is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_addRoundRect(int nPath, RectF rect, float rx, float ry, int dir) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Path.addRoundRect is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_addRoundRect(int nPath, RectF r, float[] radii, int dir) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Path.addRoundRect is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_addPath(int nPath, int src, float dx, float dy) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Path.addPath is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_addPath(int nPath, int src) { native_addPath(nPath, src, 0, 0); } + @LayoutlibDelegate /*package*/ static void native_addPath(int nPath, int src, int matrix) { // FIXME - throw new UnsupportedOperationException(); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Path.addPath is not supported.", null, null /*data*/); } + @LayoutlibDelegate /*package*/ static void native_offset(int nPath, float dx, float dy, int dst_path) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -358,10 +398,12 @@ public final class Path_Delegate { pathDelegate.offset(dx, dy, dstDelegate); } + @LayoutlibDelegate /*package*/ static void native_offset(int nPath, float dx, float dy) { native_offset(nPath, dx, dy, 0); } + @LayoutlibDelegate /*package*/ static void native_setLastPoint(int nPath, float dx, float dy) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { @@ -372,6 +414,7 @@ public final class Path_Delegate { pathDelegate.mLastY = dy; } + @LayoutlibDelegate /*package*/ static void native_transform(int nPath, int matrix, int dst_path) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); @@ -390,10 +433,12 @@ public final class Path_Delegate { pathDelegate.transform(matrixDelegate, dstDelegate); } + @LayoutlibDelegate /*package*/ static void native_transform(int nPath, int matrix) { native_transform(nPath, matrix, 0); } + @LayoutlibDelegate /*package*/ static void finalizer(int nPath) { sManager.removeDelegate(nPath); } diff --git a/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java index 516a2b98a214..bbb20e956163 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PixelXorXfermode_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.awt.Composite; @@ -59,6 +60,7 @@ public class PixelXorXfermode_Delegate extends Xfermode_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreate(int opColor) { PixelXorXfermode_Delegate newDelegate = new PixelXorXfermode_Delegate(); return sManager.addDelegate(newDelegate); diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java index 9038636a8b26..33f6c4465527 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.graphics.PorterDuffColorFilter @@ -53,11 +54,13 @@ public class PorterDuffColorFilter_Delegate extends ColorFilter_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int native_CreatePorterDuffFilter(int srcColor, int porterDuffMode) { PorterDuffColorFilter_Delegate newDelegate = new PorterDuffColorFilter_Delegate(); return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static int nCreatePorterDuffFilter(int nativeFilter, int srcColor, int porterDuffMode) { // pass diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java index 147e1d0e7607..116a773c9832 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java @@ -19,6 +19,7 @@ package android.graphics; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.awt.AlphaComposite; import java.awt.Composite; @@ -125,6 +126,7 @@ public class PorterDuffXfermode_Delegate extends Xfermode_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreateXfermode(int mode) { PorterDuffXfermode_Delegate newDelegate = new PorterDuffXfermode_Delegate(mode); return sManager.addDelegate(newDelegate); diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java index ffdf5ddb02c9..8723ed1a6584 100644 --- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java @@ -19,6 +19,7 @@ package android.graphics; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Shader.TileMode; @@ -52,6 +53,7 @@ public class RadialGradient_Delegate extends Gradient_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreate1(float x, float y, float radius, int colors[], float positions[], int tileMode) { RadialGradient_Delegate newDelegate = new RadialGradient_Delegate(x, y, radius, @@ -59,18 +61,21 @@ public class RadialGradient_Delegate extends Gradient_Delegate { return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static int nativeCreate2(float x, float y, float radius, int color0, int color1, int tileMode) { return nativeCreate1(x, y, radius, new int[] { color0, color1 }, null /*positions*/, tileMode); } + @LayoutlibDelegate /*package*/ static int nativePostCreate1(int native_shader, float x, float y, float radius, int colors[], float positions[], int tileMode) { // nothing to be done here. return 0; } + @LayoutlibDelegate /*package*/ static int nativePostCreate2(int native_shader, float x, float y, float radius, int color0, int color1, int tileMode) { // nothing to be done here. diff --git a/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java index 9fd67bead1de..28262787f9ba 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Rasterizer_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.graphics.Rasterizer @@ -54,6 +55,7 @@ public abstract class Rasterizer_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static void finalizer(int native_instance) { sManager.removeDelegate(native_instance); } diff --git a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java index d2b6b27bf543..7b912154930f 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java @@ -19,6 +19,7 @@ package android.graphics; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.os.Parcel; @@ -136,6 +137,7 @@ public class Region_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static boolean isEmpty(Region thisRegion) { Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion); if (regionDelegate == null) { @@ -145,6 +147,7 @@ public class Region_Delegate { return regionDelegate.mArea.isEmpty(); } + @LayoutlibDelegate /*package*/ static boolean isRect(Region thisRegion) { Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion); if (regionDelegate == null) { @@ -154,6 +157,7 @@ public class Region_Delegate { return regionDelegate.mArea.isRectangular(); } + @LayoutlibDelegate /*package*/ static boolean isComplex(Region thisRegion) { Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion); if (regionDelegate == null) { @@ -163,6 +167,7 @@ public class Region_Delegate { return regionDelegate.mArea.isSingular() == false; } + @LayoutlibDelegate /*package*/ static boolean contains(Region thisRegion, int x, int y) { Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion); if (regionDelegate == null) { @@ -172,6 +177,7 @@ public class Region_Delegate { return regionDelegate.mArea.contains(x, y); } + @LayoutlibDelegate /*package*/ static boolean quickContains(Region thisRegion, int left, int top, int right, int bottom) { Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion); @@ -183,6 +189,7 @@ public class Region_Delegate { regionDelegate.mArea.contains(left, top, right - left, bottom - top); } + @LayoutlibDelegate /*package*/ static boolean quickReject(Region thisRegion, int left, int top, int right, int bottom) { Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion); @@ -194,6 +201,7 @@ public class Region_Delegate { regionDelegate.mArea.intersects(left, top, right - left, bottom - top) == false; } + @LayoutlibDelegate /*package*/ static boolean quickReject(Region thisRegion, Region rgn) { Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion); if (regionDelegate == null) { @@ -211,6 +219,7 @@ public class Region_Delegate { } + @LayoutlibDelegate /*package*/ static void translate(Region thisRegion, int dx, int dy, Region dst) { Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion); if (regionDelegate == null) { @@ -232,6 +241,7 @@ public class Region_Delegate { } } + @LayoutlibDelegate /*package*/ static void scale(Region thisRegion, float scale, Region dst) { Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion); if (regionDelegate == null) { @@ -253,15 +263,18 @@ public class Region_Delegate { } } + @LayoutlibDelegate /*package*/ static int nativeConstructor() { Region_Delegate newDelegate = new Region_Delegate(); return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static void nativeDestructor(int native_region) { sManager.removeDelegate(native_region); } + @LayoutlibDelegate /*package*/ static boolean nativeSetRegion(int native_dst, int native_src) { Region_Delegate dstRegion = sManager.getDelegate(native_dst); if (dstRegion == null) { @@ -279,6 +292,7 @@ public class Region_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean nativeSetRect(int native_dst, int left, int top, int right, int bottom) { Region_Delegate dstRegion = sManager.getDelegate(native_dst); @@ -290,6 +304,7 @@ public class Region_Delegate { return dstRegion.mArea.getBounds().isEmpty() == false; } + @LayoutlibDelegate /*package*/ static boolean nativeSetPath(int native_dst, int native_path, int native_clip) { Region_Delegate dstRegion = sManager.getDelegate(native_dst); if (dstRegion == null) { @@ -311,6 +326,7 @@ public class Region_Delegate { return dstRegion.mArea.getBounds().isEmpty() == false; } + @LayoutlibDelegate /*package*/ static boolean nativeGetBounds(int native_region, Rect rect) { Region_Delegate region = sManager.getDelegate(native_region); if (region == null) { @@ -330,6 +346,7 @@ public class Region_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean nativeGetBoundaryPath(int native_region, int native_path) { Region_Delegate region = sManager.getDelegate(native_region); if (region == null) { @@ -350,6 +367,7 @@ public class Region_Delegate { return true; } + @LayoutlibDelegate /*package*/ static boolean nativeOp(int native_dst, int left, int top, int right, int bottom, int op) { Region_Delegate region = sManager.getDelegate(native_dst); @@ -368,6 +386,7 @@ public class Region_Delegate { return region.mArea.getBounds().isEmpty() == false; } + @LayoutlibDelegate /*package*/ static boolean nativeOp(int native_dst, Rect rect, int native_region, int op) { Region_Delegate region = sManager.getDelegate(native_dst); if (region == null) { @@ -385,6 +404,7 @@ public class Region_Delegate { return region.mArea.getBounds().isEmpty() == false; } + @LayoutlibDelegate /*package*/ static boolean nativeOp(int native_dst, int native_region1, int native_region2, int op) { Region_Delegate dstRegion = sManager.getDelegate(native_dst); @@ -413,6 +433,7 @@ public class Region_Delegate { } + @LayoutlibDelegate /*package*/ static int nativeCreateFromParcel(Parcel p) { // This is only called by Region.CREATOR (Parcelable.Creator<Region>), which is only // used during aidl call so really this should not be called. @@ -422,6 +443,7 @@ public class Region_Delegate { return 0; } + @LayoutlibDelegate /*package*/ static boolean nativeWriteToParcel(int native_region, Parcel p) { // This is only called when sending a region through aidl, so really this should not @@ -432,6 +454,7 @@ public class Region_Delegate { return false; } + @LayoutlibDelegate /*package*/ static boolean nativeEquals(int native_r1, int native_r2) { Region_Delegate region1 = sManager.getDelegate(native_r1); if (region1 == null) { @@ -446,6 +469,7 @@ public class Region_Delegate { return region1.mArea.equals(region2.mArea); } + @LayoutlibDelegate /*package*/ static String nativeToString(int native_region) { Region_Delegate region = sManager.getDelegate(native_region); if (region == null) { diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java index 7903ac986dfe..a1b8bdd12fdf 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Shader.TileMode; @@ -74,36 +75,12 @@ public abstract class Shader_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static void nativeDestructor(int native_shader, int native_skiaShader) { sManager.removeDelegate(native_shader); } - /*package*/ static boolean nativeGetLocalMatrix(int native_shader, int matrix_instance) { - // get the delegate from the native int. - Shader_Delegate shaderDelegate = sManager.getDelegate(native_shader); - if (shaderDelegate == null) { - return false; - } - - // can be null if shader has no matrix (int is 0) - Matrix_Delegate localMatrixDelegate = Matrix_Delegate.getDelegate( - shaderDelegate.mLocalMatrix); - - // can be null if the int is 0. - Matrix_Delegate destMatrixDelegate = Matrix_Delegate.getDelegate(matrix_instance); - if (destMatrixDelegate != null) { - if (localMatrixDelegate != null) { - destMatrixDelegate.set(localMatrixDelegate); - } else { - // since there's no local matrix, it's considered to be the identity, reset - // the destination matrix - destMatrixDelegate.reset(); - } - } - - return localMatrixDelegate == null || localMatrixDelegate.isIdentity(); - } - + @LayoutlibDelegate /*package*/ static void nativeSetLocalMatrix(int native_shader, int native_skiaShader, int matrix_instance) { // get the delegate from the native int. diff --git a/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java index 0f92ca517acb..0c9ee48ebd64 100644 --- a/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/SumPathEffect_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.awt.Stroke; @@ -60,6 +61,7 @@ public class SumPathEffect_Delegate extends PathEffect_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreate(int first, int second) { SumPathEffect_Delegate newDelegate = new SumPathEffect_Delegate(); return sManager.addDelegate(newDelegate); diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java index 048990a21a75..382e34c41ad4 100644 --- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java @@ -19,6 +19,7 @@ package android.graphics; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.graphics.SweepGradient @@ -50,21 +51,25 @@ public class SweepGradient_Delegate extends Gradient_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static int nativeCreate1(float x, float y, int colors[], float positions[]) { SweepGradient_Delegate newDelegate = new SweepGradient_Delegate(x, y, colors, positions); return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static int nativeCreate2(float x, float y, int color0, int color1) { return nativeCreate1(x, y, new int[] { color0, color1 }, null /*positions*/); } + @LayoutlibDelegate /*package*/ static int nativePostCreate1(int native_shader, float cx, float cy, int[] colors, float[] positions) { // nothing to be done here. return 0; } + @LayoutlibDelegate /*package*/ static int nativePostCreate2(int native_shader, float cx, float cy, int color0, int color1) { // nothing to be done here. diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java index 00a2a57951dd..1992341d1423 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java @@ -20,6 +20,7 @@ import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.layoutlib.bridge.impl.FontLoader; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.content.res.AssetManager; @@ -88,6 +89,7 @@ public final class Typeface_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static synchronized int nativeCreate(String familyName, int style) { if (familyName == null) { familyName = DEFAULT_FAMILY; @@ -106,6 +108,7 @@ public final class Typeface_Delegate { return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static synchronized int nativeCreateFromTypeface(int native_instance, int style) { Typeface_Delegate delegate = sManager.getDelegate(native_instance); if (delegate == null) { @@ -125,22 +128,26 @@ public final class Typeface_Delegate { return sManager.addDelegate(newDelegate); } + @LayoutlibDelegate /*package*/ static synchronized int nativeCreateFromAsset(AssetManager mgr, String path) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, "Typeface.createFromAsset() is not supported.", null /*throwable*/, null /*data*/); return 0; } + @LayoutlibDelegate /*package*/ static synchronized int nativeCreateFromFile(String path) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, "Typeface.createFromFile() is not supported.", null /*throwable*/, null /*data*/); return 0; } + @LayoutlibDelegate /*package*/ static void nativeUnref(int native_instance) { sManager.removeDelegate(native_instance); } + @LayoutlibDelegate /*package*/ static int nativeGetStyle(int native_instance) { Typeface_Delegate delegate = sManager.getDelegate(native_instance); if (delegate == null) { @@ -150,6 +157,7 @@ public final class Typeface_Delegate { return delegate.mStyle; } + @LayoutlibDelegate /*package*/ static void setGammaForText(float blackGamma, float whiteGamma) { // This is for device testing only: pass } diff --git a/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java index 312318aa44d2..88df02739c6e 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java @@ -17,6 +17,7 @@ package android.graphics; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.awt.Composite; @@ -58,6 +59,7 @@ public abstract class Xfermode_Delegate { // ---- native methods ---- + @LayoutlibDelegate /*package*/ static void finalizer(int native_instance) { sManager.removeDelegate(native_instance); } diff --git a/tools/layoutlib/bridge/src/android/os/Build_Delegate.java b/tools/layoutlib/bridge/src/android/os/Build_Delegate.java index f71860f76ec0..ff82a5e25f55 100644 --- a/tools/layoutlib/bridge/src/android/os/Build_Delegate.java +++ b/tools/layoutlib/bridge/src/android/os/Build_Delegate.java @@ -18,6 +18,7 @@ package android.os; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.util.Map; @@ -33,6 +34,7 @@ import java.util.Map; */ public class Build_Delegate { + @LayoutlibDelegate /*package*/ static String getString(String property) { Map<String, String> properties = Bridge.getPlatformProperties(); String value = properties.get(property); diff --git a/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java b/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java index 4d4ec7f4b6cd..2152c8ad19ae 100644 --- a/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java +++ b/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java @@ -16,6 +16,8 @@ package android.os; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + /** * Delegate overriding selected methods of android.os.Handler @@ -29,6 +31,7 @@ public class Handler_Delegate { // -------- Delegate methods + @LayoutlibDelegate /*package*/ static boolean sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) { // get the callback IHandlerCallback callback = sCallbacks.get(); diff --git a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java index be222fc991de..63711a78e5d8 100644 --- a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java +++ b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java @@ -17,6 +17,7 @@ package android.os; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.os.SystemClock @@ -31,6 +32,7 @@ import com.android.layoutlib.bridge.impl.DelegateManager; public class SystemClock_Delegate { private static long sBootTime = System.currentTimeMillis(); + @LayoutlibDelegate /*package*/ static boolean setCurrentTimeMillis(long millis) { return true; } @@ -42,6 +44,7 @@ public class SystemClock_Delegate { * * @return milliseconds of non-sleep uptime since boot. */ + @LayoutlibDelegate /*package*/ static long uptimeMillis() { return System.currentTimeMillis() - sBootTime; } @@ -51,6 +54,7 @@ public class SystemClock_Delegate { * * @return elapsed milliseconds since boot. */ + @LayoutlibDelegate /*package*/ static long elapsedRealtime() { return System.currentTimeMillis() - sBootTime; } @@ -60,6 +64,7 @@ public class SystemClock_Delegate { * * @return elapsed milliseconds in the thread */ + @LayoutlibDelegate /*package*/ static long currentThreadTimeMillis() { return System.currentTimeMillis(); } diff --git a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java index 9ca133892bbd..1df78c2efaac 100644 --- a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java +++ b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java @@ -17,6 +17,7 @@ package android.util; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /** * Delegate implementing the native methods of android.util.FloatMath @@ -40,6 +41,7 @@ import com.android.layoutlib.bridge.impl.DelegateManager; * @param value to be converted * @return the floor of value */ + @LayoutlibDelegate /*package*/ static float floor(float value) { return (float)Math.floor(value); } @@ -51,6 +53,7 @@ import com.android.layoutlib.bridge.impl.DelegateManager; * @param value to be converted * @return the ceiling of value */ + @LayoutlibDelegate /*package*/ static float ceil(float value) { return (float)Math.ceil(value); } @@ -61,6 +64,7 @@ import com.android.layoutlib.bridge.impl.DelegateManager; * @param angle to compute the cosine of, in radians * @return the sine of angle */ + @LayoutlibDelegate /*package*/ static float sin(float angle) { return (float)Math.sin(angle); } @@ -71,6 +75,7 @@ import com.android.layoutlib.bridge.impl.DelegateManager; * @param angle to compute the cosine of, in radians * @return the cosine of angle */ + @LayoutlibDelegate /*package*/ static float cos(float angle) { return (float)Math.cos(angle); } @@ -82,6 +87,7 @@ import com.android.layoutlib.bridge.impl.DelegateManager; * @param value to compute sqrt of * @return the square root of value */ + @LayoutlibDelegate /*package*/ static float sqrt(float value) { return (float)Math.sqrt(value); } diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java index 3946a2f5d66f..0f3cf571cf8d 100644 --- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java @@ -17,6 +17,7 @@ package android.view; import com.android.layoutlib.bridge.android.BridgeInflater; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -38,6 +39,7 @@ public class LayoutInflater_Delegate { * Recursive method used to descend down the xml hierarchy and instantiate * views, instantiate their children, and then call onFinishInflate(). */ + @LayoutlibDelegate /*package*/ static void rInflate(LayoutInflater thisInflater, XmlPullParser parser, View parent, final AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { diff --git a/tools/layoutlib/bridge/src/android/view/View_Delegate.java b/tools/layoutlib/bridge/src/android/view/View_Delegate.java index ee6694c843c6..8215f7c7a214 100644 --- a/tools/layoutlib/bridge/src/android/view/View_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/View_Delegate.java @@ -16,6 +16,8 @@ package android.view; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + /** * Delegate used to provide new implementation of a select few methods of {@link View} * @@ -25,6 +27,7 @@ package android.view; */ public class View_Delegate { + @LayoutlibDelegate /*package*/ static boolean isInEditMode(View thisView) { return true; } diff --git a/tools/layoutlib/bridge/src/com/android/internal/util/XmlUtils_Delegate.java b/tools/layoutlib/bridge/src/com/android/internal/util/XmlUtils_Delegate.java index 7fa167983814..bf998b8737cd 100644 --- a/tools/layoutlib/bridge/src/com/android/internal/util/XmlUtils_Delegate.java +++ b/tools/layoutlib/bridge/src/com/android/internal/util/XmlUtils_Delegate.java @@ -16,6 +16,8 @@ package com.android.internal.util; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + /** * Delegate used to provide new implementation of a select few methods of {@link XmlUtils} @@ -25,6 +27,8 @@ package com.android.internal.util; * */ public class XmlUtils_Delegate { + + @LayoutlibDelegate /*package*/ static final int convertValueToInt(CharSequence charSeq, int defaultValue) { if (null == charSeq) return defaultValue; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 90dcc2727e7f..acc7379990d4 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -192,6 +192,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { Capability.UNBOUND_RENDERING, Capability.CUSTOM_BACKGROUND_COLOR, Capability.RENDER, + Capability.LAYOUT_ONLY, Capability.EMBEDDED_LAYOUT, Capability.VIEW_MANIPULATION, Capability.PLAY_ANIMATION, diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java index 734f5addccb1..30da2ffc13bc 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java @@ -488,8 +488,11 @@ public final class BridgeTypedArray extends TypedArray { if (f == 0) return 0; if (f > 0) return 1; - throw new UnsupportedOperationException("Can't convert to dimension: " + - Integer.toString(index)); + Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, + "Can't convert to dimension: " + Integer.toString(index), + null, null /*data*/); + + return defValue; } /** diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index fedd789fe258..2fd58e425d9d 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -421,57 +421,63 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // now do the layout. mViewRoot.layout(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight); - mViewRoot.mAttachInfo.mTreeObserver.dispatchOnPreDraw(); - - // draw the views - // create the BufferedImage into which the layout will be rendered. - boolean newImage = false; - if (newRenderSize || mCanvas == null) { - if (params.getImageFactory() != null) { - mImage = params.getImageFactory().getImage( - mMeasuredScreenWidth, - mMeasuredScreenHeight); - } else { - mImage = new BufferedImage( - mMeasuredScreenWidth, - mMeasuredScreenHeight, - BufferedImage.TYPE_INT_ARGB); - newImage = true; + if (params.isLayoutOnly()) { + // delete the canvas and image to reset them on the next full rendering + mImage = null; + mCanvas = null; + } else { + mViewRoot.mAttachInfo.mTreeObserver.dispatchOnPreDraw(); + + // draw the views + // create the BufferedImage into which the layout will be rendered. + boolean newImage = false; + if (newRenderSize || mCanvas == null) { + if (params.getImageFactory() != null) { + mImage = params.getImageFactory().getImage( + mMeasuredScreenWidth, + mMeasuredScreenHeight); + } else { + mImage = new BufferedImage( + mMeasuredScreenWidth, + mMeasuredScreenHeight, + BufferedImage.TYPE_INT_ARGB); + newImage = true; + } + + if (params.isBgColorOverridden()) { + // since we override the content, it's the same as if it was a new image. + newImage = true; + Graphics2D gc = mImage.createGraphics(); + gc.setColor(new Color(params.getOverrideBgColor(), true)); + gc.setComposite(AlphaComposite.Src); + gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight); + gc.dispose(); + } + + // create an Android bitmap around the BufferedImage + Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage, + true /*isMutable*/, params.getDensity()); + + // create a Canvas around the Android bitmap + mCanvas = new Canvas(bitmap); + mCanvas.setDensity(params.getDensity().getDpiValue()); } - if (params.isBgColorOverridden()) { - // since we override the content, it's the same as if it was a new image. - newImage = true; + if (freshRender && newImage == false) { Graphics2D gc = mImage.createGraphics(); - gc.setColor(new Color(params.getOverrideBgColor(), true)); gc.setComposite(AlphaComposite.Src); - gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight); - gc.dispose(); - } - - // create an Android bitmap around the BufferedImage - Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage, - true /*isMutable*/, params.getDensity()); - - // create a Canvas around the Android bitmap - mCanvas = new Canvas(bitmap); - mCanvas.setDensity(params.getDensity().getDpiValue()); - } - if (freshRender && newImage == false) { - Graphics2D gc = mImage.createGraphics(); - gc.setComposite(AlphaComposite.Src); + gc.setColor(new Color(0x00000000, true)); + gc.fillRect(0, 0, + mMeasuredScreenWidth, mMeasuredScreenHeight); - gc.setColor(new Color(0x00000000, true)); - gc.fillRect(0, 0, - mMeasuredScreenWidth, mMeasuredScreenHeight); + // done + gc.dispose(); + } - // done - gc.dispose(); + mViewRoot.draw(mCanvas); } - mViewRoot.draw(mCanvas); - mViewInfoList = startVisitingViews(mViewRoot, 0); // success! diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java index ab01a3949d61..e6dc646487cb 100644 --- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java +++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java @@ -16,6 +16,8 @@ package libcore.icu; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + import java.util.Locale; /** @@ -27,80 +29,101 @@ import java.util.Locale; */ public class ICU_Delegate { + // --- Java delegates + + @LayoutlibDelegate /*package*/ static String toLowerCase(String s, String localeName) { return s.toLowerCase(); } + @LayoutlibDelegate /*package*/ static String toUpperCase(String s, String localeName) { return s.toUpperCase(); } // --- Native methods accessing ICU's database. + @LayoutlibDelegate /*package*/ static String[] getAvailableBreakIteratorLocalesNative() { return new String[0]; } + @LayoutlibDelegate /*package*/ static String[] getAvailableCalendarLocalesNative() { return new String[0]; } + @LayoutlibDelegate /*package*/ static String[] getAvailableCollatorLocalesNative() { return new String[0]; } + @LayoutlibDelegate /*package*/ static String[] getAvailableDateFormatLocalesNative() { return new String[0]; } + @LayoutlibDelegate /*package*/ static String[] getAvailableLocalesNative() { return new String[0]; } + @LayoutlibDelegate /*package*/ static String[] getAvailableNumberFormatLocalesNative() { return new String[0]; } + @LayoutlibDelegate /*package*/ static String getCurrencyCodeNative(String locale) { return ""; } + @LayoutlibDelegate /*package*/ static int getCurrencyFractionDigitsNative(String currencyCode) { return 0; } + @LayoutlibDelegate /*package*/ static String getCurrencySymbolNative(String locale, String currencyCode) { return ""; } + @LayoutlibDelegate /*package*/ static String getDisplayCountryNative(String countryCode, String locale) { return ""; } + @LayoutlibDelegate /*package*/ static String getDisplayLanguageNative(String languageCode, String locale) { return ""; } + @LayoutlibDelegate /*package*/ static String getDisplayVariantNative(String variantCode, String locale) { return ""; } + @LayoutlibDelegate /*package*/ static String getISO3CountryNative(String locale) { return ""; } + @LayoutlibDelegate /*package*/ static String getISO3LanguageNative(String locale) { return ""; } + @LayoutlibDelegate /*package*/ static String[] getISOLanguagesNative() { return Locale.getISOLanguages(); } + @LayoutlibDelegate /*package*/ static String[] getISOCountriesNative() { return Locale.getISOCountries(); } + @LayoutlibDelegate /*package*/ static boolean initLocaleDataImpl(String locale, LocaleData result) { // Used by Calendar. diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java deleted file mode 100644 index a068ae273651..000000000000 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.layoutlib.bridge; - -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Type; - -import junit.framework.TestCase; - -public class TestClassReplacement extends TestCase { - - public void testClassReplacements() { - // TODO: we want to test all the classes. For now only, no classes pass the test. -// final String[] classes = CreateInfo.RENAMED_CLASSES; - final String[] classes = new String[] { -// "android.graphics.Paint", "android.graphics._Original_Paint", -// "android.graphics.Canvas", "android.graphics._Original_Canvas", - }; - final int count = classes.length; - for (int i = 0 ; i < count ; i += 2) { - loadAndCompareClasses(classes[i], classes[i+1]); - } - } - - private void loadAndCompareClasses(String newClassName, String oldClassName) { - // load the classes - try { - Class<?> newClass = TestClassReplacement.class.getClassLoader().loadClass(newClassName); - Class<?> oldClass = TestClassReplacement.class.getClassLoader().loadClass(oldClassName); - - compare(newClass, oldClass); - } catch (ClassNotFoundException e) { - fail("Failed to load class: " + e.getMessage()); - } - } - - private void compare(Class<?> newClass, Class<?> oldClass) { - // first compare the methods. - Method[] newClassMethods = newClass.getDeclaredMethods(); - Method[] oldClassMethods = oldClass.getDeclaredMethods(); - - for (Method oldMethod : oldClassMethods) { - // we ignore anything that starts with native. This is because the class we are looking - // at has already been modified to remove the native modifiers. - if (oldMethod.getName().startsWith("native")) { - continue; - } - - // or static and private - int privateStatic = Modifier.STATIC | Modifier.PRIVATE; - if ((oldMethod.getModifiers() & privateStatic) == privateStatic) { - continue; - } - - boolean found = false; - for (Method newMethod : newClassMethods) { - - if (compareMethods(newClass, newMethod, oldClass, oldMethod)) { - found = true; - break; - } - } - - if (found == false) { - // compute a full class name that's long but not too long. - StringBuilder sb = new StringBuilder(oldMethod.getName() + "("); - Type[] params = oldMethod.getGenericParameterTypes(); - for (int j = 0; j < params.length; j++) { - if (params[j] instanceof Class) { - Class theClass = (Class)params[j]; - sb.append(theClass.getName()); - int dimensions = 0; - while (theClass.isArray()) { - dimensions++; - theClass = theClass.getComponentType(); - } - for (int i = 0; i < dimensions; i++) { - sb.append("[]"); - } - - } else { - sb.append(params[j].toString()); - } - if (j < (params.length - 1)) - sb.append(","); - } - sb.append(")"); - - fail(String.format("Missing %1$s.%2$s", newClass.getName(), sb.toString())); - } - } - - // TODO: check (somehow?) that the methods that were removed from the original class - // have been put back in the new class! - // For this we need the original unmodified class (ie renamed, but w/o the methods removed) - } - - private boolean compareMethods(Class<?> newClass, Method newMethod, - Class<?> oldClass, Method oldMethod) { - // first check the name of the method - if (newMethod.getName().equals(oldMethod.getName()) == false) { - return false; - } - - // check the return value - Class<?> oldReturnType = oldMethod.getReturnType(); - // if it's the old class, or if it's a inner class of the oldclass, we need to change this. - oldReturnType = adapt(oldReturnType, newClass, oldClass); - - // compare the return types - Class<?> newReturnType = newMethod.getReturnType(); - if (newReturnType.equals(oldReturnType) == false) { - return false; - } - - // now check the parameters type. - Class<?>[] oldParameters = oldMethod.getParameterTypes(); - Class<?>[] newParemeters = newMethod.getParameterTypes(); - if (oldParameters.length != newParemeters.length) { - return false; - } - - for (int i = 0 ; i < oldParameters.length ; i++) { - if (newParemeters[i].equals(adapt(oldParameters[i], newClass, oldClass)) == false) { - return false; - } - } - - return true; - } - - /** - * Adapts a class to deal with renamed classes. - * <p/>For instance if old class is <code>android.graphics._Original_Paint</code> and the - * new class is <code>android.graphics.Paint</code> and the class to adapt is - * <code>android.graphics._Original_Paint$Cap</code>, then the method will return a - * {@link Class} object representing <code>android.graphics.Paint$Cap</code>. - * <p/> - * This method will also ensure that all renamed classes contains all the proper inner classes - * that they should be declaring. - * @param theClass the class to adapt - * @param newClass the new class object - * @param oldClass the old class object - * @return the adapted class. - * @throws ClassNotFoundException - */ - private Class<?> adapt(Class<?> theClass, Class<?> newClass, Class<?> oldClass) { - // only look for a new class if it's not primitive as Class.forName() would fail otherwise. - if (theClass.isPrimitive() == false) { - String n = theClass.getName().replace(oldClass.getName(), newClass.getName()); - try { - return Class.forName(n); - } catch (ClassNotFoundException e) { - fail("Missing class: " + n); - } - } - - return theClass; - } -} diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java index a4140e3cc906..d3218dbd5bd3 100644 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestDelegates.java @@ -21,6 +21,8 @@ import com.android.tools.layoutlib.create.CreateInfo; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; import junit.framework.TestCase; @@ -78,10 +80,15 @@ public class TestDelegates extends TestCase { } private void compare(Class<?> originalClass, Class<?> delegateClass) throws SecurityException { - Method[] originalMethods = originalClass.getDeclaredMethods(); + List<Method> checkedDelegateMethods = new ArrayList<Method>(); + // loop on the methods of the original class, and for the ones that are annotated + // with @LayoutlibDelegate, look for a matching method in the delegate class. + // The annotation is automatically added by layoutlib_create when it replace a method + // by a call to a delegate + Method[] originalMethods = originalClass.getDeclaredMethods(); for (Method originalMethod : originalMethods) { - // look for methods that were native: they have the LayoutlibDelegate annotation + // look for methods that are delegated: they have the LayoutlibDelegate annotation if (originalMethod.getAnnotation(LayoutlibDelegate.class) == null) { continue; } @@ -114,6 +121,14 @@ public class TestDelegates extends TestCase { Method delegateMethod = delegateClass.getDeclaredMethod(originalMethod.getName(), parameters); + // check that the method has the annotation + assertNotNull( + String.format( + "Delegate method %1$s for class %2$s does not have the @LayoutlibDelegate annotation", + delegateMethod.getName(), + originalClass.getName()), + delegateMethod.getAnnotation(LayoutlibDelegate.class)); + // check that the method is static assertTrue( String.format( @@ -121,28 +136,62 @@ public class TestDelegates extends TestCase { delegateMethod.getName(), originalClass.getName()), (delegateMethod.getModifiers() & Modifier.STATIC) == Modifier.STATIC); + + // add the method as checked. + checkedDelegateMethods.add(delegateMethod); } catch (NoSuchMethodException e) { - // compute a full class name that's long but not too long. - StringBuilder sb = new StringBuilder(originalMethod.getName() + "("); - for (int j = 0; j < parameters.length; j++) { - Class<?> theClass = parameters[j]; - sb.append(theClass.getName()); - int dimensions = 0; - while (theClass.isArray()) { - dimensions++; - theClass = theClass.getComponentType(); - } - for (int i = 0; i < dimensions; i++) { - sb.append("[]"); - } - if (j < (parameters.length - 1)) { - sb.append(","); - } - } - sb.append(")"); - - fail(String.format("Missing %1$s.%2$s", delegateClass.getName(), sb.toString())); + String name = getMethodName(originalMethod, parameters); + fail(String.format("Missing %1$s.%2$s", delegateClass.getName(), name)); + } + } + + // look for dead (delegate) code. + // This looks for all methods in the delegate class, and if they have the + // @LayoutlibDelegate annotation, make sure they have been previously found as a + // match for a method in the original class. + // If not, this means the method is a delegate for a method that either doesn't exist + // anymore or is not delegated anymore. + Method[] delegateMethods = delegateClass.getDeclaredMethods(); + for (Method delegateMethod : delegateMethods) { + // look for methods that are delegates: they have the LayoutlibDelegate annotation + if (delegateMethod.getAnnotation(LayoutlibDelegate.class) == null) { + continue; } + + assertTrue( + String.format( + "Delegate method %1$s.%2$s is not used anymore and must be removed", + delegateClass.getName(), + getMethodName(delegateMethod)), + checkedDelegateMethods.contains(delegateMethod)); } + + } + + private String getMethodName(Method method) { + return getMethodName(method, method.getParameterTypes()); + } + + private String getMethodName(Method method, Class<?>[] parameters) { + // compute a full class name that's long but not too long. + StringBuilder sb = new StringBuilder(method.getName() + "("); + for (int j = 0; j < parameters.length; j++) { + Class<?> theClass = parameters[j]; + sb.append(theClass.getName()); + int dimensions = 0; + while (theClass.isArray()) { + dimensions++; + theClass = theClass.getComponentType(); + } + for (int i = 0; i < dimensions; i++) { + sb.append("[]"); + } + if (j < (parameters.length - 1)) { + sb.append(","); + } + } + sb.append(")"); + + return sb.toString(); } } diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java index c7e18dfbae65..34d91dd7f84f 100644 --- a/voip/java/android/net/sip/SipProfile.java +++ b/voip/java/android/net/sip/SipProfile.java @@ -150,9 +150,8 @@ public class SipProfile implements Parcelable, Serializable, Cloneable { /** * Sets the username used for authentication. * - * @param name auth. name of the profile + * @param name authentication username of the profile * @return this builder object - * @hide // TODO: remove when we make it public */ public Builder setAuthUserName(String name) { mProfile.mAuthUserName = name; @@ -391,10 +390,10 @@ public class SipProfile implements Parcelable, Serializable, Cloneable { /** * Gets the username for authentication. If it is null, then the username - * should be used in authentication instead. + * is used in authentication instead. * - * @return the auth. username - * @hide // TODO: remove when we make it public + * @return the authentication username + * @see #getUserName */ public String getAuthUserName() { return mAuthUserName; diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 847577fea4ff..7a9276d8e434 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -1,16 +1,16 @@ /** * 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 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and * limitations under the License. */ @@ -23,6 +23,7 @@ import android.net.wifi.WpsResult; import android.net.wifi.ScanResult; import android.net.DhcpInfo; +import android.os.Messenger; import android.os.WorkSource; /** @@ -111,5 +112,7 @@ interface IWifiManager void forgetNetwork(int networkId); WpsResult startWps(in WpsConfiguration config); + + Messenger getMessenger(); } diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java index 73c24cf02397..d4117159477b 100644 --- a/wifi/java/android/net/wifi/WifiConfigStore.java +++ b/wifi/java/android/net/wifi/WifiConfigStore.java @@ -18,7 +18,7 @@ package android.net.wifi; import android.content.Context; import android.content.Intent; -import android.net.DhcpInfo; +import android.net.DhcpInfoInternal; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkUtils; @@ -436,35 +436,28 @@ class WifiConfigStore { * that, we should remove handling DhcpInfo and move * to using LinkProperties */ - static DhcpInfo getIpConfiguration(int netId) { - DhcpInfo dhcpInfo = new DhcpInfo(); + static DhcpInfoInternal getIpConfiguration(int netId) { + DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal(); LinkProperties linkProperties = getLinkProperties(netId); if (linkProperties != null) { Iterator<LinkAddress> iter = linkProperties.getLinkAddresses().iterator(); if (iter.hasNext()) { - try { - LinkAddress linkAddress = iter.next(); - dhcpInfo.ipAddress = NetworkUtils.inetAddressToInt( - linkAddress.getAddress()); - dhcpInfo.gateway = NetworkUtils.inetAddressToInt( - linkProperties.getGateway()); - dhcpInfo.netmask = NetworkUtils.prefixLengthToNetmaskInt( - linkAddress.getNetworkPrefixLength()); - Iterator<InetAddress> dnsIterator = linkProperties.getDnses().iterator(); - dhcpInfo.dns1 = NetworkUtils.inetAddressToInt(dnsIterator.next()); - if (dnsIterator.hasNext()) { - dhcpInfo.dns2 = NetworkUtils.inetAddressToInt(dnsIterator.next()); - } - } catch (IllegalArgumentException e1) { - Log.e(TAG, "IPv6 address cannot be handled " + e1); - } catch (NullPointerException e2) { - /* Should not happen since a stored static config should be valid */ - Log.e(TAG, "Invalid partial IP configuration " + e2); + LinkAddress linkAddress = iter.next(); + dhcpInfoInternal.ipAddress = linkAddress.getAddress().getHostAddress(); + Iterator<InetAddress>gateways = linkProperties.getGateways().iterator(); + if (gateways.hasNext()) { + dhcpInfoInternal.gateway = gateways.next().getHostAddress(); + } + dhcpInfoInternal.prefixLength = linkAddress.getNetworkPrefixLength(); + Iterator<InetAddress> dnsIterator = linkProperties.getDnses().iterator(); + dhcpInfoInternal.dns1 = dnsIterator.next().getHostAddress(); + if (dnsIterator.hasNext()) { + dhcpInfoInternal.dns2 = dnsIterator.next().getHostAddress(); } } } - return dhcpInfo; + return dhcpInfoInternal; } /** @@ -592,8 +585,7 @@ class WifiConfigStore { out.writeUTF(linkAddr.getAddress().getHostAddress()); out.writeInt(linkAddr.getNetworkPrefixLength()); } - InetAddress gateway = linkProperties.getGateway(); - if (gateway != null) { + for (InetAddress gateway : linkProperties.getGateways()) { out.writeUTF(GATEWAY_KEY); out.writeUTF(gateway.getHostAddress()); } @@ -698,7 +690,7 @@ class WifiConfigStore { in.readUTF()), in.readInt()); linkProperties.addLinkAddress(linkAddr); } else if (key.equals(GATEWAY_KEY)) { - linkProperties.setGateway(InetAddress.getByName(in.readUTF())); + linkProperties.addGateway(InetAddress.getByName(in.readUTF())); } else if (key.equals(DNS_KEY)) { linkProperties.addDns(InetAddress.getByName(in.readUTF())); } else if (key.equals(PROXY_SETTINGS_KEY)) { @@ -1009,15 +1001,17 @@ class WifiConfigStore { .getLinkAddresses(); Collection<InetAddress> currentDnses = currentConfig.linkProperties.getDnses(); Collection<InetAddress> newDnses = newConfig.linkProperties.getDnses(); - InetAddress currentGateway = currentConfig.linkProperties.getGateway(); - InetAddress newGateway = newConfig.linkProperties.getGateway(); - - boolean linkAddressesDiffer = !currentLinkAddresses.containsAll(newLinkAddresses) || - (currentLinkAddresses.size() != newLinkAddresses.size()); - boolean dnsesDiffer = !currentDnses.containsAll(newDnses) || - (currentDnses.size() != newDnses.size()); - boolean gatewaysDiffer = (currentGateway == null) || - !currentGateway.equals(newGateway); + Collection<InetAddress> currentGateways = + currentConfig.linkProperties.getGateways(); + Collection<InetAddress> newGateways = newConfig.linkProperties.getGateways(); + + boolean linkAddressesDiffer = + (currentLinkAddresses.size() != newLinkAddresses.size()) || + !currentLinkAddresses.containsAll(newLinkAddresses); + boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) || + !currentDnses.containsAll(newDnses); + boolean gatewaysDiffer = (currentGateways.size() != newGateways.size()) || + !currentGateways.containsAll(newGateways); if ((currentConfig.ipAssignment != newConfig.ipAssignment) || linkAddressesDiffer || @@ -1097,7 +1091,9 @@ class WifiConfigStore { for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) { linkProperties.addLinkAddress(linkAddr); } - linkProperties.setGateway(config.linkProperties.getGateway()); + for (InetAddress gateway : config.linkProperties.getGateways()) { + linkProperties.addGateway(gateway); + } for (InetAddress dns : config.linkProperties.getDnses()) { linkProperties.addDns(dns); } diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index d5fb63e71412..28a5bc60609e 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -107,9 +107,16 @@ public class WifiConfiguration implements Parcelable { * generated WEP keys. */ public static final int IEEE8021X = 3; + /** WPA2 pre-shared key for use with soft access point + * (requires {@code preSharedKey} to be specified). + * @hide + */ + public static final int WPA2_PSK = 4; + public static final String varName = "key_mgmt"; - public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X" }; + public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X", + "WPA2_PSK" }; } /** @@ -480,6 +487,20 @@ public class WifiConfiguration implements Parcelable { dest.writeInt(nextSetBit); } + /** @hide */ + public int getAuthType() { + if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { + return KeyMgmt.WPA_PSK; + } else if (allowedKeyManagement.get(KeyMgmt.WPA2_PSK)) { + return KeyMgmt.WPA2_PSK; + } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)) { + return KeyMgmt.WPA_EAP; + } else if (allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { + return KeyMgmt.IEEE8021X; + } + return KeyMgmt.NONE; + } + /** Implement the Parcelable interface {@hide} */ public int describeContents() { return 0; diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index 4312bfdb35db..f60ae4835f85 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -19,7 +19,11 @@ package android.net.wifi; import android.os.Parcelable; import android.os.Parcel; import android.net.NetworkInfo.DetailedState; +import android.net.NetworkUtils; +import java.net.InetAddress; +import java.net.Inet6Address; +import java.net.UnknownHostException; import java.util.EnumMap; /** @@ -61,7 +65,7 @@ public class WifiInfo implements Parcelable { public static final String LINK_SPEED_UNITS = "Mbps"; private int mLinkSpeed; - private int mIpAddress; + private InetAddress mIpAddress; private String mMacAddress; @@ -72,7 +76,6 @@ public class WifiInfo implements Parcelable { mSupplicantState = SupplicantState.UNINITIALIZED; mRssi = -9999; mLinkSpeed = -1; - mIpAddress = 0; mHiddenSSID = false; } @@ -172,12 +175,13 @@ public class WifiInfo implements Parcelable { mSupplicantState = state; } - void setIpAddress(int address) { + void setInetAddress(InetAddress address) { mIpAddress = address; } public int getIpAddress() { - return mIpAddress; + if (mIpAddress == null || mIpAddress instanceof Inet6Address) return 0; + return NetworkUtils.inetAddressToInt(mIpAddress); } /** @@ -251,7 +255,12 @@ public class WifiInfo implements Parcelable { dest.writeInt(mNetworkId); dest.writeInt(mRssi); dest.writeInt(mLinkSpeed); - dest.writeInt(mIpAddress); + if (mIpAddress != null) { + dest.writeByte((byte)1); + dest.writeByteArray(mIpAddress.getAddress()); + } else { + dest.writeByte((byte)0); + } dest.writeString(getSSID()); dest.writeString(mBSSID); dest.writeString(mMacAddress); @@ -266,7 +275,11 @@ public class WifiInfo implements Parcelable { info.setNetworkId(in.readInt()); info.setRssi(in.readInt()); info.setLinkSpeed(in.readInt()); - info.setIpAddress(in.readInt()); + if (in.readByte() == 1) { + try { + info.setInetAddress(InetAddress.getByAddress(in.createByteArray())); + } catch (UnknownHostException e) {} + } info.setSSID(in.readString()); info.mBSSID = in.readString(); info.mMacAddress = in.readString(); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index d05918f51e39..0807a241b29d 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -24,6 +24,7 @@ import android.os.IBinder; import android.os.Handler; import android.os.RemoteException; import android.os.WorkSource; +import android.os.Messenger; import java.util.List; @@ -60,7 +61,7 @@ public class WifiManager { * Broadcast intent action indicating that Wi-Fi has been enabled, disabled, * enabling, disabling, or unknown. One extra provides this state as an int. * Another extra provides the previous state, if available. - * + * * @see #EXTRA_WIFI_STATE * @see #EXTRA_PREVIOUS_WIFI_STATE */ @@ -71,7 +72,7 @@ public class WifiManager { * The lookup key for an int that indicates whether Wi-Fi is enabled, * disabled, enabling, disabling, or unknown. Retrieve it with * {@link android.content.Intent#getIntExtra(String,int)}. - * + * * @see #WIFI_STATE_DISABLED * @see #WIFI_STATE_DISABLING * @see #WIFI_STATE_ENABLED @@ -81,22 +82,22 @@ public class WifiManager { public static final String EXTRA_WIFI_STATE = "wifi_state"; /** * The previous Wi-Fi state. - * + * * @see #EXTRA_WIFI_STATE */ public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state"; - + /** * Wi-Fi is currently being disabled. The state will change to {@link #WIFI_STATE_DISABLED} if * it finishes successfully. - * + * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ public static final int WIFI_STATE_DISABLING = 0; /** * Wi-Fi is disabled. - * + * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ @@ -104,14 +105,14 @@ public class WifiManager { /** * Wi-Fi is currently being enabled. The state will change to {@link #WIFI_STATE_ENABLED} if * it finishes successfully. - * + * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ public static final int WIFI_STATE_ENABLING = 2; /** * Wi-Fi is enabled. - * + * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ @@ -119,7 +120,7 @@ public class WifiManager { /** * Wi-Fi is in an unknown state. This state will occur when an error happens while enabling * or disabling. - * + * * @see #WIFI_STATE_CHANGED_ACTION * @see #getWifiState() */ @@ -356,16 +357,6 @@ public class WifiManager { public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"; /** - * In this Wi-Fi lock mode, Wi-Fi will behave as in the mode - * {@link #WIFI_MODE_FULL} but it operates at high performance - * at the expense of power. This mode should be used - * only when the wifi connection needs to have minimum loss and low - * latency as it can impact the battery life. - * @hide - */ - public static final int WIFI_MODE_FULL_HIGH_PERF = 3; - - /** * In this Wi-Fi lock mode, Wi-Fi will be kept active, * and will behave normally, i.e., it will attempt to automatically * establish a connection to a remembered access point that is @@ -383,10 +374,29 @@ public class WifiManager { * an application in this mode. */ public static final int WIFI_MODE_SCAN_ONLY = 2; + /** + * In this Wi-Fi lock mode, Wi-Fi will be kept active as in mode + * {@link #WIFI_MODE_FULL} but it operates at high performance + * with minimum packet loss and low packet latency even when + * the device screen is off. This mode will consume more power + * and hence should be used only when there is a need for such + * an active connection. + * <p> + * An example use case is when a voice connection needs to be + * kept active even after the device screen goes off. Holding the + * regular {@link #WIFI_MODE_FULL} lock will keep the wifi + * connection active, but the connection can be lossy. + * Holding a {@link #WIFI_MODE_FULL_HIGH_PERF} lock for the + * duration of the voice call will improve the call quality. + * <p> + * When there is no support from the hardware, this lock mode + * will have the same behavior as {@link #WIFI_MODE_FULL} + */ + public static final int WIFI_MODE_FULL_HIGH_PERF = 3; /** Anything worse than or equal to this will show 0 bars. */ private static final int MIN_RSSI = -100; - + /** Anything better than or equal to this will show the max bars. */ private static final int MAX_RSSI = -55; @@ -409,6 +419,22 @@ public class WifiManager { */ public static final int WIFI_FREQUENCY_BAND_2GHZ = 2; + /** List of asyncronous notifications + * @hide + */ + public static final int DATA_ACTIVITY_NOTIFICATION = 1; + + //Lowest bit indicates data reception and the second lowest + //bit indicates data transmitted + /** @hide */ + public static final int DATA_ACTIVITY_NONE = 0x00; + /** @hide */ + public static final int DATA_ACTIVITY_IN = 0x01; + /** @hide */ + public static final int DATA_ACTIVITY_OUT = 0x02; + /** @hide */ + public static final int DATA_ACTIVITY_INOUT = 0x03; + IWifiManager mService; Handler mHandler; @@ -469,7 +495,7 @@ public class WifiManager { * <p/> * The new network will be marked DISABLED by default. To enable it, * called {@link #enableNetwork}. - * + * * @param config the set of variables that describe the configuration, * contained in a {@link WifiConfiguration} object. * @return the ID of the newly created network description. This is used in @@ -509,7 +535,7 @@ public class WifiManager { /** * Internal method for doing the RPC that creates a new network description * or updates an existing one. - * + * * @param config The possibly sparse object containing the variables that * are to set or updated in the network description. * @return the ID of the network on success, {@code -1} on failure. @@ -696,7 +722,7 @@ public class WifiManager { * Note: It is possible for this method to change the network IDs of * existing networks. You should assume the network IDs can be different * after calling this method. - * + * * @return {@code true} if the operation succeeded */ public boolean saveConfiguration() { @@ -807,20 +833,20 @@ public class WifiManager { return WIFI_STATE_UNKNOWN; } } - + /** - * Return whether Wi-Fi is enabled or disabled. + * Return whether Wi-Fi is enabled or disabled. * @return {@code true} if Wi-Fi is enabled * @see #getWifiState() */ public boolean isWifiEnabled() { return getWifiState() == WIFI_STATE_ENABLED; } - + /** * Calculates the level of the signal. This should be used any time a signal * is being shown. - * + * * @param rssi The power of the signal measured in RSSI. * @param numLevels The number of levels to consider in the calculated * level. @@ -838,10 +864,10 @@ public class WifiManager { return (int)((float)(rssi - MIN_RSSI) * outputRange / inputRange); } } - + /** * Compares two signal strengths. - * + * * @param rssiA The power of the first signal measured in RSSI. * @param rssiB The power of the second signal measured in RSSI. * @return Returns <0 if the first signal is weaker than the second signal, @@ -1106,9 +1132,24 @@ public class WifiManager { } /** + * Get a reference to WifiService handler. This is used by a client to establish + * an AsyncChannel communication with WifiService + * + * @return Messenger pointing to the WifiService handler + * @hide + */ + public Messenger getMessenger() { + try { + return mService.getMessenger(); + } catch (RemoteException e) { + return null; + } + } + + /** * Allows an application to keep the Wi-Fi radio awake. * Normally the Wi-Fi radio may turn off when the user has not used the device in a while. - * Acquiring a WifiLock will keep the radio on until the lock is released. Multiple + * Acquiring a WifiLock will keep the radio on until the lock is released. Multiple * applications may hold WifiLocks, and the radio will only be allowed to turn off when no * WifiLocks are held in any application. * @@ -1144,7 +1185,7 @@ public class WifiManager { * Locks the Wi-Fi radio on until {@link #release} is called. * * If this WifiLock is reference-counted, each call to {@code acquire} will increment the - * reference count, and the radio will remain locked as long as the reference count is + * reference count, and the radio will remain locked as long as the reference count is * above zero. * * If this WifiLock is not reference-counted, the first call to {@code acquire} will lock @@ -1288,9 +1329,10 @@ public class WifiManager { * Creates a new WifiLock. * * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL}, - * and {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks. - * @param tag a tag for the WifiLock to identify it in debugging messages. This string is - * never shown to the user under normal conditions, but should be descriptive + * {@link #WIFI_MODE_FULL_HIGH_PERF} and {@link #WIFI_MODE_SCAN_ONLY} for + * descriptions of the types of Wi-Fi locks. + * @param tag a tag for the WifiLock to identify it in debugging messages. This string is + * never shown to the user under normal conditions, but should be descriptive * enough to identify your application and the specific WifiLock within it, if it * holds multiple WifiLocks. * @@ -1301,7 +1343,7 @@ public class WifiManager { public WifiLock createWifiLock(int lockType, String tag) { return new WifiLock(lockType, tag); } - + /** * Creates a new WifiLock. * diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index 030048f09b6e..39676b044001 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -16,8 +16,6 @@ package android.net.wifi; -import android.net.DhcpInfo; - /** * Native calls for sending requests to the supplicant daemon, and for * receiving asynchronous events. All methods of the form "xxxxCommand()" @@ -163,10 +161,6 @@ public class WifiNative { public native static String startWpsWithPinFromDeviceCommand(String bssid); - public native static boolean doDhcpRequest(DhcpInfo results); - - public native static String getDhcpError(); - public native static boolean setSuspendOptimizationsCommand(boolean enabled); public native static boolean setCountryCodeCommand(String countryCode); diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 4d0acdd7044c..fc42ab8bdad9 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -39,45 +39,45 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; import android.app.AlarmManager; import android.app.PendingIntent; +import android.app.backup.IBackupManager; +import android.bluetooth.BluetoothAdapter; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.DhcpInfo; +import android.net.DhcpInfoInternal; +import android.net.InterfaceConfiguration; import android.net.LinkAddress; +import android.net.LinkProperties; import android.net.NetworkInfo; -import android.net.DhcpInfo; -import android.net.NetworkUtils; -import android.net.ConnectivityManager; import android.net.NetworkInfo.DetailedState; -import android.net.LinkProperties; -import android.net.wifi.NetworkUpdateResult; +import android.net.NetworkUtils; import android.net.wifi.WpsResult.Status; import android.os.Binder; -import android.os.Message; import android.os.IBinder; import android.os.INetworkManagementService; +import android.os.Message; import android.os.PowerManager; -import android.os.SystemProperties; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.Process; +import android.os.SystemProperties; import android.os.WorkSource; import android.provider.Settings; import android.util.EventLog; import android.util.Log; -import android.app.backup.IBackupManager; -import android.bluetooth.BluetoothAdapter; -import android.content.BroadcastReceiver; -import android.content.Intent; -import android.content.Context; -import android.content.IntentFilter; +import android.util.LruCache; import com.android.internal.app.IBatteryStats; import com.android.internal.util.AsyncChannel; import com.android.internal.util.HierarchicalState; import com.android.internal.util.HierarchicalStateMachine; - +import java.net.InetAddress; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; @@ -104,7 +104,7 @@ public class WifiStateMachine extends HierarchicalStateMachine { private List<ScanResult> mScanResults; private static final Pattern scanResultPattern = Pattern.compile("\t+"); private static final int SCAN_RESULT_CACHE_SIZE = 80; - private final LinkedHashMap<String, ScanResult> mScanResultCache; + private final LruCache<String, ScanResult> mScanResultCache; private String mInterfaceName; @@ -143,7 +143,7 @@ public class WifiStateMachine extends HierarchicalStateMachine { private Context mContext; - private DhcpInfo mDhcpInfo; + private DhcpInfoInternal mDhcpInfoInternal; private WifiInfo mWifiInfo; private NetworkInfo mNetworkInfo; private SupplicantStateTracker mSupplicantStateTracker; @@ -435,10 +435,11 @@ public class WifiStateMachine extends HierarchicalStateMachine { private final IBatteryStats mBatteryStats; - public WifiStateMachine(Context context) { + public WifiStateMachine(Context context, String wlanInterface) { super(TAG); mContext = context; + mInterfaceName = wlanInterface; mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, ""); mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo")); @@ -447,9 +448,8 @@ public class WifiStateMachine extends HierarchicalStateMachine { nwService = INetworkManagementService.Stub.asInterface(b); mWifiMonitor = new WifiMonitor(this); - mDhcpInfo = new DhcpInfo(); + mDhcpInfoInternal = new DhcpInfoInternal(); mWifiInfo = new WifiInfo(); - mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0"); mSupplicantStateTracker = new SupplicantStateTracker(context, this, getHandler()); mWpsStateMachine = new WpsStateMachine(context, this, getHandler()); mLinkProperties = new LinkProperties(); @@ -465,6 +465,20 @@ public class WifiStateMachine extends HierarchicalStateMachine { mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0); mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + + ArrayList<String> available = intent.getStringArrayListExtra( + ConnectivityManager.EXTRA_AVAILABLE_TETHER); + ArrayList<String> active = intent.getStringArrayListExtra( + ConnectivityManager.EXTRA_ACTIVE_TETHER); + updateTetherState(available, active); + + } + },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); + + mContext.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -473,17 +487,7 @@ public class WifiStateMachine extends HierarchicalStateMachine { }, new IntentFilter(ACTION_START_SCAN)); - mScanResultCache = new LinkedHashMap<String, ScanResult>( - SCAN_RESULT_CACHE_SIZE, 0.75f, true) { - /* - * Limit the cache size by SCAN_RESULT_CACHE_SIZE - * elements - */ - @Override - public boolean removeEldestEntry(Map.Entry eldest) { - return SCAN_RESULT_CACHE_SIZE < this.size(); - } - }; + mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE); PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); @@ -637,8 +641,8 @@ public class WifiStateMachine extends HierarchicalStateMachine { } public DhcpInfo syncGetDhcpInfo() { - synchronized (mDhcpInfo) { - return new DhcpInfo(mDhcpInfo); + synchronized (mDhcpInfoInternal) { + return mDhcpInfoInternal.makeDhcpInfo(); } } @@ -954,7 +958,7 @@ public class WifiStateMachine extends HierarchicalStateMachine { sb.append("current HSM state: ").append(getCurrentState().getName()).append(LS); sb.append("mLinkProperties ").append(mLinkProperties).append(LS); sb.append("mWifiInfo ").append(mWifiInfo).append(LS); - sb.append("mDhcpInfo ").append(mDhcpInfo).append(LS); + sb.append("mDhcpInfoInternal ").append(mDhcpInfoInternal).append(LS); sb.append("mNetworkInfo ").append(mNetworkInfo).append(LS); sb.append("mLastSignalLevel ").append(mLastSignalLevel).append(LS); sb.append("mLastBssid ").append(mLastBssid).append(LS); @@ -972,6 +976,51 @@ public class WifiStateMachine extends HierarchicalStateMachine { * Internal private functions ********************************************************/ + private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) { + + boolean wifiTethered = false; + boolean wifiAvailable = false; + + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + + if (mCm == null) { + mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + } + + String[] wifiRegexs = mCm.getTetherableWifiRegexs(); + + for (String intf : available) { + for (String regex : wifiRegexs) { + if (intf.matches(regex)) { + + InterfaceConfiguration ifcg = null; + try { + ifcg = service.getInterfaceConfig(intf); + if (ifcg != null) { + /* IP/netmask: 192.168.43.1/255.255.255.0 */ + ifcg.addr = new LinkAddress(InetAddress.getByName("192.168.43.1"), 24); + ifcg.interfaceFlags = "[up]"; + + service.setInterfaceConfig(intf, ifcg); + } + } catch (Exception e) { + Log.e(TAG, "Error configuring interface " + intf + ", :" + e); + setWifiApEnabled(null, false); + return; + } + + if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { + Log.e(TAG, "Error tethering on " + intf); + setWifiApEnabled(null, false); + return; + } + break; + } + } + } + } + /** * Set the country code from the system setting value, if any. */ @@ -1244,14 +1293,8 @@ public class WifiStateMachine extends HierarchicalStateMachine { if (WifiConfigStore.isUsingStaticIp(mLastNetworkId)) { mLinkProperties = WifiConfigStore.getLinkProperties(mLastNetworkId); } else { - // TODO - fix this for v6 - synchronized (mDhcpInfo) { - mLinkProperties.addLinkAddress(new LinkAddress( - NetworkUtils.intToInetAddress(mDhcpInfo.ipAddress), - NetworkUtils.intToInetAddress(mDhcpInfo.netmask))); - mLinkProperties.setGateway(NetworkUtils.intToInetAddress(mDhcpInfo.gateway)); - mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns1)); - mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns2)); + synchronized (mDhcpInfoInternal) { + mLinkProperties = mDhcpInfoInternal.makeLinkProperties(); } mLinkProperties.setHttpProxy(WifiConfigStore.getProxyProperties(mLastNetworkId)); } @@ -1350,7 +1393,7 @@ public class WifiStateMachine extends HierarchicalStateMachine { sendNetworkStateChangeBroadcast(mLastBssid); /* Reset data structures */ - mWifiInfo.setIpAddress(0); + mWifiInfo.setInetAddress(null); mWifiInfo.setBSSID(null); mWifiInfo.setSSID(null); mWifiInfo.setNetworkId(-1); @@ -2450,11 +2493,11 @@ public class WifiStateMachine extends HierarchicalStateMachine { Log.d(TAG, "DHCP request started"); mDhcpThread = new Thread(new Runnable() { public void run() { - DhcpInfo dhcpInfo = new DhcpInfo(); - if (NetworkUtils.runDhcp(mInterfaceName, dhcpInfo)) { + DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal(); + if (NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal)) { Log.d(TAG, "DHCP request succeeded"); - synchronized (mDhcpInfo) { - mDhcpInfo = dhcpInfo; + synchronized (mDhcpInfoInternal) { + mDhcpInfoInternal = dhcpInfoInternal; } sendMessage(CMD_IP_CONFIG_SUCCESS); } else { @@ -2466,15 +2509,25 @@ public class WifiStateMachine extends HierarchicalStateMachine { }); mDhcpThread.start(); } else { - DhcpInfo dhcpInfo = WifiConfigStore.getIpConfiguration(mLastNetworkId); - if (NetworkUtils.configureInterface(mInterfaceName, dhcpInfo)) { + DhcpInfoInternal dhcpInfoInternal = WifiConfigStore.getIpConfiguration( + mLastNetworkId); + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService netd = INetworkManagementService.Stub.asInterface(b); + InterfaceConfiguration ifcg = new InterfaceConfiguration(); + ifcg.addr = dhcpInfoInternal.makeLinkAddress(); + ifcg.interfaceFlags = "[up]"; + try { + netd.setInterfaceConfig(mInterfaceName, ifcg); Log.v(TAG, "Static IP configuration succeeded"); - synchronized (mDhcpInfo) { - mDhcpInfo = dhcpInfo; + synchronized (mDhcpInfoInternal) { + mDhcpInfoInternal = dhcpInfoInternal; } sendMessage(CMD_IP_CONFIG_SUCCESS); - } else { - Log.v(TAG, "Static IP configuration failed"); + } catch (RemoteException re) { + Log.v(TAG, "Static IP configuration failed: " + re); + sendMessage(CMD_IP_CONFIG_FAILURE); + } catch (IllegalStateException e) { + Log.v(TAG, "Static IP configuration failed: " + e); sendMessage(CMD_IP_CONFIG_FAILURE); } } @@ -2486,9 +2539,11 @@ public class WifiStateMachine extends HierarchicalStateMachine { switch(message.what) { case CMD_IP_CONFIG_SUCCESS: mLastSignalLevel = -1; // force update of signal strength - synchronized (mDhcpInfo) { - mWifiInfo.setIpAddress(mDhcpInfo.ipAddress); + InetAddress addr; + synchronized (mDhcpInfoInternal) { + addr = NetworkUtils.numericToInetAddress(mDhcpInfoInternal.ipAddress); } + mWifiInfo.setInetAddress(addr); configureLinkProperties(); if (getNetworkDetailedState() == DetailedState.CONNECTED) { sendLinkConfigurationChangedBroadcast(); @@ -2501,7 +2556,7 @@ public class WifiStateMachine extends HierarchicalStateMachine { transitionTo(mConnectedState); break; case CMD_IP_CONFIG_FAILURE: - mWifiInfo.setIpAddress(0); + mWifiInfo.setInetAddress(null); Log.e(TAG, "IP configuration failed"); /** @@ -2818,6 +2873,14 @@ public class WifiStateMachine extends HierarchicalStateMachine { case CMD_STOP_AP: Log.d(TAG,"Stopping Soft AP"); setWifiApState(WIFI_AP_STATE_DISABLING); + + if (mCm == null) { + mCm = (ConnectivityManager) mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); + } + if (mCm.untether(SOFTAP_IFACE) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { + Log.e(TAG, "Untether initiate failed!"); + } try { nwService.stopAccessPoint(); } catch(Exception e) { |