summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CleanSpec.mk14
-rw-r--r--api/current.xml707
-rw-r--r--core/java/android/app/ApplicationErrorReport.java68
-rw-r--r--core/java/android/app/Service.java10
-rw-r--r--core/java/android/content/ContentProvider.java179
-rw-r--r--core/java/android/content/Context.java1
-rw-r--r--core/java/android/content/pm/PackageParser.java10
-rw-r--r--core/java/android/database/sqlite/SQLiteOpenHelper.java39
-rw-r--r--core/java/android/hardware/Sensor.java10
-rw-r--r--core/java/android/hardware/SensorManager.java50
-rw-r--r--core/java/android/net/DownloadManager.java2
-rw-r--r--core/java/android/os/BatteryStats.java22
-rw-r--r--core/java/android/os/Binder.java12
-rw-r--r--core/java/android/os/IPowerManager.aidl1
-rw-r--r--core/java/android/os/StrictMode.java2
-rwxr-xr-xcore/java/android/speech/tts/TextToSpeech.java3
-rw-r--r--core/java/android/view/IWindowManager.aidl6
-rwxr-xr-xcore/java/android/view/InputDevice.java32
-rw-r--r--core/java/android/view/InputEvent.aidl20
-rwxr-xr-xcore/java/android/view/InputEvent.java40
-rwxr-xr-xcore/java/android/view/KeyEvent.java42
-rw-r--r--core/java/android/view/MotionEvent.java122
-rw-r--r--core/java/android/widget/Scroller.java6
-rw-r--r--core/java/com/android/internal/app/ShutdownThread.java6
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java214
-rw-r--r--core/jni/android_hardware_SensorManager.cpp3
-rw-r--r--core/jni/android_util_Binder.cpp14
-rw-r--r--docs/html/guide/guide_toc.cs3
-rw-r--r--docs/html/guide/practices/ui_guidelines/activity_task_design.jd4
-rw-r--r--docs/html/guide/publishing/licensing.jd2373
-rw-r--r--docs/html/guide/publishing/preparing.jd31
-rw-r--r--docs/html/guide/publishing/publishing.jd25
-rw-r--r--docs/html/images/licensing_add_library.pngbin0 -> 70285 bytes
-rw-r--r--docs/html/images/licensing_arch.pngbin0 -> 41227 bytes
-rw-r--r--docs/html/images/licensing_device_signin.pngbin0 -> 75152 bytes
-rw-r--r--docs/html/images/licensing_flow.pngbin0 -> 47151 bytes
-rw-r--r--docs/html/images/licensing_gapis_8.pngbin0 -> 84597 bytes
-rw-r--r--docs/html/images/licensing_package.pngbin0 -> 78821 bytes
-rw-r--r--docs/html/images/licensing_public_key.pngbin0 -> 95876 bytes
-rw-r--r--docs/html/images/licensing_test_response.pngbin0 -> 120461 bytes
-rw-r--r--docs/html/resources/faq/commontasks.jd2
-rw-r--r--include/binder/Parcel.h12
-rw-r--r--include/gui/Sensor.h2
-rw-r--r--include/gui/SensorEventQueue.h2
-rw-r--r--include/media/AudioEffect.h18
-rw-r--r--include/media/EffectApi.h6
-rw-r--r--include/media/EffectBassBoostApi.h3
-rw-r--r--include/media/EffectEnvironmentalReverbApi.h12
-rw-r--r--include/media/EffectEqualizerApi.h15
-rw-r--r--include/media/EffectPresetReverbApi.h4
-rw-r--r--include/media/EffectVirtualizerApi.h3
-rw-r--r--include/media/EffectVisualizerApi.h3
-rw-r--r--include/media/IEffect.h6
-rw-r--r--include/media/IEffectClient.h6
-rw-r--r--include/private/surfaceflinger/SharedBufferStack.h8
-rw-r--r--include/ui/EventHub.h76
-rw-r--r--include/ui/Input.h83
-rw-r--r--include/ui/InputDevice.h353
-rw-r--r--include/ui/InputDispatcher.h45
-rw-r--r--include/ui/InputManager.h58
-rw-r--r--include/ui/InputReader.h753
-rw-r--r--include/utils/Asset.h11
-rw-r--r--include/utils/StreamingZipInflater.h82
-rw-r--r--libs/binder/Parcel.cpp10
-rw-r--r--libs/gui/Sensor.cpp12
-rw-r--r--libs/gui/SensorEventQueue.cpp4
-rw-r--r--libs/surfaceflinger_client/SharedBufferStack.cpp30
-rw-r--r--libs/ui/Android.mk1
-rw-r--r--libs/ui/EventHub.cpp233
-rw-r--r--libs/ui/Input.cpp59
-rw-r--r--libs/ui/InputDevice.cpp729
-rw-r--r--libs/ui/InputDispatcher.cpp113
-rw-r--r--libs/ui/InputManager.cpp37
-rw-r--r--libs/ui/InputReader.cpp2942
-rw-r--r--libs/utils/Android.mk3
-rw-r--r--libs/utils/Asset.cpp74
-rw-r--r--libs/utils/StreamingZipInflater.cpp225
-rw-r--r--media/java/android/media/BassBoost.java84
-rw-r--r--media/java/android/media/EnvironmentalReverb.java174
-rw-r--r--media/java/android/media/Equalizer.java136
-rw-r--r--media/java/android/media/PresetReverb.java87
-rw-r--r--media/java/android/media/Virtualizer.java82
-rw-r--r--media/jni/audioeffect/android_media_AudioEffect.cpp9
-rw-r--r--media/libeffects/factory/EffectsFactory.c7
-rwxr-xr-xmedia/libeffects/lvm/lib/Bass/src/LVDBE_Control.c22
-rwxr-xr-xmedia/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c4
-rwxr-xr-xmedia/libeffects/lvm/lib/Bundle/src/LVM_Control.c9
-rwxr-xr-xmedia/libeffects/lvm/lib/Bundle/src/LVM_Init.c4
-rwxr-xr-xmedia/libeffects/lvm/lib/Common/src/MixSoft_2St_D32C31_SAT.c12
-rwxr-xr-xmedia/libeffects/lvm/lib/Eq/src/LVEQNB_Control.c4
-rwxr-xr-xmedia/libeffects/lvm/lib/StereoWidening/src/LVCS_BypassMix.c16
-rwxr-xr-xmedia/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c69
-rwxr-xr-xmedia/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.c11
-rwxr-xr-xmedia/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.c29
-rwxr-xr-xmedia/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h9
-rwxr-xr-xmedia/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.c8
-rwxr-xr-xmedia/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.c12
-rw-r--r--media/libeffects/lvm/wrapper/Android.mk66
-rw-r--r--media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp76
-rw-r--r--media/libeffects/lvm/wrapper/Bundle/EffectBundle.h47
-rw-r--r--media/libeffects/testlibs/EffectEqualizer.cpp4
-rw-r--r--media/libeffects/testlibs/EffectReverb.c16
-rw-r--r--media/libeffects/testlibs/EffectReverb.h19
-rw-r--r--media/libeffects/visualizer/Android.mk2
-rw-r--r--media/libeffects/visualizer/EffectVisualizer.cpp4
-rw-r--r--media/libmedia/AudioEffect.cpp20
-rw-r--r--media/libmedia/IEffect.cpp14
-rw-r--r--media/libmedia/IEffectClient.cpp12
-rw-r--r--media/libmedia/Visualizer.cpp2
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.cpp6
-rw-r--r--media/libstagefright/AudioPlayer.cpp1
-rw-r--r--media/libstagefright/MPEG4Writer.cpp96
-rw-r--r--native/android/input.cpp78
-rw-r--r--native/android/sensor.cpp4
-rw-r--r--native/include/android/input.h95
-rw-r--r--native/include/android/sensor.h11
-rw-r--r--packages/DefaultContainerService/res/values/strings.xml2
-rw-r--r--services/audioflinger/AudioFlinger.cpp184
-rw-r--r--services/audioflinger/AudioFlinger.h31
-rw-r--r--services/java/com/android/server/DropBoxManagerService.java2
-rw-r--r--services/java/com/android/server/InputManager.java191
-rw-r--r--services/java/com/android/server/PackageManagerService.java106
-rw-r--r--services/java/com/android/server/PowerManagerService.java110
-rw-r--r--services/java/com/android/server/WindowManagerService.java68
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java68
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java11
-rwxr-xr-xservices/java/com/android/server/location/GpsLocationProvider.java2
-rw-r--r--services/jni/com_android_server_InputManager.cpp199
-rw-r--r--services/surfaceflinger/Layer.cpp28
-rw-r--r--services/surfaceflinger/tests/surface/Android.mk18
-rw-r--r--services/surfaceflinger/tests/surface/surface.cpp54
-rw-r--r--tests/LargeAssetTest/Android.mk11
-rw-r--r--tests/LargeAssetTest/AndroidManifest.xml28
-rw-r--r--tests/LargeAssetTest/assets/million-intsbin0 -> 4000000 bytes
-rw-r--r--tests/LargeAssetTest/res/layout/lat.xml52
-rw-r--r--tests/LargeAssetTest/res/values/strings.xml23
-rw-r--r--tests/LargeAssetTest/src/com/android/largeassettest/LargeAssetTest.java104
137 files changed, 9294 insertions, 3465 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 26d8a1b11d02..1efe77c2484f 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -54,7 +54,19 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/FrameworkTest_in
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.policy*)
$(call add-clean-step, rm -rf $(TARGET_OUT_JAVA_LIBRARIES)/android.policy.jar)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
-
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/obj/lib/libequalizer.so)
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/obj/lib/libequalizertest.so)
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/obj/lib/libreverb.so)
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/obj/lib/libreverbtest.so)
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/symbols/system/lib/libequalizer.so)
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/symbols/system/lib/libequalizertest.so)
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/symbols/system/lib/libreverb.so)
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/symbols/system/lib/libreverbtest.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libequalizer_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libequalizertest_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libreverb_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libreverbtest_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/current.xml b/api/current.xml
index 5cbdc0e3bec4..4dd0909218e4 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -38159,6 +38159,17 @@
visibility="public"
>
</field>
+<field name="DOWNLOAD_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;download&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="DROPBOX_SERVICE"
type="java.lang.String"
transient="false"
@@ -77538,6 +77549,17 @@
visibility="public"
>
</method>
+<method name="getMinDelay"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getName"
return="java.lang.String"
abstract="false"
@@ -77836,6 +77858,21 @@
deprecated="not deprecated"
visibility="public"
>
+<method name="getAltitude"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="p0" type="float">
+</parameter>
+<parameter name="p" type="float">
+</parameter>
+</method>
<method name="getDefaultSensor"
return="android.hardware.Sensor"
abstract="false"
@@ -78415,6 +78452,17 @@
visibility="public"
>
</field>
+<field name="PRESSURE_STANDARD_ATMOSPHERE"
+ type="float"
+ transient="false"
+ volatile="false"
+ value="1013.25f"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="RAW_DATA_INDEX"
type="int"
transient="false"
@@ -92749,6 +92797,567 @@
>
</field>
</class>
+<class name="DownloadManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="enqueue"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="request" type="android.net.DownloadManager.Request">
+</parameter>
+</method>
+<method name="openDownloadedFile"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="long">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+</method>
+<method name="query"
+ return="android.database.Cursor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="query" type="android.net.DownloadManager.Query">
+</parameter>
+</method>
+<method name="remove"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="long">
+</parameter>
+</method>
+<field name="ACTION_DOWNLOAD_COMPLETE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.DOWNLOAD_COMPLETE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_NOTIFICATION_CLICKED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_BYTES_DOWNLOADED_SO_FAR"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;bytes_so_far&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_DESCRIPTION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;description&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_ERROR_CODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;error_code&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_LAST_MODIFIED_TIMESTAMP"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;last_modified_timestamp&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_LOCAL_URI"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;local_uri&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_MEDIA_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;media_type&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_STATUS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;status&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_TITLE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;title&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_TOTAL_SIZE_BYTES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;total_size&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COLUMN_URI"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;uri&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_DEVICE_NOT_FOUND"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1007"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_FILE_ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1001"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_HTTP_DATA_ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1004"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_INSUFFICIENT_SPACE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1006"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_TOO_MANY_REDIRECTS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1005"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_UNHANDLED_HTTP_CODE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1002"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_UNKNOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1000"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_DOWNLOAD_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;extra_download_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_FAILED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_PAUSED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_PENDING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_RUNNING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATUS_SUCCESSFUL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DownloadManager.Query"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DownloadManager.Query"
+ type="android.net.DownloadManager.Query"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="setFilterById"
+ return="android.net.DownloadManager.Query"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="long">
+</parameter>
+</method>
+<method name="setFilterByStatus"
+ return="android.net.DownloadManager.Query"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+</class>
+<class name="DownloadManager.Request"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DownloadManager.Request"
+ type="android.net.DownloadManager.Request"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</constructor>
+<method name="setAllowedNetworkTypes"
+ return="android.net.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="setAllowedOverRoaming"
+ return="android.net.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="allowed" type="boolean">
+</parameter>
+</method>
+<method name="setDescription"
+ return="android.net.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="description" type="java.lang.String">
+</parameter>
+</method>
+<method name="setDestinationUri"
+ return="android.net.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="setMediaType"
+ return="android.net.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mediaType" type="java.lang.String">
+</parameter>
+</method>
+<method name="setRequestHeader"
+ return="android.net.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="header" type="java.lang.String">
+</parameter>
+<parameter name="value" type="java.lang.String">
+</parameter>
+</method>
+<method name="setShowNotification"
+ return="android.net.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="setTitle"
+ return="android.net.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="title" type="java.lang.String">
+</parameter>
+</method>
+<field name="NETWORK_MOBILE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NETWORK_WIFI"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NETWORK_WIMAX"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NOTIFICATION_WHEN_RUNNING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<class name="LocalServerSocket"
extends="java.lang.Object"
abstract="false"
@@ -173184,6 +173793,17 @@
visibility="public"
>
</method>
+<method name="getKeyboardType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getMotionRange"
return="android.view.InputDevice.MotionRange"
abstract="false"
@@ -173232,6 +173852,39 @@
<parameter name="keyCode" type="int">
</parameter>
</method>
+<field name="KEYBOARD_TYPE_ALPHABETIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYBOARD_TYPE_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYBOARD_TYPE_NON_ALPHABETIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="MOTION_RANGE_ORIENTATION"
type="int"
transient="false"
@@ -173331,6 +173984,17 @@
visibility="public"
>
</field>
+<field name="SOURCE_ANY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-256"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SOURCE_CLASS_BUTTON"
type="int"
transient="false"
@@ -173590,6 +174254,17 @@
>
<implements name="android.os.Parcelable">
</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getDevice"
return="android.view.InputDevice"
abstract="false"
@@ -173623,6 +174298,16 @@
visibility="public"
>
</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="mDeviceId"
type="int"
transient="false"
@@ -174266,17 +174951,6 @@
<parameter name="newFlags" 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="dispatch"
return="boolean"
abstract="false"
@@ -177476,17 +178150,6 @@
<parameter name="metaState" 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="findPointerIndex"
return="int"
abstract="false"
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 6981cd6872ba..30815c353a78 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -79,6 +79,11 @@ public class ApplicationErrorReport implements Parcelable {
public static final int TYPE_STRICT_MODE_VIOLATION = 4;
/**
+ * An error report about a StrictMode violation.
+ */
+ public static final int TYPE_RUNNING_SERVICE = 5;
+
+ /**
* Type of this report. Can be one of {@link #TYPE_NONE},
* {@link #TYPE_CRASH}, {@link #TYPE_ANR}, or {@link #TYPE_BATTERY}.
*/
@@ -130,6 +135,12 @@ public class ApplicationErrorReport implements Parcelable {
public BatteryInfo batteryInfo;
/**
+ * If this report is of type {@link #TYPE_RUNNING_SERVICE}, contains an instance
+ * of RunningServiceInfo; otherwise null.
+ */
+ public RunningServiceInfo runningServiceInfo;
+
+ /**
* Create an uninitialized instance of {@link ApplicationErrorReport}.
*/
public ApplicationErrorReport() {
@@ -223,6 +234,9 @@ public class ApplicationErrorReport implements Parcelable {
case TYPE_BATTERY:
batteryInfo.writeToParcel(dest, flags);
break;
+ case TYPE_RUNNING_SERVICE:
+ runningServiceInfo.writeToParcel(dest, flags);
+ break;
}
}
@@ -239,16 +253,25 @@ public class ApplicationErrorReport implements Parcelable {
crashInfo = new CrashInfo(in);
anrInfo = null;
batteryInfo = null;
+ runningServiceInfo = null;
break;
case TYPE_ANR:
anrInfo = new AnrInfo(in);
crashInfo = null;
batteryInfo = null;
+ runningServiceInfo = null;
break;
case TYPE_BATTERY:
batteryInfo = new BatteryInfo(in);
anrInfo = null;
crashInfo = null;
+ runningServiceInfo = null;
+ break;
+ case TYPE_RUNNING_SERVICE:
+ batteryInfo = null;
+ anrInfo = null;
+ crashInfo = null;
+ runningServiceInfo = new RunningServiceInfo(in);
break;
}
}
@@ -494,6 +517,51 @@ public class ApplicationErrorReport implements Parcelable {
}
}
+ /**
+ * Describes a running service report.
+ */
+ public static class RunningServiceInfo {
+ /**
+ * Duration in milliseconds that the service has been running.
+ */
+ public long durationMillis;
+
+ /**
+ * Dump of debug information about the service.
+ */
+ public String serviceDetails;
+
+ /**
+ * Create an uninitialized instance of RunningServiceInfo.
+ */
+ public RunningServiceInfo() {
+ }
+
+ /**
+ * Create an instance of RunningServiceInfo initialized from a Parcel.
+ */
+ public RunningServiceInfo(Parcel in) {
+ durationMillis = in.readLong();
+ serviceDetails = in.readString();
+ }
+
+ /**
+ * Save a RunningServiceInfo instance to a parcel.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(durationMillis);
+ dest.writeString(serviceDetails);
+ }
+
+ /**
+ * Dump a BatteryInfo instance to a Printer.
+ */
+ public void dump(Printer pw, String prefix) {
+ pw.println(prefix + "durationMillis: " + durationMillis);
+ pw.println(prefix + "serviceDetails: " + serviceDetails);
+ }
+ }
+
public static final Parcelable.Creator<ApplicationErrorReport> CREATOR
= new Parcelable.Creator<ApplicationErrorReport>() {
public ApplicationErrorReport createFromParcel(Parcel source) {
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 697a987d5f20..b7a750b6573e 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -400,7 +400,15 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
*
* {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
* start_compatibility}
- *
+ *
+ * <p class="caution">Note that the system calls this on your
+ * service's main thread. A service's main thread is the same
+ * thread where UI operations place for Activities running in the
+ * same process. You should always avoid stalling the main
+ * thread's event loop. When doing long-running operations,
+ * network calls, or heavy disk I/O, you should kick off a new
+ * thread, or use {@link android.os.AsyncTask}.</p>
+ *
* @param intent The Intent supplied to {@link android.content.Context#startService},
* as given. This may be null if the service is being restarted after
* its process has gone away, and it had previously returned anything
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 5fb2aaec83d7..9b9f796a1607 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -57,6 +57,7 @@ import java.util.ArrayList;
*
* <p>The primary methods that need to be implemented are:
* <ul>
+ * <li>{@link #onCreate} which is called to initialize the provider</li>
* <li>{@link #query} which returns data to the caller</li>
* <li>{@link #insert} which inserts new data into the content provider</li>
* <li>{@link #update} which updates existing data in the content provider</li>
@@ -64,8 +65,15 @@ import java.util.ArrayList;
* <li>{@link #getType} which returns the MIME type of data in the content provider</li>
* </ul></p>
*
- * <p>This class takes care of cross process calls so subclasses don't have to worry about which
- * process a request is coming from.</p>
+ * <p class="caution">Data access methods (such as {@link #insert} and
+ * {@link #update}) may be called from many threads at once, and must be thread-safe.
+ * Other methods (such as {@link #onCreate}) are only called from the application
+ * main thread, and must avoid performing lengthy operations. See the method
+ * descriptions for their expected thread behavior.</p>
+ *
+ * <p>Requests to {@link ContentResolver} are automatically forwarded to the appropriate
+ * ContentProvider instance, so subclasses don't have to worry about the details of
+ * cross-process calls.</p>
*/
public abstract class ContentProvider implements ComponentCallbacks {
/*
@@ -81,6 +89,21 @@ public abstract class ContentProvider implements ComponentCallbacks {
private Transport mTransport = new Transport();
+ /**
+ * Construct a ContentProvider instance. Content providers must be
+ * <a href="{@docRoot}guide/topics/manifest/provider-element.html">declared
+ * in the manifest</a>, accessed with {@link ContentResolver}, and created
+ * automatically by the system, so applications usually do not create
+ * ContentProvider instances directly.
+ *
+ * <p>At construction time, the object is uninitialized, and most fields and
+ * methods are unavailable. Subclasses should initialize themselves in
+ * {@link #onCreate}, not the constructor.
+ *
+ * <p>Content providers are created on the application main thread at
+ * application launch time. The constructor must not perform lengthy
+ * operations, or application startup will be delayed.
+ */
public ContentProvider() {
}
@@ -328,8 +351,8 @@ public abstract class ContentProvider implements ComponentCallbacks {
/**
- * Retrieve the Context this provider is running in. Only available once
- * onCreate(Map icicle) has been called -- this will be null in the
+ * Retrieves the Context this provider is running in. Only available once
+ * {@link #onCreate} has been called -- this will return null in the
* constructor.
*/
public final Context getContext() {
@@ -403,23 +426,59 @@ public abstract class ContentProvider implements ComponentCallbacks {
}
/**
- * Called when the provider is being started.
+ * Implement this to initialize your content provider on startup.
+ * This method is called for all registered content providers on the
+ * application main thread at application launch time. It must not perform
+ * lengthy operations, or application startup will be delayed.
+ *
+ * <p>You should defer nontrivial initialization (such as opening,
+ * upgrading, and scanning databases) until the content provider is used
+ * (via {@link #query}, {@link #insert}, etc). Deferred initialization
+ * keeps application startup fast, avoids unnecessary work if the provider
+ * turns out not to be needed, and stops database errors (such as a full
+ * disk) from halting application launch.
+ *
+ * <p>If you use SQLite, {@link android.database.sqlite.SQLiteOpenHelper}
+ * is a helpful utility class that makes it easy to manage databases,
+ * and will automatically defer opening until first use. If you do use
+ * SQLiteOpenHelper, make sure to avoid calling
+ * {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} or
+ * {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase}
+ * from this method. (Instead, override
+ * {@link android.database.sqlite.SQLiteOpenHelper#onOpen} to initialize the
+ * database when it is first opened.)
*
* @return true if the provider was successfully loaded, false otherwise
*/
public abstract boolean onCreate();
+ /**
+ * {@inheritDoc}
+ * This method is always called on the application main thread, and must
+ * not perform lengthy operations.
+ *
+ * <p>The default content provider implementation does nothing.
+ * Override this method to take appropriate action.
+ * (Content providers do not usually care about things like screen
+ * orientation, but may want to know about locale changes.)
+ */
public void onConfigurationChanged(Configuration newConfig) {
}
-
+
+ /**
+ * {@inheritDoc}
+ * This method is always called on the application main thread, and must
+ * not perform lengthy operations.
+ *
+ * <p>The default content provider implementation does nothing.
+ * Subclasses may override this method to take appropriate action.
+ */
public void onLowMemory() {
}
/**
- * Receives a query request from a client in a local process, and
- * returns a Cursor. This is called internally by the {@link ContentResolver}.
- * This method can be called from multiple
- * threads, as described in
+ * Implement this to handle query requests from clients.
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
* <p>
@@ -476,11 +535,11 @@ public abstract class ContentProvider implements ComponentCallbacks {
String selection, String[] selectionArgs, String sortOrder);
/**
- * Return the MIME type of the data at the given URI. This should start with
+ * Implement this to handle requests for the MIME type of the data at the
+ * given URI. The returned MIME type should start with
* <code>vnd.android.cursor.item</code> for a single record,
* or <code>vnd.android.cursor.dir/</code> for multiple items.
- * This method can be called from multiple
- * threads, as described in
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
*
@@ -490,11 +549,10 @@ public abstract class ContentProvider implements ComponentCallbacks {
public abstract String getType(Uri uri);
/**
- * Implement this to insert a new row.
+ * Implement this to handle requests to insert a new row.
* As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
* after inserting.
- * This method can be called from multiple
- * threads, as described in
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
* @param uri The content:// URI of the insertion request.
@@ -504,12 +562,12 @@ public abstract class ContentProvider implements ComponentCallbacks {
public abstract Uri insert(Uri uri, ContentValues values);
/**
- * Implement this to insert a set of new rows, or the default implementation will
- * iterate over the values and call {@link #insert} on each of them.
+ * Override this to handle requests to insert a set of new rows, or the
+ * default implementation will iterate over the values and call
+ * {@link #insert} on each of them.
* As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
* after inserting.
- * This method can be called from multiple
- * threads, as described in
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
*
@@ -526,13 +584,12 @@ public abstract class ContentProvider implements ComponentCallbacks {
}
/**
- * A request to delete one or more rows. The selection clause is applied when performing
- * the deletion, allowing the operation to affect multiple rows in a
- * directory.
+ * Implement this to handle requests to delete one or more rows.
+ * The implementation should apply the selection clause when performing
+ * deletion, allowing the operation to affect multiple rows in a directory.
* As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyDelete()}
* after deleting.
- * This method can be called from multiple
- * threads, as described in
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
*
@@ -549,13 +606,12 @@ public abstract class ContentProvider implements ComponentCallbacks {
public abstract int delete(Uri uri, String selection, String[] selectionArgs);
/**
- * Update a content URI. All rows matching the optionally provided selection
- * will have their columns listed as the keys in the values map with the
- * values of those keys.
+ * Implement this to handle requests to update one or more rows.
+ * The implementation should update all rows matching the selection
+ * to set the columns according to the provided values map.
* As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
* after updating.
- * This method can be called from multiple
- * threads, as described in
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
*
@@ -570,18 +626,15 @@ public abstract class ContentProvider implements ComponentCallbacks {
String[] selectionArgs);
/**
- * Open a file blob associated with a content URI.
- * This method can be called from multiple
- * threads, as described in
+ * Override this to handle requests to open a file blob.
+ * The default implementation always throws {@link FileNotFoundException}.
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
- *
- * <p>Returns a
- * ParcelFileDescriptor, from which you can obtain a
- * {@link java.io.FileDescriptor} for use with
- * {@link java.io.FileInputStream}, {@link java.io.FileOutputStream}, etc.
- * This can be used to store large data (such as an image) associated with
- * a particular piece of content.
+ *
+ * <p>This method returns a ParcelFileDescriptor, which is returned directly
+ * to the caller. This way large data (such as images and documents) can be
+ * returned without copying the content.
*
* <p>The returned ParcelFileDescriptor is owned by the caller, so it is
* their responsibility to close it when done. That is, the implementation
@@ -599,31 +652,35 @@ public abstract class ContentProvider implements ComponentCallbacks {
* no file associated with the given URI or the mode is invalid.
* @throws SecurityException Throws SecurityException if the caller does
* not have permission to access the file.
- *
+ *
* @see #openAssetFile(Uri, String)
* @see #openFileHelper(Uri, String)
- */
+ */
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
throw new FileNotFoundException("No files supported by provider at "
+ uri);
}
-
+
/**
* This is like {@link #openFile}, but can be implemented by providers
* that need to be able to return sub-sections of files, often assets
- * inside of their .apk. Note that when implementing this your clients
- * must be able to deal with such files, either directly with
- * {@link ContentResolver#openAssetFileDescriptor
- * ContentResolver.openAssetFileDescriptor}, or by using the higher-level
+ * inside of their .apk.
+ * This method can be called from multiple threads, as described in
+ * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
+ * Processes and Threads</a>.
+ *
+ * <p>If you implement this, your clients must be able to deal with such
+ * file slices, either directly with
+ * {@link ContentResolver#openAssetFileDescriptor}, or by using the higher-level
* {@link ContentResolver#openInputStream ContentResolver.openInputStream}
* or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream}
* methods.
- *
- * <p><em>Note: if you are implementing this to return a full file, you
+ *
+ * <p class="note">If you are implementing this to return a full file, you
* should create the AssetFileDescriptor with
* {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with
- * applications that can not handle sub-sections of files.</em></p>
+ * applications that can not handle sub-sections of files.</p>
*
* @param uri The URI whose file is to be opened.
* @param mode Access mode for the file. May be "r" for read-only access,
@@ -735,17 +792,21 @@ public abstract class ContentProvider implements ComponentCallbacks {
}
/**
- * Applies each of the {@link ContentProviderOperation} objects and returns an array
- * of their results. Passes through OperationApplicationException, which may be thrown
- * by the call to {@link ContentProviderOperation#apply}.
- * If all the applications succeed then a {@link ContentProviderResult} array with the
- * same number of elements as the operations will be returned. It is implementation-specific
- * how many, if any, operations will have been successfully applied if a call to
- * apply results in a {@link OperationApplicationException}.
+ * Override this to handle requests to perform a batch of operations, or the
+ * default implementation will iterate over the operations and call
+ * {@link ContentProviderOperation#apply} on each of them.
+ * If all calls to {@link ContentProviderOperation#apply} succeed
+ * then a {@link ContentProviderResult} array with as many
+ * elements as there were operations will be returned. If any of the calls
+ * fail, it is up to the implementation how many of the others take effect.
+ * This method can be called from multiple threads, as described in
+ * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
+ * Processes and Threads</a>.
+ *
* @param operations the operations to apply
* @return the results of the applications
- * @throws OperationApplicationException thrown if an application fails.
- * See {@link ContentProviderOperation#apply} for more information.
+ * @throws OperationApplicationException thrown if any operation fails.
+ * @see ContentProviderOperation#apply
*/
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index e3b7731e4048..7951a302a437 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1538,7 +1538,6 @@ public abstract class Context {
* {@link android.net.DownloadManager} for requesting HTTP downloads.
*
* @see #getSystemService
- * @hide (TODO) for now
*/
public static final String DOWNLOAD_SERVICE = "download";
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 5dc41d2e3ade..73c401135522 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2786,6 +2786,9 @@ public class PackageParser {
// For use by package manager to keep track of where it has done dexopt.
public boolean mDidDexOpt;
+ // User set enabled state.
+ public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+
// Additional data supplied by callers.
public Object mExtras;
@@ -3012,6 +3015,12 @@ public class PackageParser {
}
private static boolean copyNeeded(int flags, Package p, Bundle metaData) {
+ if (p.mSetEnabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ boolean enabled = p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ if (p.applicationInfo.enabled != enabled) {
+ return true;
+ }
+ }
if ((flags & PackageManager.GET_META_DATA) != 0
&& (metaData != null || p.mAppMetaData != null)) {
return true;
@@ -3045,6 +3054,7 @@ public class PackageParser {
if (!sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
}
+ ai.enabled = p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
return ai;
}
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index aefbabc4da91..b4615eb6b261 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -22,10 +22,16 @@ import android.util.Log;
/**
* A helper class to manage database creation and version management.
- * You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and
+ *
+ * <p>You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and
* optionally {@link #onOpen}, and this class takes care of opening the database
* if it exists, creating it if it does not, and upgrading it as necessary.
* Transactions are used to make sure the database is always in a sensible state.
+ *
+ * <p>This class makes it easy for {@link android.content.ContentProvider}
+ * implementations to defer opening and upgrading the database until first use,
+ * to avoid blocking application startup with long-running database upgrades.
+ *
* <p>For an example, see the NotePadProvider class in the NotePad sample application,
* in the <em>samples/</em> directory of the SDK.</p>
*
@@ -48,8 +54,9 @@ public abstract class SQLiteOpenHelper {
/**
* Create a helper object to create, open, and/or manage a database.
- * The database is not actually created or opened until one of
- * {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
+ * This method always returns very quickly. The database is not actually
+ * created or opened until one of {@link #getWritableDatabase} or
+ * {@link #getReadableDatabase} is called.
*
* @param context to use to open or create the database
* @param name of the database file, or null for an in-memory database
@@ -68,13 +75,20 @@ public abstract class SQLiteOpenHelper {
/**
* Create and/or open a database that will be used for reading and writing.
- * Once opened successfully, the database is cached, so you can call this
- * method every time you need to write to the database. Make sure to call
- * {@link #close} when you no longer need it.
+ * The first time this is called, the database will be opened and
+ * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
+ * called.
*
- * <p>Errors such as bad permissions or a full disk may cause this operation
+ * <p>Once opened successfully, the database is cached, so you can
+ * call this method every time you need to write to the database.
+ * (Make sure to call {@link #close} when you no longer need the database.)
+ * Errors such as bad permissions or a full disk may cause this method
* to fail, but future attempts may succeed if the problem is fixed.</p>
*
+ * <p class="caution">Database upgrade may take a long time, you
+ * should not call this method from the application main thread, including
+ * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
+ *
* @throws SQLiteException if the database cannot be opened for writing
* @return a read/write database object valid until {@link #close} is called
*/
@@ -151,6 +165,11 @@ public abstract class SQLiteOpenHelper {
* database object will be closed and the read/write object will be returned
* in the future.
*
+ * <p class="caution">Like {@link #getWritableDatabase}, this method may
+ * take a long time to return, so you should not call it from the
+ * application main thread, including from
+ * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
+ *
* @throws SQLiteException if the database cannot be opened
* @return a database object valid until {@link #getWritableDatabase}
* or {@link #close} is called.
@@ -229,9 +248,9 @@ public abstract class SQLiteOpenHelper {
public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
/**
- * Called when the database has been opened.
- * Override method should check {@link SQLiteDatabase#isReadOnly} before
- * updating the database.
+ * Called when the database has been opened. The implementation
+ * should check {@link SQLiteDatabase#isReadOnly} before updating the
+ * database.
*
* @param db The database.
*/
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 3490ac0e7d77..b49a409ceef8 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -92,6 +92,7 @@ public class Sensor {
private float mMaxRange;
private float mResolution;
private float mPower;
+ private int mMinDelay;
private int mLegacyType;
@@ -147,6 +148,15 @@ public class Sensor {
return mPower;
}
+ /**
+ * @return the minimum delay allowed between two events in microsecond
+ * or zero if this sensor only returns a value when the data it's measuring
+ * changes.
+ */
+ public int getMinDelay() {
+ return mMinDelay;
+ }
+
int getHandle() {
return mHandle;
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index e6750e677705..f6d237acde4b 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -268,6 +268,10 @@ public class SensorManager
public static final float MAGNETIC_FIELD_EARTH_MIN = 30.0f;
+ /** Standard atmosphere, or average sea-level pressure in hPa (millibar) */
+ public static final float PRESSURE_STANDARD_ATMOSPHERE = 1013.25f;
+
+
/** Maximum luminance of sunlight in lux */
public static final float LIGHT_SUNLIGHT_MAX = 120000.0f;
/** luminance of sunlight in lux */
@@ -573,11 +577,11 @@ public class SensorManager
// which won't get the rotated values
try {
sRotation = sWindowManager.watchRotation(
- new IRotationWatcher.Stub() {
- public void onRotationChanged(int rotation) {
- SensorManager.this.onRotationChanged(rotation);
+ new IRotationWatcher.Stub() {
+ public void onRotationChanged(int rotation) {
+ SensorManager.this.onRotationChanged(rotation);
+ }
}
- }
);
} catch (RemoteException e) {
}
@@ -638,7 +642,7 @@ public class SensorManager
break;
case Sensor.TYPE_ORIENTATION:
result |= SensorManager.SENSOR_ORIENTATION |
- SensorManager.SENSOR_ORIENTATION_RAW;
+ SensorManager.SENSOR_ORIENTATION_RAW;
break;
}
}
@@ -935,7 +939,8 @@ public class SensorManager
* received faster or slower than the specified rate. Usually events
* are received faster. The value must be one of
* {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
- * {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}.
+ * {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}
+ * or, the desired delay between events in microsecond.
*
* @return <code>true</code> if the sensor is supported and successfully
* enabled.
@@ -967,6 +972,7 @@ public class SensorManager
* are received faster. The value must be one of
* {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
* {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}.
+ * or, the desired delay between events in microsecond.
*
* @param handler
* The {@link android.os.Handler Handler} the
@@ -992,16 +998,17 @@ public class SensorManager
delay = 0;
break;
case SENSOR_DELAY_GAME:
- delay = 20;
+ delay = 20000;
break;
case SENSOR_DELAY_UI:
- delay = 60;
+ delay = 60000;
break;
case SENSOR_DELAY_NORMAL:
- delay = 200;
+ delay = 200000;
break;
default:
- return false;
+ delay = rate;
+ break;
}
synchronized (sListeners) {
@@ -1485,7 +1492,7 @@ public class SensorManager
* @see #getRotationMatrix(float[], float[], float[], float[])
* @see GeomagneticField
*/
- public static float[] getOrientation(float[] R, float values[]) {
+ public static float[] getOrientation(float[] R, float values[]) {
/*
* 4x4 (length=16) case:
* / R[ 0] R[ 1] R[ 2] 0 \
@@ -1511,8 +1518,27 @@ public class SensorManager
return values;
}
-
/**
+ * Computes the Altitude in meters from the atmospheric pressure and the
+ * pressure at sea level.
+ * <p>
+ * Typically the atmospheric pressure is read from a
+ * {@link Sensor#TYPE_PRESSURE} sensor. The pressure at sea level must be
+ * known, usually it can be retrieved from airport databases in the
+ * vicinity.
+ * </p>
+ *
+ * @param p0 pressure at sea level
+ * @param p atmospheric pressure
+ * @return Altitude in meters
+ */
+ public static float getAltitude(float p0, float p) {
+ final float coef = 1.0f / 5.255f;
+ return 44330.0f * (1.0f - (float)Math.pow(p/p0, coef));
+ }
+
+
+ /**
* {@hide}
*/
public void onRotationChanged(int rotation) {
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
index 455bd36e5a3e..1d88c18ea511 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/net/DownloadManager.java
@@ -42,8 +42,6 @@ import java.util.Set;
* Instances of this class should be obtained through
* {@link android.content.Context#getSystemService(String)} by passing
* {@link android.content.Context#DOWNLOAD_SERVICE}.
- *
- * @hide
*/
public class DownloadManager {
/**
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 0afd6cd22bea..a69938889f12 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -106,7 +106,7 @@ public abstract class BatteryStats implements Parcelable {
// NOTE: Update this list if you add/change any stats above.
// These characters are supposed to represent "total", "last", "current",
- // and "unplugged". They were shortened for effeciency sake.
+ // and "unplugged". They were shortened for efficiency sake.
private static final String[] STAT_NAMES = { "t", "l", "c", "u" };
/**
@@ -395,19 +395,23 @@ public abstract class BatteryStats implements Parcelable {
public char batteryTemperature;
public char batteryVoltage;
+ // Constants from SCREEN_BRIGHTNESS_*
public static final int STATE_BRIGHTNESS_MASK = 0x000000f;
public static final int STATE_BRIGHTNESS_SHIFT = 0;
+ // Constants from SIGNAL_STRENGTH_*
public static final int STATE_SIGNAL_STRENGTH_MASK = 0x00000f0;
public static final int STATE_SIGNAL_STRENGTH_SHIFT = 4;
+ // Constants from ServiceState.STATE_*
public static final int STATE_PHONE_STATE_MASK = 0x0000f00;
public static final int STATE_PHONE_STATE_SHIFT = 8;
+ // Constants from DATA_CONNECTION_*
public static final int STATE_DATA_CONNECTION_MASK = 0x000f000;
public static final int STATE_DATA_CONNECTION_SHIFT = 12;
public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<30;
public static final int STATE_SCREEN_ON_FLAG = 1<<29;
public static final int STATE_GPS_ON_FLAG = 1<<28;
- public static final int STATE_PHONE_ON_FLAG = 1<<27;
+ public static final int STATE_PHONE_IN_CALL_FLAG = 1<<27;
public static final int STATE_PHONE_SCANNING_FLAG = 1<<26;
public static final int STATE_WIFI_ON_FLAG = 1<<25;
public static final int STATE_WIFI_RUNNING_FLAG = 1<<24;
@@ -619,7 +623,7 @@ public abstract class BatteryStats implements Parcelable {
new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged"),
new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen"),
new BitDescription(HistoryItem.STATE_GPS_ON_FLAG, "gps"),
- new BitDescription(HistoryItem.STATE_PHONE_ON_FLAG, "phone"),
+ new BitDescription(HistoryItem.STATE_PHONE_IN_CALL_FLAG, "phone_in_call"),
new BitDescription(HistoryItem.STATE_PHONE_SCANNING_FLAG, "phone_scanning"),
new BitDescription(HistoryItem.STATE_WIFI_ON_FLAG, "wifi"),
new BitDescription(HistoryItem.STATE_WIFI_RUNNING_FLAG, "wifi_running"),
@@ -717,6 +721,18 @@ public abstract class BatteryStats implements Parcelable {
public abstract int getDischargeCurrentLevel();
/**
+ * Get the amount the battery has discharged since the stats were
+ * last reset after charging, as a lower-end approximation.
+ */
+ public abstract int getLowDischargeAmountSinceCharge();
+
+ /**
+ * Get the amount the battery has discharged since the stats were
+ * last reset after charging, as an upper-end approximation.
+ */
+ public abstract int getHighDischargeAmountSinceCharge();
+
+ /**
* Returns the total, last, or current battery uptime in microseconds.
*
* @param curTime the elapsed realtime in microseconds.
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 59b8274a481b..f8260cababeb 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -226,9 +226,16 @@ public class Binder implements IBinder {
try {
fd.close();
} catch (IOException e) {
+ // swallowed, not propagated back to the caller
}
}
}
+ // Write the StrictMode header.
+ if (reply != null) {
+ reply.writeNoException();
+ } else {
+ StrictMode.clearGatheredViolations();
+ }
return true;
}
return false;
@@ -341,12 +348,15 @@ final class BinderProxy implements IBinder {
public void dump(FileDescriptor fd, String[] args) throws RemoteException {
Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
data.writeFileDescriptor(fd);
data.writeStringArray(args);
try {
- transact(DUMP_TRANSACTION, data, null, 0);
+ transact(DUMP_TRANSACTION, data, reply, 0);
+ reply.readException();
} finally {
data.recycle();
+ reply.recycle();
}
}
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index dedc34773c91..01cc408074d0 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -26,6 +26,7 @@ interface IPowerManager
void releaseWakeLock(IBinder lock, int flags);
void userActivity(long when, boolean noChangeLights);
void userActivityWithForce(long when, boolean noChangeLights, boolean force);
+ void clearUserActivityTimeout(long now, long timeout);
void setPokeLock(int pokey, IBinder lock, String tag);
int getSupportedWakeLockFlags();
void setStayOnSetting(int val);
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index dc9259078152..d4b0500a5ac1 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -97,7 +97,7 @@ public final class StrictMode {
* via Parcel.writeNoException() (amusingly) where the caller can
* choose how to react.
*/
- private static ThreadLocal<ArrayList<ApplicationErrorReport.CrashInfo>> gatheredViolations =
+ private static final ThreadLocal<ArrayList<ApplicationErrorReport.CrashInfo>> gatheredViolations =
new ThreadLocal<ArrayList<ApplicationErrorReport.CrashInfo>>() {
@Override protected ArrayList<ApplicationErrorReport.CrashInfo> initialValue() {
// Starts null to avoid unnecessary allocations when
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 26c167e1f013..841257f36770 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -1065,6 +1065,9 @@ public class TextToSpeech {
if (!mStarted) {
return result;
}
+ if (loc == null) {
+ return result;
+ }
try {
String language = loc.getISO3Language();
String country = loc.getISO3Country();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9b7b2f40fd3c..d6b92125490c 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -26,6 +26,7 @@ import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindowSession;
import android.view.KeyEvent;
+import android.view.InputEvent;
import android.view.MotionEvent;
/**
@@ -50,10 +51,13 @@ interface IWindowManager
boolean inputMethodClientHasFocus(IInputMethodClient client);
// These can only be called when injecting events to your own window,
- // or by holding the INJECT_EVENTS permission.
+ // or by holding the INJECT_EVENTS permission. These methods may block
+ // until pending input events are finished being dispatched even when 'sync' is false.
+ // Avoid calling these methods on your UI thread or use the 'NoWait' version instead.
boolean injectKeyEvent(in KeyEvent ev, boolean sync);
boolean injectPointerEvent(in MotionEvent ev, boolean sync);
boolean injectTrackballEvent(in MotionEvent ev, boolean sync);
+ boolean injectInputEventNoWait(in InputEvent ev);
// These can only be called when holding the MANAGE_APP_TOKENS permission.
void pauseKeyDispatching(IBinder token);
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 21c297675052..d5b2121ea3dd 100755
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -30,11 +30,13 @@ package android.view;
* As a further wrinkle, different kinds of input sources uses different coordinate systems
* to describe motion events. Refer to the comments on the input source constants for
* the appropriate interpretation.
+ * </p>
*/
public final class InputDevice {
private int mId;
private String mName;
private int mSources;
+ private int mKeyboardType;
/**
* A mask for input source classes.
@@ -173,6 +175,12 @@ public final class InputDevice {
* @see #SOURCE_CLASS_JOYSTICK
*/
public static final int SOURCE_JOYSTICK_RIGHT = 0x02000000 | 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.
+ */
+ public static final int SOURCE_ANY = 0xffffff00;
/**
* Constant for retrieving the range of values for {@link MotionEvent.PointerCoords#x}.
@@ -237,6 +245,22 @@ public final class InputDevice {
* @see #getMotionRange
*/
public static final int MOTION_RANGE_ORIENTATION = 8;
+
+ /**
+ * There is no keyboard.
+ */
+ public static final int KEYBOARD_TYPE_NONE = 0;
+
+ /**
+ * The keyboard is not fully alphabetic. It may be a numeric keypad or an assortment
+ * of buttons that are not mapped as alphabetic keys suitable for text input.
+ */
+ public static final int KEYBOARD_TYPE_NON_ALPHABETIC = 1;
+
+ /**
+ * The keyboard supports a complement of alphabetic keys.
+ */
+ public static final int KEYBOARD_TYPE_ALPHABETIC = 2;
/**
* Gets information about the input device with the specified id.
@@ -265,6 +289,14 @@ public final class InputDevice {
}
/**
+ * Gets the keyboard type.
+ * @return The keyboard type.
+ */
+ public int getKeyboardType() {
+ return mKeyboardType;
+ }
+
+ /**
* Gets the key character map associated with this input device.
* @return The key character map.
*/
diff --git a/core/java/android/view/InputEvent.aidl b/core/java/android/view/InputEvent.aidl
new file mode 100644
index 000000000000..61acaaf1fa8d
--- /dev/null
+++ b/core/java/android/view/InputEvent.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android.view.InputEvent.aidl
+**
+** Copyright 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.view;
+
+parcelable InputEvent;
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 78b73fe95805..9afd16e8f06c 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -16,6 +16,7 @@
package android.view;
+import android.os.Parcel;
import android.os.Parcelable;
/**
@@ -25,6 +26,11 @@ public abstract class InputEvent implements Parcelable {
protected int mDeviceId;
protected int mSource;
+ /** @hide */
+ protected static final int PARCEL_TOKEN_MOTION_EVENT = 1;
+ /** @hide */
+ protected static final int PARCEL_TOKEN_KEY_EVENT = 2;
+
/*package*/ InputEvent() {
}
@@ -69,4 +75,38 @@ public abstract class InputEvent implements Parcelable {
public final void setSource(int source) {
mSource = source;
}
+
+ public final 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) {
+ int token = in.readInt();
+ if (token == PARCEL_TOKEN_KEY_EVENT) {
+ return KeyEvent.createFromParcelBody(in);
+ } else if (token == PARCEL_TOKEN_MOTION_EVENT) {
+ return MotionEvent.createFromParcelBody(in);
+ } else {
+ throw new IllegalStateException("Unexpected input event type token in parcel.");
+ }
+ }
+
+ public InputEvent[] newArray(int size) {
+ return new InputEvent[size];
+ }
+ };
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index dd0d21a307fa..9223e17fb5a5 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1211,44 +1211,48 @@ public class KeyEvent extends InputEvent implements Parcelable {
public static final Parcelable.Creator<KeyEvent> CREATOR
= new Parcelable.Creator<KeyEvent>() {
public KeyEvent createFromParcel(Parcel in) {
- return new KeyEvent(in);
+ in.readInt(); // skip token, we already know this is a KeyEvent
+ return KeyEvent.createFromParcelBody(in);
}
public KeyEvent[] newArray(int size) {
return new KeyEvent[size];
}
};
-
- public int describeContents() {
- return 0;
+
+ /** @hide */
+ public static KeyEvent createFromParcelBody(Parcel in) {
+ return new KeyEvent(in);
+ }
+
+ private KeyEvent(Parcel in) {
+ readBaseFromParcel(in);
+
+ mAction = in.readInt();
+ mKeyCode = in.readInt();
+ mRepeatCount = in.readInt();
+ mMetaState = in.readInt();
+ mScanCode = in.readInt();
+ mFlags = in.readInt();
+ mDownTime = in.readLong();
+ mEventTime = in.readLong();
}
public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(PARCEL_TOKEN_KEY_EVENT);
+
+ writeBaseToParcel(out);
+
out.writeInt(mAction);
out.writeInt(mKeyCode);
out.writeInt(mRepeatCount);
out.writeInt(mMetaState);
- out.writeInt(mDeviceId);
- out.writeInt(mSource);
out.writeInt(mScanCode);
out.writeInt(mFlags);
out.writeLong(mDownTime);
out.writeLong(mEventTime);
}
- private KeyEvent(Parcel in) {
- mAction = in.readInt();
- mKeyCode = in.readInt();
- mRepeatCount = in.readInt();
- mMetaState = in.readInt();
- mDeviceId = in.readInt();
- mSource = in.readInt();
- mScanCode = in.readInt();
- mFlags = in.readInt();
- mDownTime = in.readLong();
- mEventTime = in.readLong();
- }
-
private native boolean native_isSystemKey(int keyCode);
private native boolean native_hasDefaultAction(int keyCode);
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 13360d9eb97a..98fe73f891d4 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -181,61 +181,61 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* Offset for the sample's X coordinate.
* @hide
*/
- static public final int SAMPLE_X = 0;
+ static private final int SAMPLE_X = 0;
/**
* Offset for the sample's Y coordinate.
* @hide
*/
- static public final int SAMPLE_Y = 1;
+ static private final int SAMPLE_Y = 1;
/**
* Offset for the sample's pressure.
* @hide
*/
- static public final int SAMPLE_PRESSURE = 2;
+ static private final int SAMPLE_PRESSURE = 2;
/**
* Offset for the sample's size
* @hide
*/
- static public final int SAMPLE_SIZE = 3;
+ static private final int SAMPLE_SIZE = 3;
/**
* Offset for the sample's touch major axis length.
* @hide
*/
- static public final int SAMPLE_TOUCH_MAJOR = 4;
+ static private final int SAMPLE_TOUCH_MAJOR = 4;
/**
* Offset for the sample's touch minor axis length.
* @hide
*/
- static public final int SAMPLE_TOUCH_MINOR = 5;
+ static private final int SAMPLE_TOUCH_MINOR = 5;
/**
* Offset for the sample's tool major axis length.
* @hide
*/
- static public final int SAMPLE_TOOL_MAJOR = 6;
+ static private final int SAMPLE_TOOL_MAJOR = 6;
/**
* Offset for the sample's tool minor axis length.
* @hide
*/
- static public final int SAMPLE_TOOL_MINOR = 7;
+ static private final int SAMPLE_TOOL_MINOR = 7;
/**
* Offset for the sample's orientation.
* @hide
*/
- static public final int SAMPLE_ORIENTATION = 8;
+ static private final int SAMPLE_ORIENTATION = 8;
/**
* Number of data items for each sample.
* @hide
*/
- static public final int NUM_SAMPLE_DATA = 9;
+ static private final int NUM_SAMPLE_DATA = 9;
/**
* Number of possible pointers.
@@ -918,7 +918,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* 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/4 radians (finger pointing fully left) to PI/4 radians
+ * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
* (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.
@@ -1453,43 +1453,8 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final Parcelable.Creator<MotionEvent> CREATOR
= new Parcelable.Creator<MotionEvent>() {
public MotionEvent createFromParcel(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.mDownTimeNano = in.readLong();
- ev.mAction = in.readInt();
- ev.mXOffset = in.readFloat();
- ev.mYOffset = in.readFloat();
- ev.mXPrecision = in.readFloat();
- ev.mYPrecision = in.readFloat();
- ev.mDeviceId = in.readInt();
- ev.mSource = in.readInt();
- ev.mEdgeFlags = in.readInt();
- ev.mMetaState = 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;
- return ev;
+ in.readInt(); // skip token, we already know this is a MotionEvent
+ return MotionEvent.createFromParcelBody(in);
}
public MotionEvent[] newArray(int size) {
@@ -1497,11 +1462,50 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
};
- public int describeContents() {
- return 0;
- }
+ /** @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();
+
+ 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;
+ 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;
@@ -1509,14 +1513,14 @@ public final class MotionEvent extends InputEvent implements Parcelable {
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(mDeviceId);
- out.writeInt(mSource);
out.writeInt(mEdgeFlags);
out.writeInt(mMetaState);
@@ -1614,28 +1618,28 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* 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/4 radians (finger pointing fully left) to PI/4 radians
+ * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
* (finger pointing fully right).
*/
public float orientation;
/*
- private static final float PI_8 = (float) (Math.PI / 8);
+ private static final float PI_4 = (float) (Math.PI / 4);
public float getTouchWidth() {
- return Math.abs(orientation) > PI_8 ? touchMajor : touchMinor;
+ return Math.abs(orientation) > PI_4 ? touchMajor : touchMinor;
}
public float getTouchHeight() {
- return Math.abs(orientation) > PI_8 ? touchMinor : touchMajor;
+ return Math.abs(orientation) > PI_4 ? touchMinor : touchMajor;
}
public float getToolWidth() {
- return Math.abs(orientation) > PI_8 ? toolMajor : toolMinor;
+ return Math.abs(orientation) > PI_4 ? toolMajor : toolMinor;
}
public float getToolHeight() {
- return Math.abs(orientation) > PI_8 ? toolMinor : toolMajor;
+ return Math.abs(orientation) > PI_4 ? toolMinor : toolMajor;
}
*/
}
diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java
index 784a75f22a7c..4cb0839036f2 100644
--- a/core/java/android/widget/Scroller.java
+++ b/core/java/android/widget/Scroller.java
@@ -218,7 +218,11 @@ public class Scroller {
// Pin to mMinY <= mCurrY <= mMaxY
mCurrY = Math.min(mCurrY, mMaxY);
mCurrY = Math.max(mCurrY, mMinY);
-
+
+ if (mCurrX == mFinalX && mCurrY == mFinalY) {
+ mFinished = true;
+ }
+
break;
}
}
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index a96253bb4bc4..d1aff2a0bfb8 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -84,7 +84,7 @@ public final class ShutdownThread extends Thread {
public static void shutdown(final Context context, boolean confirm) {
// ensure that only one thread is trying to power down.
// any additional calls are just returned
- synchronized (sIsStartedGuard){
+ synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Request to shutdown already running, returning.");
return;
@@ -133,6 +133,10 @@ public final class ShutdownThread extends Thread {
private static void beginShutdownSequence(Context context) {
synchronized (sIsStartedGuard) {
+ if (sIsStarted) {
+ Log.d(TAG, "Request to shutdown already running, returning.");
+ return;
+ }
sIsStarted = true;
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 642c313eec11..b3323e9aca8d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -63,7 +63,7 @@ public final class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 48;
+ private static final int VERSION = 49;
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 1000;
@@ -184,6 +184,8 @@ public final class BatteryStatsImpl extends BatteryStats {
int mDischargeStartLevel;
int mDischargeUnplugLevel;
int mDischargeCurrentLevel;
+ int mLowDischargeAmountSinceCharge;
+ int mHighDischargeAmountSinceCharge;
long mLastWriteTime = 0; // Milliseconds
@@ -260,7 +262,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mPluggedCount = in.readInt();
mCount.set(mPluggedCount);
mLoadedCount = in.readInt();
- mLastCount = in.readInt();
+ mLastCount = 0;
mUnpluggedCount = in.readInt();
unpluggables.add(this);
}
@@ -273,7 +275,6 @@ public final class BatteryStatsImpl extends BatteryStats {
public void writeToParcel(Parcel out) {
out.writeInt(mCount.get());
out.writeInt(mLoadedCount);
- out.writeInt(mLastCount);
out.writeInt(mUnpluggedCount);
}
@@ -348,13 +349,12 @@ public final class BatteryStatsImpl extends BatteryStats {
void writeSummaryFromParcelLocked(Parcel out) {
int count = mCount.get();
out.writeInt(count);
- out.writeInt(count - mLoadedCount);
}
void readSummaryFromParcelLocked(Parcel in) {
mLoadedCount = in.readInt();
mCount.set(mLoadedCount);
- mLastCount = in.readInt();
+ mLastCount = 0;
mUnpluggedCount = mPluggedCount = mLoadedCount;
}
}
@@ -428,11 +428,11 @@ public final class BatteryStatsImpl extends BatteryStats {
mCount = in.readInt();
mLoadedCount = in.readInt();
- mLastCount = in.readInt();
+ mLastCount = 0;
mUnpluggedCount = in.readInt();
mTotalTime = in.readLong();
mLoadedTime = in.readLong();
- mLastTime = in.readLong();
+ mLastTime = 0;
mUnpluggedTime = in.readLong();
unpluggables.add(this);
}
@@ -467,11 +467,9 @@ public final class BatteryStatsImpl extends BatteryStats {
public void writeToParcel(Parcel out, long batteryRealtime) {
out.writeInt(mCount);
out.writeInt(mLoadedCount);
- out.writeInt(mLastCount);
out.writeInt(mUnpluggedCount);
out.writeLong(computeRunTimeLocked(batteryRealtime));
out.writeLong(mLoadedTime);
- out.writeLong(mLastTime);
out.writeLong(mUnpluggedTime);
}
@@ -569,18 +567,16 @@ public final class BatteryStatsImpl extends BatteryStats {
long runTime = computeRunTimeLocked(batteryRealtime);
// Divide by 1000 for backwards compatibility
out.writeLong((runTime + 500) / 1000);
- out.writeLong(((runTime - mLoadedTime) + 500) / 1000);
out.writeInt(mCount);
- out.writeInt(mCount - mLoadedCount);
}
void readSummaryFromParcelLocked(Parcel in) {
// Multiply by 1000 for backwards compatibility
mTotalTime = mLoadedTime = in.readLong() * 1000;
- mLastTime = in.readLong() * 1000;
+ mLastTime = 0;
mUnpluggedTime = mTotalTime;
mCount = mLoadedCount = in.readInt();
- mLastCount = in.readInt();
+ mLastCount = 0;
mUnpluggedCount = mCount;
}
}
@@ -1243,7 +1239,6 @@ public final class BatteryStatsImpl extends BatteryStats {
if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(SystemClock.elapsedRealtime());
- mGpsNesting++;
mScreenOn = true;
mScreenOnTimer.startRunningLocked(this);
if (mScreenBrightnessBin >= 0) {
@@ -1297,7 +1292,7 @@ public final class BatteryStatsImpl extends BatteryStats {
public void notePhoneOnLocked() {
if (!mPhoneOn) {
- mHistoryCur.states |= HistoryItem.STATE_PHONE_ON_FLAG;
+ mHistoryCur.states |= HistoryItem.STATE_PHONE_IN_CALL_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(SystemClock.elapsedRealtime());
@@ -1308,7 +1303,7 @@ public final class BatteryStatsImpl extends BatteryStats {
public void notePhoneOffLocked() {
if (mPhoneOn) {
- mHistoryCur.states &= ~HistoryItem.STATE_PHONE_ON_FLAG;
+ mHistoryCur.states &= ~HistoryItem.STATE_PHONE_IN_CALL_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(SystemClock.elapsedRealtime());
@@ -1317,38 +1312,43 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ void stopAllSignalStrengthTimersLocked(int except) {
+ for (int i = 0; i < NUM_SIGNAL_STRENGTH_BINS; i++) {
+ if (i == except) {
+ continue;
+ }
+ while (mPhoneSignalStrengthsTimer[i].isRunningLocked()) {
+ mPhoneSignalStrengthsTimer[i].stopRunningLocked(this);
+ }
+ }
+ }
+
/**
* Telephony stack updates the phone state.
* @param state phone state from ServiceState.getState()
*/
public void notePhoneStateLocked(int state) {
+ boolean scanning = false;
+
int bin = mPhoneSignalStrengthBin;
- boolean isAirplaneMode = state == ServiceState.STATE_POWER_OFF;
- // Stop all timers
- if (isAirplaneMode || state == ServiceState.STATE_OUT_OF_SERVICE) {
- for (int i = 0; i < NUM_SIGNAL_STRENGTH_BINS; i++) {
- while (mPhoneSignalStrengthsTimer[i].isRunningLocked()) {
- mPhoneSignalStrengthsTimer[i].stopRunningLocked(this);
- }
- }
- }
- // Stop Signal Scanning timer, in case we're going into service
- if (mPhoneSignalScanningTimer.isRunningLocked()) {
- mHistoryCur.states &= ~HistoryItem.STATE_PHONE_SCANNING_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Phone stopped scanning to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
- mPhoneSignalScanningTimer.stopRunningLocked(this);
- }
+
+ // If the phone is powered off, stop all timers.
+ if (state == ServiceState.STATE_POWER_OFF) {
+ stopAllSignalStrengthTimersLocked(-1);
// If we're back in service or continuing in service, restart the old timer.
- if (state == ServiceState.STATE_IN_SERVICE) {
+ } if (state == ServiceState.STATE_IN_SERVICE) {
if (bin == -1) bin = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
if (!mPhoneSignalStrengthsTimer[bin].isRunningLocked()) {
mPhoneSignalStrengthsTimer[bin].startRunningLocked(this);
}
+
+ // If we're out of service, we are in the lowest signal strength
+ // bin and have the scanning bit set.
} else if (state == ServiceState.STATE_OUT_OF_SERVICE) {
+ scanning = true;
mPhoneSignalStrengthBin = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ stopAllSignalStrengthTimersLocked(mPhoneSignalStrengthBin);
if (!mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].isRunningLocked()) {
mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].startRunningLocked(this);
}
@@ -1361,6 +1361,17 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ if (!scanning) {
+ // If we are no longer scanning, then stop the scanning timer.
+ if (mPhoneSignalScanningTimer.isRunningLocked()) {
+ mHistoryCur.states &= ~HistoryItem.STATE_PHONE_SCANNING_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Phone stopped scanning to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ mPhoneSignalScanningTimer.stopRunningLocked(this);
+ }
+ }
+
if (mPhoneServiceState != state) {
mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_PHONE_STATE_MASK)
| (state << HistoryItem.STATE_PHONE_STATE_SHIFT);
@@ -2622,10 +2633,6 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeLong(mLoadedSystemTime);
out.writeLong(mLoadedForegroundTime);
out.writeInt(mLoadedStarts);
- out.writeLong(mLastUserTime);
- out.writeLong(mLastSystemTime);
- out.writeLong(mLastForegroundTime);
- out.writeInt(mLastStarts);
out.writeLong(mUnpluggedUserTime);
out.writeLong(mUnpluggedSystemTime);
out.writeLong(mUnpluggedForegroundTime);
@@ -2652,10 +2659,10 @@ public final class BatteryStatsImpl extends BatteryStats {
mLoadedSystemTime = in.readLong();
mLoadedForegroundTime = in.readLong();
mLoadedStarts = in.readInt();
- mLastUserTime = in.readLong();
- mLastSystemTime = in.readLong();
- mLastForegroundTime = in.readLong();
- mLastStarts = in.readInt();
+ mLastUserTime = 0;
+ mLastSystemTime = 0;
+ mLastForegroundTime = 0;
+ mLastStarts = 0;
mUnpluggedUserTime = in.readLong();
mUnpluggedSystemTime = in.readLong();
mUnpluggedForegroundTime = in.readLong();
@@ -2828,7 +2835,7 @@ public final class BatteryStatsImpl extends BatteryStats {
void readFromParcelLocked(Parcel in) {
mWakeups = in.readInt();
mLoadedWakeups = in.readInt();
- mLastWakeups = in.readInt();
+ mLastWakeups = 0;
mUnpluggedWakeups = in.readInt();
int numServs = in.readInt();
@@ -2845,7 +2852,6 @@ public final class BatteryStatsImpl extends BatteryStats {
void writeToParcelLocked(Parcel out) {
out.writeInt(mWakeups);
out.writeInt(mLoadedWakeups);
- out.writeInt(mLastWakeups);
out.writeInt(mUnpluggedWakeups);
out.writeInt(mServiceStats.size());
@@ -3002,9 +3008,9 @@ public final class BatteryStatsImpl extends BatteryStats {
mLoadedStartTime = in.readLong();
mLoadedStarts = in.readInt();
mLoadedLaunches = in.readInt();
- mLastStartTime = in.readLong();
- mLastStarts = in.readInt();
- mLastLaunches = in.readInt();
+ mLastStartTime = 0;
+ mLastStarts = 0;
+ mLastLaunches = 0;
mUnpluggedStartTime = in.readLong();
mUnpluggedStarts = in.readInt();
mUnpluggedLaunches = in.readInt();
@@ -3022,9 +3028,6 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeLong(mLoadedStartTime);
out.writeInt(mLoadedStarts);
out.writeInt(mLoadedLaunches);
- out.writeLong(mLastStartTime);
- out.writeInt(mLastStarts);
- out.writeInt(mLastLaunches);
out.writeLong(mUnpluggedStartTime);
out.writeInt(mUnpluggedStarts);
out.writeInt(mUnpluggedLaunches);
@@ -3336,6 +3339,8 @@ public final class BatteryStatsImpl extends BatteryStats {
mDischargeStartLevel = 0;
mDischargeUnplugLevel = 0;
mDischargeCurrentLevel = 0;
+ mLowDischargeAmountSinceCharge = 0;
+ mHighDischargeAmountSinceCharge = 0;
}
public BatteryStatsImpl(Parcel p) {
@@ -3434,6 +3439,8 @@ public final class BatteryStatsImpl extends BatteryStats {
doWrite = true;
resetAllStatsLocked();
mDischargeStartLevel = level;
+ mLowDischargeAmountSinceCharge = 0;
+ mHighDischargeAmountSinceCharge = 0;
}
updateKernelWakelocksLocked();
mHistoryCur.batteryLevel = (byte)level;
@@ -3457,6 +3464,10 @@ public final class BatteryStatsImpl extends BatteryStats {
mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart;
mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart;
mDischargeCurrentLevel = level;
+ if (level < mDischargeUnplugLevel) {
+ mLowDischargeAmountSinceCharge = mDischargeUnplugLevel-level-1;
+ mHighDischargeAmountSinceCharge = mDischargeUnplugLevel-level;
+ }
doPlugLocked(getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime));
}
if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
@@ -3723,6 +3734,20 @@ public final class BatteryStatsImpl extends BatteryStats {
}
@Override
+ public int getLowDischargeAmountSinceCharge() {
+ synchronized(this) {
+ return mLowDischargeAmountSinceCharge;
+ }
+ }
+
+ @Override
+ public int getHighDischargeAmountSinceCharge() {
+ synchronized(this) {
+ return mHighDischargeAmountSinceCharge;
+ }
+ }
+
+ @Override
public int getCpuSpeedSteps() {
return sNumSpeedSteps;
}
@@ -3926,15 +3951,13 @@ public final class BatteryStatsImpl extends BatteryStats {
mStartCount = in.readInt();
mBatteryUptime = in.readLong();
- mBatteryLastUptime = in.readLong();
mBatteryRealtime = in.readLong();
- mBatteryLastRealtime = in.readLong();
mUptime = in.readLong();
- mLastUptime = in.readLong();
mRealtime = in.readLong();
- mLastRealtime = in.readLong();
mDischargeUnplugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
+ mLowDischargeAmountSinceCharge = in.readInt();
+ mHighDischargeAmountSinceCharge = in.readInt();
mStartCount++;
@@ -4058,11 +4081,8 @@ public final class BatteryStatsImpl extends BatteryStats {
String procName = in.readString();
Uid.Proc p = u.getProcessStatsLocked(procName);
p.mUserTime = p.mLoadedUserTime = in.readLong();
- p.mLastUserTime = in.readLong();
p.mSystemTime = p.mLoadedSystemTime = in.readLong();
- p.mLastSystemTime = in.readLong();
p.mStarts = p.mLoadedStarts = in.readInt();
- p.mLastStarts = in.readInt();
}
NP = in.readInt();
@@ -4074,17 +4094,13 @@ public final class BatteryStatsImpl extends BatteryStats {
String pkgName = in.readString();
Uid.Pkg p = u.getPackageStatsLocked(pkgName);
p.mWakeups = p.mLoadedWakeups = in.readInt();
- p.mLastWakeups = in.readInt();
final int NS = in.readInt();
for (int is = 0; is < NS; is++) {
String servName = in.readString();
Uid.Pkg.Serv s = u.getServiceStatsLocked(pkgName, servName);
s.mStartTime = s.mLoadedStartTime = in.readLong();
- s.mLastStartTime = in.readLong();
s.mStarts = s.mLoadedStarts = in.readInt();
- s.mLastStarts = in.readInt();
s.mLaunches = s.mLoadedLaunches = in.readInt();
- s.mLastLaunches = in.readInt();
}
}
@@ -4111,16 +4127,13 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(mStartCount);
out.writeLong(computeBatteryUptime(NOW_SYS, STATS_SINCE_CHARGED));
- out.writeLong(computeBatteryUptime(NOW_SYS, STATS_CURRENT));
out.writeLong(computeBatteryRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED));
- out.writeLong(computeBatteryRealtime(NOWREAL_SYS, STATS_CURRENT));
out.writeLong(computeUptime(NOW_SYS, STATS_SINCE_CHARGED));
- out.writeLong(computeUptime(NOW_SYS, STATS_CURRENT));
out.writeLong(computeRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED));
- out.writeLong(computeRealtime(NOWREAL_SYS, STATS_CURRENT));
out.writeInt(mDischargeUnplugLevel);
out.writeInt(mDischargeCurrentLevel);
-
+ out.writeInt(mLowDischargeAmountSinceCharge);
+ out.writeInt(mHighDischargeAmountSinceCharge);
mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
@@ -4256,11 +4269,8 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeString(ent.getKey());
Uid.Proc ps = ent.getValue();
out.writeLong(ps.mUserTime);
- out.writeLong(ps.mUserTime - ps.mLoadedUserTime);
out.writeLong(ps.mSystemTime);
- out.writeLong(ps.mSystemTime - ps.mLoadedSystemTime);
out.writeInt(ps.mStarts);
- out.writeInt(ps.mStarts - ps.mLoadedStarts);
}
}
@@ -4272,7 +4282,6 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeString(ent.getKey());
Uid.Pkg ps = ent.getValue();
out.writeInt(ps.mWakeups);
- out.writeInt(ps.mWakeups - ps.mLoadedWakeups);
final int NS = ps.mServiceStats.size();
out.writeInt(NS);
if (NS > 0) {
@@ -4282,11 +4291,8 @@ public final class BatteryStatsImpl extends BatteryStats {
BatteryStatsImpl.Uid.Pkg.Serv ss = sent.getValue();
long time = ss.getStartTimeToNowLocked(NOW);
out.writeLong(time);
- out.writeLong(time - ss.mLoadedStartTime);
out.writeInt(ss.mStarts);
- out.writeInt(ss.mStarts - ss.mLoadedStarts);
out.writeInt(ss.mLaunches);
- out.writeInt(ss.mLaunches - ss.mLoadedLaunches);
}
}
}
@@ -4311,9 +4317,9 @@ public final class BatteryStatsImpl extends BatteryStats {
mStartCount = in.readInt();
mBatteryUptime = in.readLong();
- mBatteryLastUptime = in.readLong();
+ mBatteryLastUptime = 0;
mBatteryRealtime = in.readLong();
- mBatteryLastRealtime = in.readLong();
+ mBatteryLastRealtime = 0;
mScreenOn = false;
mScreenOnTimer = new StopwatchTimer(-1, null, mUnpluggables, in);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
@@ -4337,10 +4343,10 @@ public final class BatteryStatsImpl extends BatteryStats {
mBluetoothOnTimer = new StopwatchTimer(-2, null, mUnpluggables, in);
mUptime = in.readLong();
mUptimeStart = in.readLong();
- mLastUptime = in.readLong();
+ mLastUptime = 0;
mRealtime = in.readLong();
mRealtimeStart = in.readLong();
- mLastRealtime = in.readLong();
+ mLastRealtime = 0;
mOnBattery = in.readInt() != 0;
mOnBatteryInternal = false; // we are no longer really running.
mTrackBatteryPastUptime = in.readLong();
@@ -4351,6 +4357,8 @@ public final class BatteryStatsImpl extends BatteryStats {
mUnpluggedBatteryRealtime = in.readLong();
mDischargeUnplugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
+ mLowDischargeAmountSinceCharge = in.readInt();
+ mHighDischargeAmountSinceCharge = in.readInt();
mLastWriteTime = in.readLong();
mMobileDataRx[STATS_LAST] = in.readLong();
@@ -4396,11 +4404,15 @@ public final class BatteryStatsImpl extends BatteryStats {
}
public void writeToParcel(Parcel out, int flags) {
- writeToParcelLocked(out, flags);
+ writeToParcelLocked(out, true, flags);
+ }
+
+ public void writeToParcelWithoutUids(Parcel out, int flags) {
+ writeToParcelLocked(out, false, flags);
}
@SuppressWarnings("unused")
- void writeToParcelLocked(Parcel out, int flags) {
+ void writeToParcelLocked(Parcel out, boolean inclUids, int flags) {
final long uSecUptime = SystemClock.uptimeMillis() * 1000;
final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
final long batteryUptime = getBatteryUptimeLocked(uSecUptime);
@@ -4412,9 +4424,7 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(mStartCount);
out.writeLong(mBatteryUptime);
- out.writeLong(mBatteryLastUptime);
out.writeLong(mBatteryRealtime);
- out.writeLong(mBatteryLastRealtime);
mScreenOnTimer.writeToParcel(out, batteryRealtime);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].writeToParcel(out, batteryRealtime);
@@ -4433,10 +4443,8 @@ public final class BatteryStatsImpl extends BatteryStats {
mBluetoothOnTimer.writeToParcel(out, batteryRealtime);
out.writeLong(mUptime);
out.writeLong(mUptimeStart);
- out.writeLong(mLastUptime);
out.writeLong(mRealtime);
out.writeLong(mRealtimeStart);
- out.writeLong(mLastRealtime);
out.writeInt(mOnBattery ? 1 : 0);
out.writeLong(batteryUptime);
out.writeLong(mTrackBatteryUptimeStart);
@@ -4446,6 +4454,8 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeLong(mUnpluggedBatteryRealtime);
out.writeInt(mDischargeUnplugLevel);
out.writeInt(mDischargeCurrentLevel);
+ out.writeInt(mLowDischargeAmountSinceCharge);
+ out.writeInt(mHighDischargeAmountSinceCharge);
out.writeLong(mLastWriteTime);
out.writeLong(getMobileTcpBytesReceived(STATS_SINCE_UNPLUGGED));
@@ -4458,27 +4468,35 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(getBluetoothPingCount());
- out.writeInt(mKernelWakelockStats.size());
- for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
- SamplingTimer kwlt = ent.getValue();
- if (kwlt != null) {
- out.writeInt(1);
- out.writeString(ent.getKey());
- Timer.writeTimerToParcel(out, kwlt, batteryRealtime);
- } else {
- out.writeInt(0);
+ if (inclUids) {
+ out.writeInt(mKernelWakelockStats.size());
+ for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
+ SamplingTimer kwlt = ent.getValue();
+ if (kwlt != null) {
+ out.writeInt(1);
+ out.writeString(ent.getKey());
+ Timer.writeTimerToParcel(out, kwlt, batteryRealtime);
+ } else {
+ out.writeInt(0);
+ }
}
+ } else {
+ out.writeInt(0);
}
out.writeInt(sNumSpeedSteps);
- int size = mUidStats.size();
- out.writeInt(size);
- for (int i = 0; i < size; i++) {
- out.writeInt(mUidStats.keyAt(i));
- Uid uid = mUidStats.valueAt(i);
+ if (inclUids) {
+ int size = mUidStats.size();
+ out.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ out.writeInt(mUidStats.keyAt(i));
+ Uid uid = mUidStats.valueAt(i);
- uid.writeToParcelLocked(out, batteryRealtime);
+ uid.writeToParcelLocked(out, batteryRealtime);
+ }
+ } else {
+ out.writeInt(0);
}
}
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 7b2341813099..e29495cf0543 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -38,6 +38,7 @@ struct SensorOffsets
jfieldID range;
jfieldID resolution;
jfieldID power;
+ jfieldID minDelay;
} gSensorOffsets;
/*
@@ -74,6 +75,7 @@ sensors_module_get_next_sensor(JNIEnv *env, jobject clazz, jobject sensor, jint
env->SetFloatField(sensor, sensorOffsets.range, list->getMaxValue());
env->SetFloatField(sensor, sensorOffsets.resolution, list->getResolution());
env->SetFloatField(sensor, sensorOffsets.power, list->getPowerUsage());
+ env->SetIntField(sensor, sensorOffsets.minDelay, list->getMinDelay());
next++;
return next<count ? next : 0;
@@ -154,6 +156,7 @@ nativeClassInit (JNIEnv *_env, jclass _this)
sensorOffsets.range = _env->GetFieldID(sensorClass, "mMaxRange", "F");
sensorOffsets.resolution = _env->GetFieldID(sensorClass, "mResolution","F");
sensorOffsets.power = _env->GetFieldID(sensorClass, "mPower", "F");
+ sensorOffsets.minDelay = _env->GetFieldID(sensorClass, "mMinDelay", "I");
}
static JNINativeMethod gMethods[] = {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 040b32494def..5c4e4fd971dc 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -1566,15 +1566,15 @@ static void android_os_Parcel_enforceInterface(JNIEnv* env, jobject clazz, jstri
if (parcel != NULL) {
const jchar* str = env->GetStringCritical(name, 0);
if (str) {
- const int32_t old_strict_policy =
- IPCThreadState::self()->getStrictModePolicy();
- int32_t strict_policy;
- bool isValid = parcel->enforceInterface(
+ IPCThreadState* threadState = IPCThreadState::self();
+ const int32_t oldPolicy = threadState->getStrictModePolicy();
+ const bool isValid = parcel->enforceInterface(
String16(str, env->GetStringLength(name)),
- &strict_policy);
+ threadState);
env->ReleaseStringCritical(name, str);
if (isValid) {
- if (old_strict_policy != strict_policy) {
+ const int32_t newPolicy = threadState->getStrictModePolicy();
+ if (oldPolicy != newPolicy) {
// Need to keep the Java-level thread-local strict
// mode policy in sync for the libcore
// enforcements, which involves an upcall back
@@ -1582,7 +1582,7 @@ static void android_os_Parcel_enforceInterface(JNIEnv* env, jobject clazz, jstri
// Parcel.enforceInterface signature, as it's
// pseudo-public, and used via AIDL
// auto-generation...)
- set_dalvik_blockguard_policy(env, strict_policy);
+ set_dalvik_blockguard_policy(env, newPolicy);
}
return; // everything was correct -> return silently
}
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 19f0f1df950b..d0318cfba096 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -371,6 +371,9 @@
<span class="zh-CN" style="display:none">应用程序版本控制</span>
<span class="zh-TW" style="display:none">應用程式版本設定</span>
</a></li>
+ <li><a href="<?cs var:toroot ?>guide/publishing/licensing.html">
+ <span class="en">Licensing Your Applications</span>
+ </a> <span class="new">new!</span></li>
<li><a href="<?cs var:toroot ?>guide/publishing/preparing.html">
<span class="en">Preparing to Publish</span>
<span class="de" style="display:none">Vorbereitung auf die Veröffentlichung</span>
diff --git a/docs/html/guide/practices/ui_guidelines/activity_task_design.jd b/docs/html/guide/practices/ui_guidelines/activity_task_design.jd
index 05f61beb86e0..c8d241c68ed7 100644
--- a/docs/html/guide/practices/ui_guidelines/activity_task_design.jd
+++ b/docs/html/guide/practices/ui_guidelines/activity_task_design.jd
@@ -256,8 +256,8 @@ page.title=Activity and Task Design Guidelines
to be able go to a subsequent screen B and then use the BACK key to go
back to screen A, then the screen A needs to be implemented as an
activity. The one exception to this rule is if your application
- <a href=#taking_over_back_key title="takes control of the BACK key"
- takes control of the BACK key</a> and manages the navigation itself.
+ <a href="#taking_over_back_key">takes control of the BACK key</a> and manages the navigation
+itself.
</p>
diff --git a/docs/html/guide/publishing/licensing.jd b/docs/html/guide/publishing/licensing.jd
new file mode 100644
index 000000000000..e3b913585354
--- /dev/null
+++ b/docs/html/guide/publishing/licensing.jd
@@ -0,0 +1,2373 @@
+page.title=Licensing Your Applications
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>Market Licensing quickview: </h2>
+ <ul>
+ <li>Licensing lets you protect your application on any device that includes Android Market.</li>
+ <li>Your app maintains control of how it enforces its licensing status. </li>
+ <li>Adding licensing to an app is straightforward, using the library available through the SDK.</li>
+ <li>The service is free and is available to all developers who publish on Android Market. </li>
+ </ul>
+
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#account">Setting Up A Publisher Account</a></li>
+ <li><a href="#dev-setup">Setting Up the Development Environment</a></li>
+ <li><a href="#app-integration">Integrating the LVL with Your Application</a>
+ <ol>
+ <li><a href="#add-library">Including the LVL</a></li>
+ <li><a href="#manifest-permission">Adding the licensing permission</a></li>
+ <li><a href="#impl-Policy">Implementing a Policy</a></li>
+ <li><a href="#impl-Obfuscator">Implementing an Obfuscator</a></li>
+ <li><a href="#impl-lc">Checking the license</a></li>
+ <li><a href="#impl-DeviceLimiter">Implementing a DeviceLimiter</a></li>
+ </ol></li>
+ <li><a href="#test-env">Setting Up the Test Environment</a>
+ <ol>
+ <li><a href="#test-response">Test responses</a></li>
+ <li><a href="#test-acct-setup">Test accounts</a></li>
+ <li><a href="#acct-signin">Signing in on a device or emulator</a></li>
+ </ol></li>
+ <li><a href="#app-obfuscation">Obfuscating Your Application</a></li>
+ <li><a href="#app-publishing">Publishing a Licensed Application</a></li>
+ <li><a href="#support">Where to Get Support</a></li>
+ </ol>
+
+ <h2>Appendix</h2>
+ <ol>
+ <li><a href="#lvl-summary">Summary of LVL Classes and Interfaces</a></li>
+ <li><a href="#server-response-codes">Server Response Codes</a></li>
+ <li><a href="#extras">Server Response Extras</a></li>
+ </ol>
+
+</div>
+</div>
+
+<p>Android Market offers a licensing service that lets you enforce licensing
+policies for paid applications that you publish through Android Market. With
+Android Market Licensing, your applications can query Android Market at run time to
+obtain their licensing status for the current user, then allow or disallow
+further use as appropriate. </p>
+
+<p>Using the service, you can apply a flexible licensing policy on an
+application-by-application basis &mdash; each application can enforce licensing
+in the way most appropriate for it. If necessary, an application can apply custom
+constraints based on the licensing status obtained from Android Market.
+For example, an application can check the licensing status and then apply custom
+constraints that allow the user to run it unlicensed for a specific number
+of times, or for a specific validity period. An application can also restrict use of the
+application to a specific device, in addition to any other constraints. </p>
+
+<p>The licensing service is a secure means of controlling access to your
+applications. When an application checks the licensing status, the Market server
+signs the licensing status response using a key pair that is uniquely associated
+with the publisher account. Your application stores the public key in its
+compiled <code>.apk</code> file and uses it to verify the licensing status
+response.</p>
+
+<p>Any application that you publish through Android Market can use the Android
+Market Licensing service. No special account or registration is needed.
+Additionally, because the service uses no dedicated framework APIs, you can add
+licensing to any legacy application that uses a minimum API level of 3 or
+higher.</p>
+
+<p>To help you add licensing to your application, the Android SDK provides
+library sources that you can include in your application project. The
+License Verification Library (LVL) handles all of
+the licensing-related communication with the Android Market client and the
+licensing service. With the LVL integrated, your application can determine its
+licensing status for the current user by simply calling a library checker method
+and implementing a callback that receives the status.</p>
+
+<p>This document explains how the licensing service works and how to add it to
+your application. </p>
+
+
+<h2 id="overview">Overview</h2>
+
+<p>Android Market Licensing is a network-based service that lets an application
+on an Android-powered device query a trusted licensing server, to determine
+whether the application is licensed to the current device user. After receiving
+the server response, the application can then allow or disallow further use of
+the application as needed. In the service, the role of the licensing server is
+to provide the license status for the current user; the application itself is
+responsible for querying the server and conditionally granting access to the
+application. </p>
+
+<h4>Application, Android Market client, and server</h4>
+
+<p>The licensing service is based on the capability of the Android Market server
+to determine whether a given user is licensed to use a given application. The
+server considers a user licensed if the user is recorded to have purchased the
+application, or if the application is available for free. To properly identify
+the user and determine the license status, the server requires information about
+the application and user &mdash; the application and the Android Market client
+work together to assemble the information and pass it to the server. </p>
+
+<p>In the licensing service, an application does not query the licensing server
+directly, but instead calls the Android Market client over remote IPC to
+initiate a license request. In the license request:</p>
+
+<ul>
+<li>The application provides its package name and a nonce that is later used to
+validate any response from the server, as well as a callback over which the
+response can be returned asynchronously.</li>
+<li>The Android Market client, which has greater permissions than the
+application, collects the necessary information about the user and the device,
+such as the device's primary Google account username, IMSI, and other
+information. It then sends the license check request to the server on behalf of
+the application.</li>
+<li>The server evaluates the request using all available information, attempting
+to establish the user's identity to a sufficient level of confidence. The server
+then checks the user identity against purchase records for the application and
+returns a license response, which the Android Market client returns to the
+application over the IPC callback.</li>
+</ul>
+
+<p>Notice that during a license check, the application does not manage any
+network connections or use any licensing related APIs in the Android platform.
+</p>
+
+<div class="figure" style="width:469px">
+<img src="{@docRoot}images/licensing_arch.png" alt=""/>
+<p class="img-caption"><strong>Figure 1.</strong> Your application initiates a
+license check through the LVL and the Android Market
+client, which handles communication with the Market server.</p>
+</div>
+
+<h4>License responses secured through public key cryptography</h4>
+
+<p>To ensure the integrity of each license query, the server signs the license
+response data using an RSA key pair that is shared exclusively between the
+server and the application publisher.</p>
+
+<p>The licensing service generates a single licensing key pair for each
+publisher account and exposes the public key in the account's profile page. The
+publisher copies the public key and embeds it in the application source code,
+then compiles and publishes the <code>.apk.</code> The server retains the
+private key internally and uses it to sign license responses for applications
+published on that account. </p>
+
+<p>When the application receives a signed response, it uses the embedded public
+key to verify the data. The use of public key cryptography in the licensing
+service makes it possible for the application to detect responses that have been
+tampered with or are spoofed.</p>
+
+<h4>Use of licensing in your application</h4>
+
+<p>To use licensing in your application, add code to the application to
+initiate a license check request and handle the response when it is received.
+You can choose when, and how often, you want your application to check its
+license and you have full control over how it handles the response, verifies the
+signed response data, and enforces access controls. </p>
+
+<p>To simplify the process of adding support for licensing, download and
+integrate the Licensing Verification Library, described below. Integration is
+straightforward.</p>
+
+<p>When you are finished integrating the LVL, use a test environment
+provided by the publisher site to test your application's handling of server
+responses. </p>
+
+<p>Finally, publish the application <code>.apk</code> on Market using the
+normal process. If you previously used the copy-protection provided by Android
+Market, you can remove it from applications that use licensing. </p>
+
+<h4>Licensing Verification Library simplifies implementation</h4>
+
+<p>The Android SDK includes a License Verification Library (LVL) that you can
+download and use as the basis for your application's licensing implementation.
+The LVL greatly simplifies the process of adding licensing to your application
+and helps ensure a more secure, robust implementation for your application. The
+LVL provides internal classes that handle most of the standard operations of a
+license query, such as contacting Android Market to initiate a license request
+and decrypting and validating the responses. It also exposes key interfaces that
+let you easily plug in your custom code for defining licensing policy and
+managing access as needed by your application. The key LVL interfaces are: </p>
+
+<ul>
+<li>Policy &mdash; your implementation determines whether to allow access to the
+application, based on the license response received from the server and any
+other data available (such as from a backend server associated with your
+application). The implementation can evaluate the various fields of the license
+response and apply other constraints, if needed. The implementation also lets
+you manage the handling of license checks that result in errors, such as network
+errors.</li>
+<li>LicenseCheckerCallback &mdash; your implementation manages access to the
+application, based on the result of the Policy's handling of the license
+response. Your implementation can manage access in any way needed, including
+displaying the license result in the UI or directing the user to purchase the
+application (if not currently licensed). </li>
+</ul>
+
+<p>To help you get started with a Policy, the LVL provides two fully complete
+Policy implementations that you can use without modification or adapt to your
+needs:</p>
+
+<ul>
+<li><a href="#ServerManagedPolicy">ServerManagedPolicy</a> is a flexible Policy
+that uses settings provided by the licensing server to manage response caching
+and access to the application while the device is offline (such as when the
+user is on on an airplane). For most applications, the use of
+ServerManagedPolicy is highly recommended. </li>
+<li><a href="#StrictPolicy">StrictPolicy</a> is a restrictive Policy that
+does not cache any response data and allows the application access <em>only</em>
+when the server returns a licensed response.</li>
+</ul>
+
+<p>The LVL is available as a downloadable component of the Android SDK. The
+component includes both the LVL itself and an example application that shows how
+the library should be integrated with your application and how your application
+should manage response data, UI interaction, and error conditions. </p>
+
+<p>The LVL sources are provided as an Android <em>library project</em>, which
+means that you can maintain a single set of library sources and share them
+across multiple applications. A full test environment is also available through
+the SDK, so you can develop and test the licensing implementation in your
+applications before publishing them, even if you don't have access to a
+physical device.</p>
+
+<h4>Requirements and limitations</h4>
+
+<p>Android Market Licensing is designed to let you apply license controls to
+applications that you publish through Android Market and that users download
+from Market. The service is not designed to let you control access to
+applications that are not published through Android Market or that are run
+on devices that do not offer the Android Market client. </p>
+
+<p>Here are some points to keep in mind as you implement licensing in your
+application: </p>
+
+<ul>
+<li>Only paid applications published through Market can use the
+service. </li>
+<li>An application can use the service only if the Android Market client is
+installed on its host device and the device is running Android 1.5 (API level 3)
+or higher.</li>
+<li>To complete a license check, the licensing server must be accessible over
+the network. You can implement license caching behaviors to manage access when
+there is no network connectivity. </li>
+<li>The security of your application's licensing controls ultimately relies on
+the design of your implementation itself. The service provides the building
+blocks that let you securely check licensing, but the actual enforcement and
+handling of the license are factors in your control. By following the best
+practices in this document, you can help ensure that your implementation will be
+secure.</li>
+<li>Adding licensing to an application does not affect the way the application
+functions when run on a device that does not offer Android Market.</li>
+<li>Licensing is currently for paid apps only, since free apps are considered
+licensed for all users. If your application is already published as free,
+you won't be able to upload a new version that uses licensing.</li>
+</ul>
+
+<h4>Replacement for copy protection</h4>
+
+<p>Android Market Licensing is a flexible, secure mechanism for controlling
+access to your applications. It effectively replaces the copy-protection
+mechanism and gives you wider distribution potential for your applications. </p>
+
+<ul>
+<li>A limitation of copy protection is that applications using it can be
+installed only on compatible devices that provide a secure internal storage
+environment. For example, a copy-protected application cannot be downloaded
+from Market to a device that provides root access, and the application
+cannot be installed to a device's SD card. </li>
+<li>With Android Market licensing, you can move to a license-based model in
+which access is not bound to the characteristics of the host device, but to your
+publisher account on Android Market and the licensing policy that you define.
+Your application can be installed and controlled on any compatible device on
+any storage, including SD card.</li>
+</ul>
+
+<p>Although no license mechanism can completely prevent all unauthorized use,
+the licensing service lets you control access for most types of normal usage,
+across all compatible devices, locked or unlocked, that run Android 1.5 or
+higher version of the platform.</p>
+
+<p>The sections below describe how to add Android Market licensing to your
+applications. </p>
+
+<h2 id="account">Setting Up a Publisher Account</h2>
+
+<p>Android Market licensing lets you manage access to applications that
+users have downloaded from Android Market. To use licensing in an application,
+you need to have a publisher account on Android Market so that you can
+publish the application to users. </p>
+
+<p>If you don't already have a publisher account, you need to register for one
+using your Google account and agree to the terms of service. Once you are
+registered, you can upload applications at your convenience and begin debugging
+and testing your licensing implementation. For more information about publishing
+on Android Market, see <a
+href="{@docRoot}guide/publishing/publishing.html">Publishing Your
+Applications</a></p>
+
+<p>To register as an Android Market developer and set up your publisher account,
+visit the Android Market publisher site:</p>
+
+<p style="margin-left:2em;"><a
+href="http://market.android.com/publish">http://market.android.com/publish</a>
+</p>
+
+<p>If you already have a publisher account on Android Market, use your existing
+account to set up licensing. You <em>do not</em> need to register for a new
+account to support licensing (and doing so is not recommended, especially if you
+are adding licensing support to applications that you have already published).
+In all cases, if you have published applications, you manage licensing for those
+applications through the account on which the applications are published. </p>
+
+<p>Once your publisher account is set up, use the account to:</p>
+
+<ul>
+<li>Obtain a public key for licensing</li>
+<li>Debug and test an application's licensing implementation, prior to
+publishing the application</li>
+<li>Publish the applications to which you have added licensing support</li>
+</ul>
+
+<h4>Administrative settings for licensing</h4>
+
+<p>Once you are signed into your publisher account, you can manage several
+administrative controls for Android Market licensing. The controls are available
+in the Edit Profile page, in the "Licensing" panel, shown below. The controls
+let you: </p>
+
+<ul>
+<li>Set up multiple "test accounts", identified by email address. The licensing
+server allows users signed into test accounts on a device or emulator to send
+license checks and receive static test reponses.</li>
+<li>Obtain the account's public key for licensing. When you are implementing
+licensing in an application, you must copy the public key string into the
+application.</li>
+<li>Configure static test responses that the server sends, when it receives a
+license check for an application uploaded to the publisher account, from a user
+signed in to the publisher account or a test account.</li>
+</ul>
+
+<div style="margin-bottom:2em;">
+
+<img src="{@docRoot}images/licensing_public_key.png" style="text-align:left;margin-bottom:0;" />
+<div style="margin:0 2em;padding:0"><strong>Figure 2.</strong> The Licensing
+panel of your account's Edit Profile page lets you manage administrative
+settings for licensing.</div>
+</div>
+
+<p>For more information about how to work with test accounts and static test
+responses, see <a href="#test-env">Setting Up a Testing Environment</a>, below.
+
+<h2 id="dev-setup">Setting Up the Development Environment</h2>
+
+<p>Once you've set up your publisher account on Android Market, the next step is
+to set up your development environment for licensing. </p>
+
+<p>Setting up your environment for licensing involves these tasks:</p>
+
+<ol>
+<li><a href="#download-sdk">Downloading the latest SDK</a>, if you haven't already done so </li>
+<li><a href="#runtime-setup">Setting up the runtime environment</a> for development</a></li>
+<li><a href="#download-lvl">Downloading the Market Licensing component</a> into your SDK </li>
+<li><a href="#lvl-setup">Setting up the Licensing Verification Library</a></li>
+<li><a href="#add-library">Including the LVL library project in your application</a></li>
+</ol>
+
+<p>The sections below describe these tasks. When you are done with setup,
+you can begin <a href="#app-integration">integrating the LVL into your applications</a>.</p>
+
+<p>To get started, you need to set up a proper runtime environment on which
+you can run, debug and test your application's implementation of license
+checking and enforcement. </p>
+
+
+<h3 id="download-sdk">Downloading the latest SDK</h3>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>Licensing sample application</h2>
+
+<p>To work with Android Market licensing, you need a functioning Android
+application to which you can add licensing support. </p>
+
+<p style="margin-top:.5em;">If you are new to Android
+and don't yet have a functioning application, the LVL component includes a sample
+application that you can set up as a new application project. The sample provides
+a complete, working example of how licensing works. For more information, see <a
+href="#download-lvl">Downloading the LVL</a>.</p>
+
+</div>
+</div>
+
+<p>If you haven't done so, you need to download the Android SDK before you can
+develop Android applications. The SDK provides the tools that you need to build
+and debug Android applications, including applications that use Android Market
+licensing. For complete information, including installation intructions, see
+the <a href="{@docRoot}sdk/index.html">Android SDK</a>. </p>
+
+<p>If you have already installed the SDK, make sure to update the
+SDK tools and ADT Plugin to the latest versions. You can update the SDK tools
+using the Android SDK and AVD Manager and ADT through <strong>Help</strong> &gt;
+<strong>Software Updates...</strong> in Eclipse. </p>
+
+<p>After you've installed the latest SDK and tools, set up your development
+environment as described below. </p>
+
+
+<h3 id="runtime-setup">Setting up the runtime environment</h3>
+
+<p>As described earlier, applications check licensing status not by contacting
+the licensing server directly, but by binding to a service provided by the
+Android Market application and initiating a license check request. The Android
+Market service then handles the direct communication with the licensing server
+and finally routes the response back to your application. To debug and test
+licensing in your application, you need to set up a runtime environment that
+includes the necessary Android Market service, so that your application is able
+to send license check requests to the licensing server. </p>
+
+<p>There are two types of runtime environment that you can use: </p>
+
+<ul>
+<li>An Android-powered device that includes the Android Market application, or</li>
+<li>An Android emulator running the Google APIs Add-on, API level 8 (release 2)
+or higher</li>
+</ul>
+
+<p>The sections below provide more information. </p>
+
+<h4 id="runtime-device">Running on a device</h4>
+
+<p>You can use an Android-powered device as the runtime environment for
+debugging and testing licensing on your application.</p>
+
+<p>The device you use must:</p>
+
+<ul>
+<li>Run a standard version of the Android 1.5 or later (API level
+3 or higher) platform, <em>and</em> </li>
+<li>Run a system image on which the Android Market client application
+is preinstalled. </li>
+</ul>
+
+<p>If Android Market is not preinstalled in the system image, your application won't
+be able to communicate with the Android Market licensing server. </p>
+
+<p>For general information about how to set up a device for use in developing
+Android applications, see <a
+href="{@docRoot}guide/developing/device.html">Developing on a Device</a>.</p>
+
+<h4 id="runtime-emulator">Running on an Android emulator</h4>
+
+<p>You can also use an Android emulator as your runtime
+environment for debugging and testing licensing.</p>
+
+<p>Because the standard Android platforms provided in the Android SDK <em>do
+not</em> include Android Market, you need to download the Google APIs Add-On
+platform, API Level 8 (or higher), from the SDK repository. After downloading
+the add-on, you need to create an AVD configuration that uses that system image.
+</p>
+
+<p>The Google APIs Add-On does not include the full Android Market client.
+However, it does provide: </p>
+
+<ul>
+<li>An Android Market background service that implements the
+<code>ILicensingService</code> remote interface, so that your application can
+send license checks over the network to the licensing server. </li>
+<li>A set of underlying account services that let you add an a Google account on
+the AVD and sign in using your publisher account or test account credentials.
+Signing in using your publisher or test account enables you to debug and test
+your application without having publish it. For more information see <a
+href="#acct-signin">Signing in to an authorized account</a>, below.</li>
+</ul>
+
+<p>Several versions of the add-on are available in the SDK repository, but only
+<strong>Google APIs Add-On, API 8 (release 2) or higher</strong> version of the
+add-on includes the necessary Android Market services. This means that you
+cannot use Google APIs Add-On API 7 or lower as a runtime environment for
+developing licensing on an emulator.</p>
+
+<div style="margin-bottom:2em;">
+
+<img src="{@docRoot}images/licensing_gapis_8.png" style="text-align:left;margin-bottom:0;" />
+<div style="margin:0 2em;padding:0"><strong>Figure 3.</strong> Google APIs
+Add-On, API 8 (release 2) or higher lets you debug and test your licensing
+implemention in an emulator.</div>
+</div>
+
+<p>To set up an emulator for adding licensing to an application, follow
+these steps: </p>
+
+<ol>
+ <li>Launch the Android SDK and AVD Manager. </li>
+ <li>In the <strong>Available Packages</strong> panel, select and download the
+SDK component "Google APIs (Google Inc.) - API Level 8" (or higher) from the SDK
+repository, as shown in the figure above.
+ <p>When the download is complete, use the Android SDK and AVD Manager to
+create a new AVD based on that component, described next.</p></li>
+ <li>In the <strong>Virtual
+Devices</strong> panel of the Android SDK and AVD Manager, click
+<strong>New</strong> and set the configuration details for the new AVD. </li>
+ <li>In the dialog that appears, assign a descriptive name to the AVD and then
+use the "Target" menu to choose the "Google APIs (Google Inc.) - API Level 8" as
+the system image to run on the new AVD. Set the other configuration details as
+needed and then click <strong>Create AVD</strong> to finish. The SDK tools
+create the new AVD configuration, which then appears in the list of available
+Android Virtual Devices.</li>
+</ol>
+
+<p>If you are not familiar with AVDs or how to use them, see <a
+href="{@docRoot}guide/developing/tools/avd.html">Android Virtual Devices</a>.</p>
+
+<h4 id="project-update">Updating your project configuration</h4>
+
+<p>After you set up a runtime environment that meets the requirements described
+above &mdash; either on an actual device or on an emulator &mdash; make sure to
+update your application project or build scripts as needed, so that your compiled
+<code>.apk</code> files that use licensing are deployed into that environment.
+In particular, if you are developing in Eclipse, make sure that you set up a
+Run/Debug Configuration that targets the appropriate device or AVD. </p>
+
+<p>You do not need to make any changes to your application's
+build configuration, provided that the project is already configured to compile
+against a standard Android 1.5 (API level 3) or higher library. For example:
+
+<ul>
+<li>If you have an existing application that is compiled against
+the Android 1.5 library, you do not need to make any changes to your
+build configuration to support licensing. The build target meets the minimum
+requirements for licensing, so you would continue building
+against the same version of the Android platform.</li>
+
+<li>Similarly, if you are building against Android 1.5 (API level 3) but
+are using an emulator running the Google APIs Add-On API 8 as the application's
+runtime environment, there is no need to change your application's build
+configuration. </li>
+</ul>
+
+<p>In general, adding licensing to an application should have no impact
+whatsoever on the application's build configuration.</p>
+
+
+<h3 id="download-lvl">Downloading the LVL</h3>
+
+<p>The License Verification Library (LVL) is a collection of helper classes that
+greatly simplify the work that you need to do to add licensing to your
+application. In all cases, we recommend that you download the LVL and use it as
+the basis for the licensing implementation in your application.</p>
+
+<p>The LVL is available as a downloadable component of the Android SDK. The
+component includes: </p>
+
+<ul>
+<li>The LVL sources, stored inside an Android library project. </li>
+<li>An example application called "sample" that depends on the LVL library
+project. The example illustrates how an application uses the library helper
+classes to check and enforce licensing.</li>
+</ul>
+
+<p>To download the LVL component into your development environment, use the
+Android SDK and AVD Manager. Launch the Android SDK and AVD Manager and then
+select the "Market Licensing" component, as shown in the figure below.
+Accept the terms and click <strong>Install Selected</strong> to begin the download. </p>
+
+<div style="margin-bottom:2em;">
+
+<img src="{@docRoot}images/licensing_package.png" style="text-align:left;margin-bottom:0;" />
+<div style="margin:0 2em;padding:0"><strong>Figure 4.</strong> The Market
+Licensing package contains the LVL and the LVL sample application. </div>
+</div>
+
+<p>When the download is complete, the Android SDK and AVD Manager installs both
+the LVL library project and the example application into these directories: </p>
+
+<p style="margin-left:2em"><code>&lt;<em>sdk</em>&gt;/marketlicensing/library/</code>
+&nbsp;&nbsp;(the LVL library project)<br />
+<code>&lt;<em>sdk</em>&gt;/marketlicensing/sample/</code>&nbsp;&nbsp;(the example
+application)</p>
+
+<p>If you aren't familiar with how to download components into your SDK, see the
+<a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>
+document. </p>
+
+
+<h3 id="lvl-setup">Setting Up the Licensing Verification Library</h3>
+
+<p>After downloading the LVL to your computer, you need to set it up in your
+development environment, either as an Android library project or by
+copying (or importing) the library sources directly into your existing
+application package. In general, using the LVL as a library project is recommended,
+since it lets you reuse your licensing code across multiple applications and
+maintain it more easily over time. Note that the LVL is not designed to be
+compiled separately and added to an application as a static .jar file. </p>
+
+<h4>Moving the library sources out location</h4>
+
+<p>Because you will be customizing the LVL sources to some extent, you should
+make sure to <em>move or copy</em> the library sources (the entire
+directory at <code>&lt;<em>sdk</em>&gt;/marketlicensing/library/</code>)
+to a working directory outside of the SDK. You can then add the sources
+in the working location to your source-code management system, rather
+than those in the SDK.</p>
+
+<p>Moving the library sources is important is because, when you later update the
+Market licensing package, the SDK installs the new files to the same location as
+the older files. Moving your working library files to a safe location ensures
+that they won't be inadvertently overwritten when you download a new version of
+the LVL.</p>
+
+<h4>Creating the LVL as a library project</h4>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>Working with library projects</h2>
+
+<p>The LVL is provided as an Android library project, which means that you can
+share its code and resources across multiple applications. </p>
+
+<p style="margin-top:.5em;">If you aren't familiar with library projects or how
+to use them, read more in <a
+href="{@docRoot}guide/developing/eclipse-adt.html#libraryProject">Developing in
+Eclipse with ADT</a> or <a
+href="{@docRoot}guide/developing/other-ide.html#libraryProject">Developing in
+Other IDEs</a>, as appropriate for your environment.</p>
+
+</div>
+</div>
+
+<p>The recommended way of using the LVL is setting it up as a new Android
+<em>library project</em>. A library project is a type of development project
+that holds shared Android source code and resources. Other Android application
+projects can reference the library project and, at build time, include its
+compiled sources in their <code>.apk</code> files. In the context of licensing, this means
+that you can do most of your licensing development once, in a library project,
+then include the library sources in your various application projects. In this
+way, you can easily maintain a uniform implementation of licensing across all of
+your projects and maintain it centrally. </p>
+
+<p>The LVL is provided as a configured library project &mdash; once you have
+downloaded it, you can start using it right away. </p>
+
+<p>If you are working in Eclipse with ADT, you need to add the LVL to your
+workspace as a new development project, in the same way as you would a new
+application project. </p>
+
+<ol>
+<li>Use the New Project Wizard to create a new
+project from existing sources. Select the LVL's <code>library</code> directory
+(the directory containing the library's AndroidManifest.xml file) as the project
+root.</li>
+<li>When you are creating the library project, you can select any application
+name, package, and set other fields as needed. </li>
+<li>For the library's build target, select Android 1.5 (API level 3) or higher.</li>
+</ol>
+
+<p> When created, the project is
+predefined as a library project in its <code>default.properties</code> file, so
+no further configuration is needed. </p>
+
+<p>For more information about how to create an application project or work with
+library projects in Eclipse, see <a
+href="{@docRoot}guide/developing/eclipse-adt.html#CreatingAProject">Developing
+in Eclipse with ADT</a>.</p>
+
+<h4>Copying the LVL sources to your application</h4>
+
+<p>As an alternative to adding the LVL as a library project, you can copy the
+library sources directly into your application. To do so, copy (or import) the
+directory
+<code>&lt;<em>sdk</em>&gt;/extras/marketlicensing/library/src/com</code> into
+your application's <code>src/</code> directory.</p>
+
+<p>If you add the LVL sources directly to your application, you can skip the
+next section and start working with the library, as described in <a
+href="#app-integration"></a>.</p>
+
+
+<h3 id="add-library">Including the LVL library project sources in your
+application</h3>
+
+<p>If you want to use the LVL sources as a library project, you need to add a
+reference to the LVL library project in your project properties. This tells
+build tools to include the LVL library project sources in your application at
+compile time. The process for adding a reference to a library project varies,
+based on your development environment, as described below.</p>
+
+<p> If you are developing in Eclipse with ADT, you should already have added the
+library project to your workspace, as described in the previous section. If you
+haven't done that already, do it now before continuing. </p>
+
+<p>Next, open the application's project properties window, as shown below.
+Select the "Android" properties group and click <strong>Add</strong>, then
+choose the LVL library project (com_android_vending_licensing) and click
+<strong>OK</strong>. For more information, see
+<a href="{@docRoot}guide/developing/eclipse-adt.html#libraryProject">Developing
+in Eclipse with ADT</a></p>
+
+<div style="margin-bottom:2em;">
+
+<img src="{@docRoot}images/licensing_add_library.png" style="text-align:left;margin-bottom:0;" />
+<div style="margin:0 2em;padding:0"><strong>Figure 5.</strong> If you are
+working in Eclipse with ADT, you can add the LVL library project to your
+application from the application's project properties.</div>
+</div>
+
+<p>If you are developing using the SDK command-line tools, navigate to the
+directory containing your application project and open the
+<code>default.properties</code> file. Add a line to the file that specifies the
+<code>android.library.reference.&lt;n&gt;</code> key and the path to the
+library. For example: </p>
+
+<pre>android.library.reference.1=path/to/library_project</pre>
+
+<p>Alternatively, you can use this command to update the project
+properties, including the reference to the library project:</p>
+
+<pre class="no-pretty-print" style="color:black">android update lib-project
+--target <em>&lt;target_ID&gt;</em> \
+--path <em>path/to/my/app_project</em> \
+--library <em>path/to/my/library_project</em>
+</pre>
+
+<p>For more information about working with library projects, see <a
+href="{@docRoot}guide/developing/eclipse-adt.html#libraryProject">Developing
+in Eclipse with ADT</a> or <a
+href="{@docRoot}guide/developing/other-ide.html#libraryProject">Developing in
+Other IDEs</a>, as appropriate for your environment.</p>
+
+
+<h2 id="app-integration">Integrating the LVL with Your Application</h2>
+
+<p>Once you've followed the steps above to set up a publisher account and
+development environment, you are ready to begin integrating the LVL with your
+application. </p>
+
+<p>Integrating the LVL with your application code involves these tasks:</p>
+
+<ol>
+<li><a href="#manifest-permission">Adding the licensing permission</a> your application's manifest.</li>
+<li><a href="#impl-Policy">Implementing a Policy</a> &mdash; you can choose one of the full implementations provided in the LVL or create your own.</li>
+<li><a href="#impl-Obfuscator">Implementing an Obfuscator</a>, if your Policy will cache any license response data. </li>
+<li><a href="#impl-lc">Adding code to check the license</a> in your application's main Activity</li>
+<li><a href="#impl-DeviceLimiter">Implementing a DeviceLimiter</a> (optional and not recommended for most applications)</li>
+</ol>
+
+<p>The sections below describe these tasks. When you are done with the
+integration, you should be able to compile your application successfully and you
+can begin testing, as described in <a href="#test-env">Setting Up the Test
+Environment</a>.</p>
+
+<p>For an overview of the full set of source files included in the LVL, see <a
+href="#lvl-summary">Summary of LVL Classes and Interfaces</a>.</p>
+
+
+<h3 id="manifest-permission">Adding the licensing permission to your
+AndroidManifest.xml</h3>
+
+<p>To use the Android Market application for sending a license check to the
+server, your application must request the proper permission,
+<code>com.android.vending.CHECK_LICENSE</code>. If your application does
+not declare the licensing permission but attempts to initiate a license check,
+the LVL throws a security exception.</p>
+
+<p>To request the licensing permission in your application, declare a <a
+href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><code>&lt;uses-permission&gt;</code></a>
+element as a child of <code>&lt;manifest&gt;</code>, as follows: </p>
+
+<p style="margin-left:2em;"><code>&lt;uses-permission
+android:name="com.android.vending.CHECK_LICENSE"&gt;</code></p>
+
+<p>For example, here's how the LVL sample application declares the permission:
+</p>
+
+<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
+
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android" ..."&gt;
+ ...
+ &lt;!-- Devices &gt;= 3 have version of Android Market that supports licensing. --&gt;
+ &lt;uses-sdk android:minSdkVersion="3" /&gt;
+ &lt;!-- Required permission to check licensing. --&gt;
+ &lt;uses-permission android:name="com.android.vending.CHECK_LICENSE" /&gt;
+ ...
+&lt;/manifest&gt;
+</pre>
+
+<p class="note"><strong>Note:</strong> Currently, you cannot declare the
+<code>CHECK_LICENSE</code> permission in the LVL's manifest, because the SDK
+Tools will not merge it into the manifests of dependent applications. Instead,
+you must declare the permission in the manifest of each dependent application.
+</p>
+
+
+<h3 id="impl-Policy">Implementing a Policy</h3>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>ServerManagedPolicy</h2>
+
+<p>The LVL includes a complete Policy implementation called ServerManagedPolicy
+that makes use of license-management settings provided by the Android Market
+server. </p>
+
+<p style="margin-top:.5em;">Use of ServerManagedPolicy as the basis for your
+Policy is strongly recommended. For more information, see <a
+href="#ServerManagedPolicy">ServerManagedPolicy</a> section, below.</p>
+
+</div>
+</div>
+
+<p>Android Market licensing service does not itself determine whether a
+given user with a given license should be granted access to your application.
+Rather, that responsibility is left to a Policy implementation that you provide
+in your application.</p>
+
+<p>Policy is an interface declared by the LVL that is designed to hold your
+application's logic for allowing or disallowing user access, based on the result
+of a license check. To use the LVL, your application <em>must</em> provide an
+implementation of Policy. </p>
+
+<p>The Policy interface declares two methods, <code>allowAccess()</code> and
+<code>processServerResponse()</code>, which are called by a LicenseChecker
+instance when processing a response from the license server. It also declares an
+enum called <code>LicenseResponse</code>, which specifies the license response
+value passed in calls to <code>processServerResponse()</code>. </p>
+
+<ul>
+<li><code>processServerResponse()</code> lets you preprocess the raw response
+data received from the licensing server, prior to determining whether to grant
+access.
+
+<p>A typical implementation would extract some or all fields from the license
+response and store the data locally to a persistent store, such as through
+{@link android.content.SharedPreferences} storage, to ensure that the data is
+accessible across application invocations and device power cycles. For example,
+a Policy would maintain the timestamp of last successful license check, the
+retry count, the license validity period, and similar information in a
+persistent store, rather than resetting the values each time the application is
+launched.</p>
+
+<p>When storing response data locally, the Policy must ensure that the data is
+obfuscated (see <a href="#impl-Obfuscator">Implementing an Obfuscator</a>,
+below).</p></li>
+
+<li><code>allowAccess()</code> determines whether to grant the user access to
+your application, based on any available license response data (from the
+licensing server or from cache) or other application-specific information. For
+example, your implementation of <code>allowAccess()</code> could take into
+account additional criteria, such as usage or other data retrieved from a
+backend server. In all cases, an implementation of <code>allowAccess()</code>
+should only return <code>true</code> if there user is licensed to use the
+application, as determined by the licensing server, or if there is a transient
+network or system problem that prevents the license check from completing. In
+such cases, your implementation can maintain a count of retry responses and
+provisionally allow access until the next license check is complete.</li>
+
+</ul>
+
+<p>To simplify the process of adding licensing to your application and to
+provide an illustration of how a Policy should be designed, the LVL includes
+two full Policy implementations that you can use without modification:</p>
+
+<ul>
+<li><a href="#ServerManagedPolicy">ServerManagedPolicy</a>, a flexible Policy
+that uses server-provided settings and cached responses to manage access across
+varied network conditions, and</li>
+<li><a href="#StrictPolicy">StrictPolicy</a>, which does not cache any response
+data and allows access <em>only</em> if the server returns a licensed
+response.</li>
+</ul>
+
+<p>For most applications, the use of ServerManagedPolicy is highly
+recommended. ServerManagedPolicy is the LVL default and is integrated with
+the LVL sample application.</p>
+
+
+<h4 id="custom-policies">Guidelines for custom policies</h4>
+
+<p>In your licensing implementation, you can use one of the complete policies
+provided in the LVL (ServerManagedPolicy or StrictPolicy) or you can create a
+custom Policy. For any type of custom policy, there are several important design
+points to understand and account for in your implementation.</p>
+
+<p>The licensing server applies general request limits to guard against overuse
+of resources that could result in denial of service. When an application exceeds
+the request limit, the licensing server returns a 503 response, which gets
+passed through to your application as a general server error. This means that no
+license response will be available to the user until the limit is reset, which
+can affect the user for an indefinite period.</p>
+
+<p>If you are designing a custom Policy, we recommend that the Policy:
+<ol>
+<!-- <li>Limits the number of points at which your app calls for a license check
+to the minimum. </li> -->
+<li>Caches (and properly obfuscates) the most recent successful license response
+in local persistent storage.</li>
+<li>Returns the cached response for all license checks, for as long as the
+cached response is valid, rather than making a request to the licensing server.
+Setting the response validity accrording to the server-provided <code>VT</code>
+extra is highly recommended. See <a href="#extras">Server Response Extras</a>
+for more information.</li>
+<li>Uses an exponential backoff period if retrying any requests the result in
+errors. However, because the Android Market client automatically retries failed
+requests, the Policy does not need to do so, in most cases.</li>
+<li>Provides for a "grace period" that allows the user to access your
+application for a limited time or number of uses, while a license check is being
+retried. The grace period benefits the user by allowing access until the next
+license check can be completed successfully and it benefits you by placing a
+hard limit on access to your application when there is no valid license response
+available.</li>
+</ol>
+
+<p>Designing your Policy according to the guidelines listed above is critical,
+because it ensures the best possible experience for users while giving you
+effective control over your application even in error conditions. </p>
+
+<p>Note that any Policy can use settings provided by the licensing server to
+help manage validity and caching, retry grace period, and more. Extracting the
+server-provided settings is straightforward and making use of them is highly
+recommended. See the ServerManagedPolicy implementation for an example of how to
+extract and use the extras. For a list of server settings and information about
+how to use them, see <a href="#extras">Server Response Extras</a> in the
+Appendix of this document.</p>
+
+<h4 id="ServerManagedPolicy">ServerManagedPolicy</h4>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>Server Response Extras</h2>
+
+<p>For certain types of licensing responses, the licensing server appends extra
+settings to the responses, to help the application manage licensing effectively.
+</p>
+
+<p style="margin-top:.5em;">See <a href="#extras">Server Response Extras</a> for
+a list of settings and <code>ServerManagedPolicy.java</code> for information
+about how a Policy can usethe extras.</p>
+
+</div>
+</div>
+
+<p>The LVL includes a full and recommended implementation of the Policy
+interface called ServerManagedPolicy. The implementation is integrated with the
+LVL classes and serves as the default Policy in the library. </p>
+
+<p>ServerManagedPolicy provides all of the handling for license and retry
+responses. It caches all of the response data locally in a
+{@link android.content.SharedPreferences} file, obfuscating it with the
+application's Obfuscator implementation. This ensures that the license response
+data is secure and persists across device power cycles. ServerManagedPolicy
+provides concrete implementations of the interface methods
+<code>processServerResponse()</code> and <code>allowAccess()</code> and also
+includes a set of supporting methods and types for managing license
+responses.</p>
+
+<p>Importantly, a key feature of ServerMangedPolicy is its use of
+server-provided settings as the basis for managing licensing across an
+application's refund period and through varying network and error conditions.
+When an application contacts the Android Market server for a license check, the
+server appends several settings as key-value pairs in the license response
+extras field. For example, the server provides recommended values for the
+application's license validity period, retry grace period, and maximum allowable
+retry count, among others. ServerManagedPolicy extracts the values from the
+license response in its <code>processServerResponse()</code> method and checks
+them in its <code>allowAccess()</code> method. For a list of the server-provided
+settings used by ServerManagedPolicy, see <a href="#extras">Server Response
+Extras</a> in the Appendix of this document.</p>
+
+<p>For convenience, best performance, and the benefit of using license settings
+from the Android Market server, <strong>using ServerManagedPolicy as your
+licensing Policy is strongly recommended</strong>. </p>
+
+<p>If you are concerned about the security of license response data that is
+stored locally in SharedPreferences, you can use a stronger obfuscation
+algorithm or design a stricter Policy that does not store license data. The LVL
+includes an example of such a Policy &mdash; see <a
+href="#StrictPolicy">StrictPolicy</a> for more information.</p>
+
+<p>To use ServerManagedPolicy, simply import it to your Activity, create an
+instance, and pass a reference to the instance when constructing your
+LicenseChecker. See <a href="#lc-lcc">Instantiate LicenseChecker and
+LicenseCheckerCallback</a> for more information. </p>
+
+<h4 id="StrictPolicy">StrictPolicy</h4>
+
+<p>The LVL includes an alternative full implementation of the Policy interface
+called StrictPolicy. The StrictPolicy implementation provides a more restrictive
+Policy than ServerManagedPolicy, in that it does not allow the user to access
+the application unless a license response is received from the server at the
+time of access that indicates that the user is licensed.</p>
+
+<p>The principal feature of StrictPolicy is that it does not store <em>any</em>
+license response data locally, in a persistent store. Because no data is stored,
+retry requests are not tracked and cached responses can not be used to fulfill
+license checks. The Policy allows access only if:</p>
+
+<ul>
+<li>The license response is received from the licensing server, and </li>
+<li>The license response indicates that the user is licensed to access the
+application. </li>
+</ul>
+
+<p>Using StrictPolicy is appropriate if your primary concern is to ensure that,
+in all possible cases, no user will be allowed to access the application unless
+the user is confirmed to be licensed at the time of use. Additionally, the
+Policy offers slightly more security than ServerManagedPolicy &mdash; since
+there is no data cached locally, there is no way a malicious user could tamper
+with the cached data and obtain access to the application.</p>
+
+<p>At the same time, this Policy presents a challenge for normal users, since it
+means that they won't be able to access the application when there is no network
+(cell or wi-fi) connection available. Another side-effect is that your
+application will send more license check requests to the server, since using a
+cached response is not possible. Depending on network conditions, this might
+prove challenging for users also.</p>
+
+<p>Overall, this policy represents a tradeoff of some degree of user convenience
+for absolute security and control over access. Consider the tradeoff carefully
+before using this Policy.</p>
+
+<p>To use StrictPolicy, simply import it to your Activity, create an instance,
+and pass a reference to it when constructing your LicenseChecker. See
+<a href="#lc-lcc">Instantiate LicenseChecker and LicenseCheckerCallback</a>
+for more information. </p>
+
+<h3 id="impl-Obfuscator">Implementing an Obfuscator</h3>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>AESObfuscator</h2>
+
+<p>The LVL includes a full Obfuscator implementation in the
+<code>AESObfuscator.java</code> file. The Obfuscator uses AES encryption to
+obfuscate/unobfuscate data. If you are using a Policy (such as
+ServerManagedPolicy) that caches license response data, using AESObfuscator as
+basis for your Obfuscator implementation is highly recommended. </p>
+
+</div>
+</div>
+
+<p>A typical Policy implementation needs to save the license response data for
+an application to a persistent store, so that it is accessible across
+application invocations and device power cycles. For example, a Policy would
+maintain the timestamp of the last successful license check, the retry count,
+the license validity period, and similar information in a persistent store,
+rather than resetting the values each time the application is launched. The
+default Policy included in the LVL, ServerManagedPolicy, stores license response
+data in a {@link android.content.SharedPreferences} instance, to ensure that the
+data is persistent. </p>
+
+<p>Because the Policy will use stored license response data to determine whether
+to allow or disallow access to the application, it <em>must</em> ensure that any
+stored data is secure and can not be reused or manipulated by a root user on a
+device. Specifically, the Policy must always obfuscate the data before storing
+it, using a key that is unique for the application and device. Obfuscating using
+a key that is both application-specific and device-specific is critical, because
+it prevents the obfuscated data from being shared among applications and
+devices.</p>
+
+<p>The LVL assists the application with storing its license response data in a
+secure, persistent manner. First, it provides an <code>Obfuscator</code>
+interface that lets your application supply the obfuscation algorithm of its
+choice for stored data. Building on that, the LVL provides the helper class
+<code>PreferenceObfuscator</code>, which handles most of the work of calling the
+application's Obfuscator class and reading and writing the obfuscated data in a
+SharedPreferences instance. </p>
+
+<p>The LVL provides a full Obfuscator implementation called
+<code>AESObfuscator</code> that uses AES encryption to obfuscate data. You can
+use <code>AESObfuscator</code> in your application without modification or you
+can adapt it to your needs. For more information, see the next section.</p>
+
+<p>Alternatively, you can write a custom Obfuscator based on your own code
+or use an obfuscator program such as ProGuard for additional security.</p>
+
+<h4 id="AESObfuscator">AESObfuscator</h4>
+
+<p>The LVL includes a full and recommended implementation of the Obfuscator
+interface called AESObfuscator. The implementation is integrated with the
+LVL sample application and serves as the default Obfuscator in the library. </p>
+
+<p>AESObfuscator provides secure obfuscation of data by using AES to
+encrypt and decrypt the data as it is written to or read from storage.
+The Obfuscator seeds the encryption using three data fields provided
+by the application: </p>
+
+<ol>
+<li>A salt &mdash; an array of random bytes to use for each (un)obfuscation. </li>
+<li>An application identifier string, typically the package name of the application.</li>
+<li>A device identifier string, derived from as many device-specific sources
+as possible, so as to make it as unique.</li>
+</ol>
+
+<p>To use AESObfuscator, first import it to your Activity. Declare a private
+static final array to hold the salt bytes and initialize it to 20 randomly
+generated bytes.</p>
+
+<pre> ...
+ // Generate 20 random bytes, and put them here.
+ private static final byte[] SALT = new byte[] {
+ -46, 65, 30, -128, -103, -57, 74, -64, 51, 88, -95,
+ -45, 77, -117, -36, -113, -11, 32, -64, 89
+ };
+ ...
+</pre>
+
+<p>Next, declare a variable to hold a device identifier and generate a value for
+it in any way needed. For example, the sample application included in the LVL
+queries the system settings for the
+<code>android.Settings.Secure.ANDROID_ID</code>, which is unique to each device.
+</p>
+
+<p>Note that, depending on the APIs you use to derive device-specific
+information, your application might need to request additional permissions in
+order to secure device-specific information. For example, if you query the
+TelephonyManager to obtain the device IMEI or related data, your application
+will also need to request the <code>android.permission.READ_PHONE_STATE</code>
+permission in its manifest. Before requesting permissions in this way, consider
+how doing so might affect your application or its filtering of your application
+on Android Market (since some permissions can cause the SDK build tools to add
+the associated <code>&lt;uses-feature&gt;</code>).</p>
+
+<p>Finally, construct an instance of AESObfuscator, passing the salt,
+application identifier, and device identifier. You can construct the instance
+directly, while constructing your Policy and LicenseChecker. For example:</p>
+
+<pre> ...
+ // Construct the LicenseChecker with a Policy.
+ mChecker = new LicenseChecker(
+ this, new ServerManagedPolicy(this,
+ new AESObfuscator(SALT, getPackageName(), deviceId)),
+ BASE64_PUBLIC_KEY // Your public licensing key.
+ );
+ ...
+</pre>
+
+<p>For a complete example, see MainActivity in the LVL sample application.</p>
+
+
+<h3 id="impl-lc">Checking the license from your application's main Activity</h3>
+
+<p>Once you've implemented a Policy for managing access to your application, the
+next step is to add a license check to your application, which initiates a query
+to the licensing server if needed and manages access to the application based on
+the license response. All of the work of adding the license check and handling
+the response takes place in your main {@link android.app.Activity} source file.
+</p>
+
+<p>To add the license check and handle the response, you must:</p>
+
+<ol>
+ <li><a href="#imports">Add imports</a></li>
+ <li><a href="#lc-impl">Implement LicenseCheckerCallback</a> as a private inner class</li>
+ <li><a href="#thread-handler">Create a Handler</a> for posting from LicenseCheckerCallback to the UI thread</li>
+ <li><a href="#lc-lcc">Instantiate LicenseChecker</a> and LicenseCheckerCallback</li>
+ <li><a href="#check-access">Call checkAccess()</a> to initiate the license check</li>
+ <li><a href="#account-key">Embed your public key</a> for licensing</li>
+ <li><a href="#handler-cleanup">Call your LicenseChecker's onDestroy() method</a> to close IPC connections.</li>
+</ol>
+
+<p>The sections below describe these tasks. </p>
+
+<h4 id="lc-overview">Overview of license check and response</h4>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>Example: MainActivity</h2>
+
+<p>The sample application included with the LVL provides a full example of how
+to initiate a license check and handle the result, in the
+<code>MainActivity.java</code> file.</p>
+
+</div>
+</div>
+
+<p>In most cases, you should add the license check to your application's main
+{@link android.app.Activity}, in the <code>onCreate()</code> method. This
+ensures that when the user launches your application directly, the license check
+will be invoked immediately. In some cases, you can add license checks in other
+locations as well. For example, if your application includes multiple Activity
+components that other applications can start by {@link android.content.Intent},
+you could add license checks in those Activities.</p>
+
+<p>A license check consists of two main actions: </p>
+
+<ul>
+<li>A call to a method to initiate the license check &mdash; in the LVL, this is
+a call to the <code>checkAccess()</code> method of a LicenseChecker object that
+you construct.</li>
+<li>A callback that returns the result of the license check. In the LVL, this is
+a <code>LicenseCheckerCallback</code> interface that you implement. The
+interface declares two methods, <code>allow()</code> and
+<code>dontAllow()</code>, which are invoked by the library based on to the
+result of the license check. You implement those two methods with whatever logic
+you need, to allow or disallow the user access to your application. Note that
+these methods do not determine <em>whether</em> to allow access &mdash; that
+determination is the responsibility of your Policy implementation. Rather, these
+methods simply provide the application behaviors for <em>how</em> to allow and
+disallow access (and handle application errors).</li>
+</ul>
+
+<div style="margin-bottom:2em;">
+
+<img src="{@docRoot}images/licensing_flow.png" style="text-align:left;margin-bottom:0;margin-left:3em;" />
+<div style="margin:.5em 0 1.5em 2em;padding:0"><strong>Figure 6.</strong> Overview of a
+typical license check interaction.</div>
+</div>
+
+<p>The diagram above illustrates how a typical license check takes place: </p>
+
+<ol>
+<li>Code in the application's main Activity instantiates LicenseCheckerCallback
+and LicenseChecker objects. When constructing LicenseChecker, the code passes in
+{@link android.content.Context}, a Policy implementation to use, and the
+publisher account's public key for licensing as parameters. </li>
+<li>The code then calls the <code>checkAccess()</code> method on the
+LicenseChecker object. The method implementation calls the Policy to determine
+whether there is a valid license response cached locally, in
+{@link android.content.SharedPreferences}.
+<ul>
+<li>If so, the <code>checkAccess()</code> implementation calls
+<code>allow()</code>.</li>
+<li>Otherwise, the LicenseChecker initiates a license check request that is sent
+to the licensing server.</li>
+</ul>
+</li>
+<li>When a response is received, LicenseChecker creates a LicenseValidator that
+verifies the signed license data and extracts the fields of the response, then
+passes them to your Policy for further evaluation.
+ <ul>
+ <li>If the license is valid, the Policy caches the response in
+SharedPreferences and notifies the validator, which then calls the
+<code>allow()</code> method on the LicenseCheckerCallback object. </li>
+ <li>If the license not valid, the Policy notifies the validator, which calls
+the <code>dontAllow()</code> method on LicenseCheckerCallback. </li>
+ </ul>
+</li>
+<li>In case of a recoverable local or server error, such as when the network is
+not available to send the request, LicenseChecker passes a RETRY response to
+your Policy's <code>processServerResponse()</code> method. </li>
+<li>In case of a application error, such as when the application attempts to
+check the license of an invalid package name, LicenseChecker passes an error
+response to the LicenseCheckerCallback's <code>applicationError()</code>
+method. </li>
+</ol>
+
+<p>Note that, in addition to initiating the license check and handling the
+result, which are described in the sections below, your application also needs
+to provide a <a href="#impl-Policy">Policy implementation</a> and, if the Policy
+stores response data (such as ServerManagedPolicy), an <a
+href="#impl-Obfuscator">Obfuscator</a> implementation. </p>
+
+
+<h4 id="imports">Add imports</h4>
+
+<p>First, open the class file of the application's main Activity and import
+LicenseChecker and LicenseCheckerCallback from the LVL package.</p>
+
+<pre> import com.android.vending.licensing.LicenseChecker;
+ import com.android.vending.licensing.LicenseCheckerCallback;</pre>
+
+<p>If you are using the default Policy implementation provided with the LVL,
+ServerManagedPolicy, import it also, together with the AESObfuscator. If you are
+using a custom Policy or Obfuscator, import those instead. </p>
+
+<pre> import com.android.vending.licensing.ServerManagedPolicy;
+ import com.android.vending.licensing.AESObfuscator;</pre>
+
+<h4 id="lc-impl">Implement LicenseCheckerCallback as a private inner class</h4>
+
+<p>LicenseCheckerCallback is an interface provided by the LVL for handling
+result of a license check. To support licensing using the LVL, you must
+implement LicenseCheckerCallback and
+its methods to allow or disallow access to the application.</p>
+
+<p>The result of a license check is always a call to one of the
+LicenseCheckerCallback methods, made based on the validation of the response
+payload, the server response code itself, and any additional processing provided
+by your Policy. Your application can implement the methods in any way needed. In
+general, it's best to keep the methods simple, limiting them to managing UI
+state and application access. If you want to add further processing of license
+responses, such as by contacting a backend server or applying custom constraints,
+you should consider incorporating that code into your Policy, rather than
+putting it in the LicenseCheckerCallback methods. </p>
+
+<p>In most cases, you should declare your implementation of
+LicenseCheckerCallback as a private class inside your application's main
+Activity class. </p>
+
+<p>Implement the <code>allow()</code> and <code>dontAllow()</code> methods as
+needed. To start with, you can use simple result-handling behaviors in the
+methods, such as displaying the license result in a dialog. This helps you get
+your application running sooner and can assist with debugging. Later, after you
+have determined the exact behaviors you want, you can add more complex handling.
+</p>
+
+<p>Some suggestions for handling unlicensed responses in
+<code>dontAllow()</code> include: </p>
+
+<ul>
+<li>Display a "Try again" dialog to the user, including a button to initiate a
+new license check. </li>
+<li>Display a "Purchase this application" dialog, including a button that
+deep-links the user to the application's details page on Market, from which the
+use can purchase the application. For more information on how to set up such
+links, see <a
+href="{@docRoot}guide/publishing/publishing.html#marketintent">Using Intents to
+Launch the Market Application on a Device</a>. </li>
+<li>Display a Toast notification that indicates that the features of the
+application are limited because it is not licensed. </li>
+</ul>
+
+<p>The example below shows how the LVL sample application implements
+LicenseCheckerCallback, with methods that display the license check result in a
+dialog. </p>
+
+<pre> private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
+ public void allow() {
+ if (isFinishing()) {
+ // Don't update UI if Activity is finishing.
+ return;
+ }
+ // Should allow user access.
+ displayResult(getString(R.string.allow));
+ }
+
+ public void dontAllow() {
+ if (isFinishing()) {
+ // Don't update UI if Activity is finishing.
+ return;
+ }
+ displayResult(getString(R.string.dont_allow));
+ // Should not allow access. An app can handle as needed,
+ // typically by informing the user that the app is not licensed
+ // and then shutting down the app or limiting the user to a
+ // restricted set of features.
+ // In this example, we show a dialog that takes the user to Market.
+ showDialog(0);
+ }
+ }
+</pre>
+
+<p>Additionally, you should implement the <code>applicationError()</code>
+method, which the LVL calls to let your application handle errors that are not
+retryable. For a list of such errors, see <a
+href="#server-response-codes">Server Response Codes</a> in the Appendix of this
+document. You can implement the method in any way needed. In most cases, the
+method should log the error code and call <code>dontAllow()</code>.</p>
+
+<h4 id="thread-handler">Create a Handler for posting from LicenseCheckerCallback
+to the UI thread</h4>
+
+<p>During a license check, the LVL passes the request to the Android Market
+application, which handles communication with the licensing server. The LVL
+passes the request over asynchronous IPC (using {@link android.os.Binder}) so
+the actual processing and network communication do not take place on a thread
+managed by your application. Similarly, when the Android Market application
+receives the result, it invokes a callback method over IPC, which in turn
+executes in an IPC thread pool in your application's process.</p>
+
+<p>The LicenseChecker class manages your application's IPC communication with
+the Android Market application, including the call that sends the request and
+the callback that receives the response. LicenseChecker also tracks open license
+requests and manages their timeouts. </p>
+
+<p>So that it can handle timeouts properly and also process incoming responses
+without affecting your application's UI thread, LicenseChecker spawns a
+background thread at instantiation. In the thread it does all processing of
+license check results, whether the result is a response received from the server
+or a timeout error. At the conclusion of processing, the LVL calls your
+LicenseCheckerCallback methods from the background thread. </p>
+
+<p>To your application, this means that:</p>
+
+<ol>
+<li>Your LicenseCheckerCallback methods will be invoked, in many cases, from a
+background thread.</li>
+<li>Those methods won't be able to update state or invoke any processing in the
+UI thread, unless you create a Handler in the UI thread and have your callback
+methods post to the Handler.</li>
+</ol>
+
+<p>If you want your LicenseCheckerCallback methods to update the UI thread,
+instantiate a {@link android.os.Handler} in the main Activity's
+{@link android.app.Activity#onCreate(android.os.Bundle) onCreate()} method,
+as shown below. In this example, the LVL sample application's
+LicenseCheckerCallback methods (see above) call <code>displayResult()</code> to
+update the UI thread through the Handler's
+{@link android.os.Handler#post(java.lang.Runnable) post()} method.</p>
+
+<pre>private Handler mHandler;
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ ...
+ mHandler = new Handler();
+ }
+</pre>
+
+<p>Then, in your LicenseCheckerCallback methods, you can use Handler methods to
+post Runnable or Message objects to the Handler. Here's how the sample
+application included in the LVL posts a Runnable to a Handler in the UI thread
+to display the license status.</p>
+
+<pre> private void displayResult(final String result) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ mStatusText.setText(result);
+ setProgressBarIndeterminateVisibility(false);
+ mCheckLicenseButton.setEnabled(true);
+ }
+ });
+ }
+</pre>
+
+<h4 id="lc-lcc">Instantiate LicenseChecker and LicenseCheckerCallback</h4>
+
+<p>In the main Activity's
+{@link android.app.Activity#onCreate(android.os.Bundle) onCreate()} method,
+create private instances of LicenseCheckerCallback and LicenseChecker. You must
+instantiate LicenseCheckerCallback first, because you need to pass a reference
+to that instance when you call the contructor for LicenseChecker. </p>
+
+<p>When you instantiate LicenseChecker, you need to pass in these parameters:</p>
+
+<ul>
+<li>The application {@link android.content.Context}</li>
+<li>A reference to the Policy implementation to use for the license check. In
+most cases, you would use the default Policy implementation provided by the LVL,
+ServerManagedPolicy. </li>
+<li>The String variable holding your publisher account's public key for
+licensing. </li>
+</ul>
+
+<p>If you are using ServerManagedPolicy, you won't need to access the class
+directly, so you can instantiate it directly in the LicenseChecker constructor,
+as shown in the example below. Note that you need to pass a reference to a new
+Obfuscator instance when you construct ServerManagedPolicy.</p>
+
+<p>The example below shows the instantiation of LicenseChecker and
+LicenseCheckerCallback from the <code>onCreate()</code> method of an Activity
+class. </p>
+
+<pre>public class MainActivity extends Activity {
+ ...
+ private LicenseCheckerCallback mLicenseCheckerCallback;
+ private LicenseChecker mChecker;
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ...
+ // Construct the LicenseCheckerCallback. The library calls this when done.
+ mLicenseCheckerCallback = new MyLicenseCheckerCallback();
+
+ // Construct the LicenseChecker with a Policy.
+ mChecker = new LicenseChecker(
+ this, new ServerManagedPolicy(this,
+ new AESObfuscator(SALT, getPackageName(), deviceId)),
+ BASE64_PUBLIC_KEY // Your public licensing key.
+ );
+ ...
+ }
+}
+</pre>
+
+
+<p>Note that LicenseChecker calls the LicenseCheckerCallback methods from the UI
+thread <em>only</em> if there is valid license response cached locally. If the
+license check is sent to the server, the callbacks always originate from the
+background thread, even for network errors. </p>
+
+
+<h4 id="check-access">Call checkAccess() to initiate the license check</h4>
+
+<p>In your main Activity, add a call to the <code>checkAccess()</code> method of the
+LicenseChecker instance. In the call, pass a reference to your
+LicenseCheckerCallback instance as a parameter. If you need to handle any
+special UI effects or state management before the call, you might find it useful
+to call <code>checkAccess()</code> from a wrapper method. For example, the LVL
+sample application calls <code>checkAccess()</code> from a
+<code>doCheck()</code> wrapper method:</p>
+
+<pre> &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ...
+ // Call a wrapper method that initiates the license check
+ doCheck();
+ ...
+ }
+ ...
+ private void doCheck() {
+ mCheckLicenseButton.setEnabled(false);
+ setProgressBarIndeterminateVisibility(true);
+ mStatusText.setText(R.string.checking_license);
+ mChecker.checkAccess(mLicenseCheckerCallback);
+ }
+</pre>
+
+
+<h4 id="account-key">Embed your public key for licensing</h4>
+
+<p>For each publisher account, the Android Market service automatically
+generates a 2048-bit RSA public/private key pair that is used exlusively for
+licensing. The key pair is uniquely associated with the publisher account and is
+shared across all applications that are published through the account. Although
+associated with a publisher account, the key pair is <em>not</em> the same as
+the key that you use to sign your applications (or derived from it).</p>
+
+<p>The Android Market publisher site exposes the public key for licensing to any
+developer signed in to the publisher account, but it keeps the private key
+hidden from all users in a secure location. When an application requests a
+license check for an application published in your account, the licensing server
+signs the license response using the private key of your account's key pair.
+When the LVL receives the response, it uses the public key provided by the
+application to verify the signature of the license response. </p>
+
+<p>To add licensing to an application, you must obtain your publisher account's
+public key for licensing and copy it into your application. Here's how to find
+your account's public key for licensing:</p>
+
+<ol>
+<li>Go to the Android Market <a
+href="http://market.android.com/publish">publisher site</a> and sign in.
+Make sure that you sign in to the account from which the application you are
+licensing is published (or will be published). </li>
+<li>In the account home page, locate the "Edit profile" link and click it. </li>
+<li>In the Edit Profile page, locate the "Licensing" pane, shown below. Your
+public key for licensing is given in the "Public key" text box. </p>
+</ol>
+
+<p>To add the public key to your application, simply copy/paste the key string
+from the text box into your application as the value of the String variable
+<code>BASE64_PUBLIC_KEY</code>. When you are copying, make sure that you have
+selected the entire key string, without omitting any characters. </p>
+
+<p>Here's an example from the LVL sample application:</p>
+
+<pre> public class MainActivity extends Activity {
+ private static final String BASE64_PUBLIC_KEY = "MIIBIjANBgkqhkiG ... "; //truncated for this example
+ ...
+ }
+</pre>
+
+<h4 id="handler-cleanup">Call your LicenseChecker's onDestroy() method
+to close IPC connections</h4>
+
+<p>Finally, to let the LVL clean up before your application
+{@link android.content.Context} changes, add a call to the LicenseChecker's
+<code>onDestroy()</code> method from your Activity's
+{@link android.app.Activity#onDestroy()} implementation. The call causes the
+LicenseChecker to properly close any open IPC connection to the Android Market
+application's ILicensingService and removes any local references to the service
+and handler.</p>
+
+<p>Failing to add the call the LicenseChecker's <code>onDestroy()</code> method
+can lead to problems over the lifecycle of your application. For example, if the
+user changes screen orientation while a license check is active, the application
+{@link android.content.Context} is destroyed. If your application does not
+properly close the LicenseChecker's IPC connection, your application will crash
+when the response is received. Similarly, if the user exits your application
+while a license check is in progress, your application will crash when the
+response is received, unless your application has properly called the
+LicenseChecker's <code>onDestroy()</code> method to disconnect from the service.
+</p>
+
+<p>Here's an example from the sample application included in the LVL, where
+<code>mChecker</code> is the LicenseChecker instance:</p>
+
+<pre> &#64;Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mChecker.onDestroy();
+ ...
+ }
+</pre>
+
+<p>If you are extending or modifying LicenseChecker, you might also need to call
+the LicenseChecker's <code>finishCheck()</code> method, to clean up any open IPC
+connections.</p>
+
+<h3 id="impl-DeviceLimiter">Implementing a DeviceLimiter</h3>
+
+<p>In some cases, you might want your Policy to limit the number of actual
+devices that are permitted to use a single license. This would prevent a user
+from moving a licensed application onto a number of devices and using the
+application on those devices under the same account ID. It would also prevent a
+user from "sharing" the application by providing the account information
+associated with the license to other individuals, who could then sign in to that
+account on their devices and access the license to the application. </p>
+
+<p>The LVL supports per-device licensing by providing a
+<code>DeviceLimiter</code> interface, which declares a single method,
+<code>allowDeviceAccess()</code>. When a LicenseValidator is handling a response
+from the licensing server, it calls <code>allowDeviceAccess()</code>, passing a
+user ID string extracted from the response.</p>
+
+<p>If you do not want to support device limitation, <strong>no work is
+required</strong> &mdash; the LicenseChecker class automatically uses a default
+implementation called NullDeviceLimiter. As the name suggests, NullDeviceLimiter
+is a "no-op" class whose <code>allowDeviceAccess()</code> method simply returns
+a LICENSED response for all users and devices. </p>
+
+<div style="border-left:4px solid #FFCF00;margin:1em;padding: 0 0 0 .5em">
+<p><strong>Caution:</strong> Per-device licensing is <em>not recommended for
+most applications</em> because:</p>
+<ul>
+<li>It requires that you provide a backend server to manage a users and devices
+mapping, and </li>
+<li>It could inadvertently result in a user being denied access to an
+application that they have legitimately purchased on another device.</li>
+</ul>
+</div>
+
+
+<h2 id="test-env">Setting Up the Testing Environment</h2>
+
+<p>The Android Market publisher site provides configuration tools that let you
+and others test licensing on your application before it is published. As you are
+implementing licensing, you can make use of the publisher site tools to test
+your application's Policy and handling of different licensing responses and
+error conditions.</p>
+
+<p>The main components of the test environment for licensing include: </p>
+
+<ul>
+<li>A "Test response" configuration in your publisher account that lets you
+control the licensing response returned, when the server processes a license
+check for an uploaded application from the publisher account or a test
+account.</li>
+<li>An optional set of test accounts that will receive the configured test
+response when checking the license of an application that you have uploaded
+(regardless whether the application is published or not).</li>
+<li>A runtime environment for the application that includes the Android Market
+application or Google APIs Add-On, on which the user is signed in to the
+publisher account or one of the test accounts.
+</ul>
+
+<p>Setting up the test environment properly involves:</p>
+
+<ol>
+<li><a href="#test-response">Setting static test responses</a> that are returned by the licensing server.</li>
+<li><a href="#test-acct-setup">Setting up test accounts</a> as needed.</li>
+<li><a href="#acct-signin">Signing in</a> properly to an emulator or device, before initiating a license check test.</li>
+</ol>
+
+<p>The sections below provide more information.</p>
+
+
+<h3 id="test-response">Setting test responses for license checks</h3>
+
+<p>Android Market provides a configuration setting in your publisher account
+that lets you override the normal processing of a license check for an
+application you have uploaded and return a specified response code. The setting
+is for testing only and applies <em>only</em> to license checks for applications
+that you have uploaded. For other users (users not signed in to test accounts),
+the server always processes license checks according to normal rules. </p>
+
+<p>To set a test response for your account, sign in to your publisher account
+and click "Edit Profile". In the Edit Profile page, locate the Test Response
+menu in the Licensing panel, shown below. You can select from the full set of
+valid server response codes to control the response or condition you want to
+test in your application.</p>
+
+<p>In general, you should make sure to test your application's licensing
+implementation with every response code available in the Test Response" menu.
+For a description of the codes, see <a href="#server-response-codes">Server
+Response Codes</a>, below.</p>
+
+<div style="margin-bottom:2em;" id="licensing_test_response">
+
+<img src="{@docRoot}images/licensing_test_response.png" style="text-align:left;margin-bottom:0;" />
+<div style="margin:0 2em;padding:0"><strong>Figure 7.</strong> The Licensing
+panel of your account's Edit Profile page, showing the Test Accounts field and the
+Test Response menu.</div>
+</div>
+
+<p>Note that the test response that you configure applies account-wide &mdash;
+that is, it applies not to a single application, but to <em>all</em>
+applications associated with the publisher account. If you are testing multiple
+applications at once, changing the test response will affect all of those
+applications on their next license check.</p>
+
+<p>Finally, before you can successfully send these test responses to your
+application, you must sign in to the device or emulator on which the application
+is installed, and from which it is querying the server. Specifically, you must
+sign using either your publisher account or one of the test accounts that you
+have set up. For more information about test accounts, see the next section.</p>
+
+<p>See <a href="#server-response-codes">Server Response Codes</a> for a list of
+test responses available and their meanings. </p>
+
+
+<h3 id="test-acct-setup">Setting up test accounts</h3>
+
+<p>In some cases, you might want to let multiple teams of developers test
+licensing on applications that will ultimately be published through your
+publisher account, but without giving them access to your publisher account's
+sign-in credentials. To meet that need, the Android Market publisher site lets
+you set up one or more optional <em>test accounts</em> &mdash; accounts that are
+authorized to query the licensing server and receive static test responses from
+your publisher account.</p>
+
+<p>Test accounts are standard Google accounts that you register on your
+publisher account, such that they will receive the test response for
+applications that you have uploaded. Developers can then sign in to their
+devices or emulators using the test account credentials and initiate license
+checks from installed applications. When the licensing server receives a license
+check from a user of a test account, it returns the static test response
+configured for the publisher account. </p>
+
+<p>Necessarily, there are limitations on the access and permissions given to
+users signed in through test accounts, including:</p>
+
+<ul>
+<li>Test account users can query the licensing server only for applications that
+are already uploaded to the publisher account. </li>
+<li>Test account users do not have permission to upload applications to your
+publisher account.</li>
+<li>Test account users do not have permission to set the publisher account's
+static test response.</li>
+</ul>
+
+<p>The table below summarizes the differences in capabilities, between the
+publisher account, a test account, and any other account.</p>
+
+<p class="table-caption" id="acct-types-table"><strong>Table 1.</strong>
+Differences in account types for testing licensing.</p>
+
+<table>
+<tr>
+<th>Account Type</th>
+<th>Can check license before upload?</th>
+<th>Can receive test response?</th>
+<th>Can set test response?</th>
+</tr>
+
+<tr>
+<td>Publisher account</td>
+<td>Yes</td>
+<td>Yes</td>
+<td>Yes</td>
+</tr>
+
+<tr>
+<td>Test account</td>
+<td>No</td>
+<td>Yes</td>
+<td>No</td>
+</tr>
+
+<tr>
+<td>Other</td>
+<td>No</td>
+<td>No</td>
+<td>No</td>
+</tr>
+</table>
+
+<h4 id="reg-test-acct">Registering test accounts on the publisher account</h4>
+
+<p>To get started, you need to register each test account in your publisher
+account. As shown in <a href="#licensing_test_response">Figure 7</a>, above, you
+register test accounts in the Licensing panel of your publisher account's Edit
+Profile page. Simply enter the accounts as a comma-delimited list and click
+<strong>Save</strong> to save your profile changes.</p>
+
+<p>You can use any Google account as a test account. If you want to own and
+control the test accounts, you can create the accounts yourself and distribute
+the credentials to your developers or testers.</p>
+
+<h4 id="test-app-upload">Handling application upload and distribution for test
+account users</h4>
+
+<p>As mentioned above, users of test accounts can only receive static test
+responses for applications that are uploaded to the publisher account. Since
+those users do not have permission to upload applications, as the publisher you
+will need to work with those users to collect apps for upload and distribute
+uploaded apps for testing. You can handle collection and distribution in any way
+that is convenient. </p>
+
+<p>Once an application is uploaded and becomes known to the licensing server,
+developers and testers can continue modify the application in their local
+development environment, without having to upload new versions. You only need to
+upload a new version if the local application increments the
+<code>versionCode</code> attribute in the manifest file. </p>
+
+<h4 id="test-key">Distributing your public key to test account users</h4>
+
+<p>The licensing server handles static test responses in the normal way,
+including signing the license response data, adding extras parameters, and so
+on. To support developers who are implementing licensing using test accounts,
+rather than having access to the publisher account, you will need to distribute
+your public key to them. Developers without access to the publisher site do not
+have access to your public key, and without the key they won't be able to
+verify license responses. </p>
+
+<p>Note that if you decide to generate a new licensing key pair for your account
+for some reason, you need to notify all users of test accounts. For
+testers, you can embed the new key in the application package and distribute it
+to users. For developers, you will need to distribute the new key to them
+directly. </p>
+
+
+<h3 id="acct-signin">Signing in to an authorized account in the runtime
+environment</h3>
+
+<p>The licensing service is designed to determine whether a given user is
+licensed to use a given application &mdash; during a license check, the Android
+Market application gathers the user ID from the primary account on the system
+and sends it to the server, together with the package name of the application
+and other information. However, if there is no user information available, the
+license check cannot succeed, so the Android Market application terminates the
+request and returns an error to the application. </p>
+
+<p>During testing, to ensure that your application can successfully query the
+licensing server, you must make sure that you sign in to an account <em>on the
+device or emulator</em> using:</p>
+
+<ul>
+<li>The credentials of a publisher account, or</li>
+<li>The credentials of a test account that is registered with a publisher
+account</li>
+</ul>
+
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>Signing in to a Google account on an emulator</h2>
+
+<p>If you are testing licensing on an emulator, you need to sign in to a Google
+account on the emulator. If you do not see an option to create a new Google
+account, the problem might be that your AVD is running a standard Android system
+image, rather than the Google APIs Add-On, API 8 (release 2) or higher. </p>
+
+<p style="margin-top:.5em;">For more information, see <a
+href="#runtime-setup">Setting up the runtime environment</a>, above.</p>
+
+</div>
+</div>
+
+<p>Signing in using a publisher account offers the advantage of letting your
+applications receive static test responses even before the applications are
+uploaded to the publisher site.</p>
+
+<p>If you are part of a larger organization or are working with external groups
+on applications that will be published through your site, you will more likely
+want to distribute test accounts instead, then use those to sign in during
+testing. </p>
+
+<p>To sign in on a device or emulator, follow the steps below. The preferred
+approach is to sign in as the primary account &mdash; however, if there are
+other accounts already in use on the device or emulator, you can create an
+additional account and sign in to it using the publisher or test account
+credentials. </p>
+
+<ol>
+<li>Open Settings &gt; Accounts &amp; sync</li>
+<li>Select <strong>Add Account</strong> and choose to add a "Google" account.
+</li>
+<li>Select <strong>Next</strong> and then <strong>Sign in</strong>.</li>
+<li>Enter the username and password of either the publisher account or a test
+account that is registered in the publisher account.</li>
+<li>Select <strong>Sign in</strong>. The system signs you in to the new
+account.</li>
+</ol>
+
+<p>Once you are signed in, you can begin testing licensing in your application
+(if you have completed the LVL integration steps above). When your application
+initiates a license check, it will receive a response containing the static test
+response configured on the publisher account. </p>
+
+<p>Note that, if you are using an emulator, you will need to sign in to the
+publisher account or test account each time you wipe data when restarting the
+emulator.</p>
+
+<div style="margin:2em 1em 1em 1em;">
+
+<img src="{@docRoot}images/licensing_device_signin.png" style="text-align:left;" />
+<div style="margin:.25em 1.25em;padding:0"><strong>Figure 8.</strong> Example of
+setting up a Google account on a device or emulator.</div>
+</div>
+
+<h2 id="app-obfuscation">Obfuscating Your Application</h2>
+
+<p>To ensure the security of your application, particularly for a paid
+application that uses licensing and/or custom constraints and protections, it's
+very important to obfuscate your application code. Properly obfuscating your
+code makes it more difficult for a malicious user to decompile the application's
+bytecode, modify it &mdash; such as by removing the license check &mdash;
+and then recompile it.</p>
+
+<p>Several obfuscator programs are available for Android applications, including
+<a href="http://proguard.sourceforge.net/">ProGuard</a>, which also offers
+code-optimization features. The use of ProGuard or a similar program to obfuscate
+your code is <em>strongly recommended</em> for all applications that use Android
+Market Licensing. </p>
+
+<h2 id="app-publishing">Publishing a Licensed Application</h2>
+
+<p>When you are finished testing your license implementation, you are ready to
+publish the application on Android Market. Follow the normal steps to <a
+href="{@docRoot}guide/publishing/preparing.html">prepare</a>, <a
+href="{@docRoot}guide/publishing/app-signing.html">sign</a>, and then <a
+href="{@docRoot}guide/publishing/publishing.html">publish the application</a>.
+</p>
+
+<h4>Removing Copy Protection</h4>
+
+<p>After uploading your licensed application, remember to remove copy protection
+from the application, if it is currently used. To check and remove copy
+protection, sign in to the publisher site and go the application's upload
+details page. In the Publishing options section, make sure that the Copy
+Protection radio button selection is "Off".</p>
+
+<h4>Considerations for Free Apps</h4>
+
+<p>Licensing is currently supported only for paid applications. If you already
+published your application as free, you won't be able to upload an updated
+version that includes licensing (that is, an application that uses the same
+package name and that includes the <a href="#manifest-permission">licensing
+permission</a>). Here are some points to keep in mind:</p>
+
+<ul>
+<li>If you want to offer a free version of your application that provides a
+reduced feature set (or that offers the full feature set for trial period), the
+free version of your application must not include the licensing permission and
+must use a different package name than the paid version of the app.</li>
+<li>If you want to offer a paid version of your free application that uses
+licensing, you can do so under a new package name.</li>
+</ul>
+
+<h2 id="support">Where to Get Support</h2>
+
+<p>If you have questions or encounter problems while implementing or deploying
+publishing in your applications, please use the support resources listed in the
+table below. By directing your queries to the correct forum, you can get the
+support you need more quickly. </p>
+
+<p class="table-caption"><strong>Table 2.</strong> Developer support resources
+for Android Market Licensing Service.</p>
+
+<table>
+
+<tr>
+<th>Support Type</th>
+<th>Resource</th>
+<th>Range of Topics</th>
+</tr>
+<tr>
+<td rowspan="2">Development and testing issues</td>
+<td>Google Groups: <a
+href="http://groups.google.com/group/android-developers">android-developers</a>
+</td>
+<td rowspan="2">LVL download and integration, library projects, Policy
+questions, user experience ideas, handling of responses, Obfuscator, IPC, test
+environment setup</td>
+</tr>
+<tr>
+<td>Stack Overflow: <a
+href="http://stackoverflow.com/questions/tagged/android">http://stackoverflow.com/questions/tagged/android</a></td>
+</tr>
+<tr>
+<td rowspan="2">Accounts, publishing, and deployment issues</td>
+<td><a href="http://www.google.com/support/forum/p/Android+Market">Android
+Market Help Forum</a></td>
+<td rowspan="2">Publisher accounts, licensing key pair, test accounts, server
+responses, test responses, application deployment and results</td>
+</tr>
+<tr>
+<td><a
+href="http://market.android.com/support/bin/answer.py?answer=186113">Market
+Licensing Support FAQ</a></td>
+</tr>
+<tr>
+<td>LVL issue tracker</td>
+<td><a href="http://code.google.com/p/marketlicensing/issues/">Marketlicensing
+project issue tracker</a></td>
+<td>Bug and issue reports related specifically to the LVL source code classes
+and interface implementations</td>
+</tr>
+
+</table>
+
+<p>For general information about how to post to the groups listed above, see <a
+href="{@docRoot}resources/community-groups.html">Developer Forums</a> document
+in the Resources tab.</p>
+
+<h2 id="lvl-summary">Summary of LVL Classes and Interfaces</h2>
+
+<p>The table below lists all of the source files in the License Verification
+Library (LVL) available through the Android SDK. All of the files are part of
+the <code>com.android.vending.licensing</code> package.</p>
+
+<p class="table-caption"><strong>Table A-1.</strong> Summary of LVL library
+classes and interfaces.</p>
+
+<div style="width:99%">
+<table width="100%">
+
+<tr>
+<th width="15%">Category</th>
+<th width="20%">Name</th>
+<th width="100%">Description</th>
+</tr>
+
+<tr>
+<td rowspan="2">License check and result</td>
+<td>LicenseChecker</td>
+<td>Class that you instantiate (or subclass) to initiate a license check.</td>
+</tr>
+<tr>
+<td><em>LicenseCheckerCallback</em></td>
+<td>Interface that you implement to handle result of the license check.</td>
+</tr>
+
+<tr>
+<td rowspan="3" width="15%">Policy</td>
+<td width="20%"><em>Policy</em></td>
+<td width="100%">Interface that you implement to determine whether to allow
+access to the application, based on the license response. </td>
+</tr>
+<tr>
+<td>ServerManagedPolicy</td>
+<td width="100%">Default Policy implementation. Uses settings provided by the
+licensing server to manage local storage of license data, license validity,
+retry.</td>
+</tr>
+<tr>
+<td>StrictPolicy</td>
+<td>Alternative Policy implementation. Enforces licensing based on a direct
+license response from the server only. No caching or request retry.</td>
+</tr>
+
+<tr>
+<td rowspan="2" width="15%">Data obfuscation <br><em>(optional)</em></td>
+<td width="20%"><em>Obfuscator</em></td>
+<td width="100%">Interface that you implement if you are using a Policy (such as
+ServerManagedPolicy) that caches license response data in a persistent store.
+Applies an obfuscation algorithm to encode and decode data being written or
+read.</td>
+</tr>
+<tr>
+<td>AESObfuscator</td>
+<td>Default Obfuscator implementation that uses AES encryption/decryption
+algorithm to obfuscate/unobfuscate data.</td>
+</tr>
+
+<tr>
+<td rowspan="2" width="15%">Device limitation<br><em>(optional)</em></td>
+<td width="20%"><em>DeviceLimiter</em></td>
+<td width="100%">Interface that you implement if you want to restrict use of an
+application to a specific device. Called from LicenseValidator. Implementing
+DeviceLimiter is not recommended for most applications because it requires a
+backend server and may cause the user to lose access to licensed applications,
+unless designed with care.</td>
+</tr>
+<tr>
+<td>NullDeviceLimiter</td>
+<td>Default DeviceLimiter implementation that is a no-op (allows access to all
+devices).</td>
+</tr>
+
+<tr>
+<td rowspan="6" width="15%">Library core, no integration needed</td>
+<td width="20%">ResponseData</td>
+<td width="100%">Class that holds the fields of a license response.</td>
+</tr>
+<tr>
+<td>LicenseValidator</td>
+<td>Class that decrypts and verifies a response received from the licensing
+server.</td>
+</tr>
+<tr>
+<td>ValidationException</td>
+<td>Class that indicates errors that occur when validating the integrity of data
+managed by an Obfuscator.</td>
+</tr>
+<tr>
+<td>PreferenceObfuscator</td>
+<td>Utility class that writes/reads obfuscated data to the system's
+{@link android.content.SharedPreferences} store.</td>
+</tr>
+<tr>
+<td><em>ILicensingService</em></td>
+<td>One-way IPC interface over which a license check request is passed to the
+Android Market client.</td>
+</tr>
+<tr>
+<td><em>ILicenseResultListener</em></td>
+<td>One-way IPC callback implementation over which the application receives an
+asynchronous response from the licensing server.</td>
+</tr>
+
+</table>
+</div>
+
+
+<h2 id="server-response-codes">Server Response Codes</h2>
+
+<p>The table below lists all of the license response codes supported by the
+licensing server. In general, an application should handle all of these response
+codes. By default, the LicenseValidator class in the LVL provides all of the
+necessary handling of these response codes for you. </p>
+
+<p class="table-caption"><strong>Table A-2.</strong> Summary of response codes
+returned by the Android Market server in a license response.</p>
+
+<table>
+
+<tr>
+<th>Response Code</th>
+<th>Description</th>
+<th>Signed?</th>
+<th>Extras</th>
+<th>Comments</th>
+</tr>
+<tr>
+<td>LICENSED</td>
+<td>The application is licensed to the user. The user has purchased the
+application or the application is free.</td>
+<td>Yes</td>
+<td><code>VT</code>,&nbsp;<code>GT</code>, <code>GR</code></td>
+<td><em>Allow access according to Policy constraints.</em></td>
+</tr>
+<tr>
+<td>LICENSED_OLD_KEY</td>
+<td>The application is licensed to the user, but there is an updated application
+version available that is signed with a different key. </td>
+<td>Yes </td>
+<td><code>VT</code>, <code>GT</code>, <code>GR</code>, <code>UT</code></td>
+<td><em>Optionally allow access according to Policy constraints.</em>
+<p style="margin-top:.5em;">Can indicate that the key pair used by the installed
+application version is invalid or compromised. The application can allow access
+if needed or inform the user that an upgrade is available and limit further use
+until upgrade.</p>
+</td>
+</tr>
+<tr>
+<td>NOT_LICENSED</td>
+<td>The application is not licensed to the user.</td>
+<td>No</td>
+<td></td>
+<td><em>Do not allow access.</em></td>
+</tr>
+<tr>
+<td>ERROR_CONTACTING_SERVER</td>
+<td>Local error &mdash; the Android Market application was not able to reach the
+licensing server, possibly because of network availability problems. </td>
+<td>No</td>
+<td></td>
+<td><em>Retry the license check according to Policy retry limits.</em></td>
+</tr>
+<tr>
+<td>ERROR_SERVER_FAILURE</td>
+<td>Server error &mdash; the server could not load the publisher account's key
+pair for licensing.</td>
+<td>No</td>
+<td></td>
+<td><em>Retry the license check according to Policy retry limits.</em>
+</td>
+</tr>
+<tr>
+<td>ERROR_INVALID_PACKAGE_NAME</td>
+<td>Local error &mdash; the application requested a license check for a package
+that is not installed on the device. </td>
+<td>No </td>
+<td></td>
+<td><em>Do not retry the license check.</em>
+<p style="margin-top:.5em;">Typically caused by a developement error.</p>
+</td>
+</tr>
+<tr>
+<td>ERROR_NON_MATCHING_UID</td>
+<td>Local error &mdash; the application requested a license check for a package
+whose UID (package, user ID pair) does not match that of the requesting
+application. </td>
+<td>No </td>
+<td></td>
+<td><em>Do not retry the license check.</em>
+<p style="margin-top:.5em;">Typically caused by a developement error.</p>
+</td>
+</tr>
+<tr>
+<td>ERROR_NOT_MARKET_MANAGED</td>
+<td>Server error &mdash; the application (package name) was not recognized by
+Android Market. </td>
+<td>No</td>
+<td></td>
+<td><em>Do not retry the license check.</em>
+<p style="margin-top:.5em;">Can indicate that the application was not published
+through Android Market or that there is an development error in the licensing
+implementation.</p>
+</td>
+</tr>
+
+</table>
+
+
+<h2 id="extras">Server Response Extras</h2>
+
+<p>The licensing server includes several settings in certain types of license
+responses, to assist the application and its Policy in managing access to the
+application across the 24-hour refund period and other conditions. Specifically,
+the server provides recommended values for the application's license validity
+period, retry grace period, maximum allowable retry count, and other settings.
+The server appends the settings as key-value pairs in the license response
+"extras" field. </p>
+
+<p>Any Policy implementation can extract the extras settings from the license
+response and use them as needed. The LVL default Policy implementation, <a
+href="#ServerManagedPolicy">ServerManagedPolicy</a>, serves as a working
+implementation and an illustration of how to obtain, store, and use the
+settings. </p>
+
+<p class="table-caption"><strong>Table A-3.</strong> Summary of
+license-management settings supplied by the Android Market server in a license
+response.</p>
+
+<table>
+<tr>
+<th>Extra</th><th>Description</th>
+</tr>
+
+<tr>
+ <td>VT</td>
+ <td>License validity timestamp. Specifies the date/time at which the current
+(cached) license response expires and must be rechecked on the licensing server.
+ </td>
+</tr>
+<tr>
+ <td>GT</td>
+ <td>Grace period timestamp. Specifies the end of the period during which a
+Policy may allow access to the application, even though the response status is
+RETRY. <p>The value is managed by the server, however a typical value would be 5
+or more days.</p></td>
+</tr>
+<tr>
+ <td>GR</td>
+ <td>Maximum retries count. Specifies how many consecutive RETRY license checks
+the Policy should allow, before denying the user access to the application.
+<p>The value is managed by the server, however a typical value would be "10" or
+higher.</p></td>
+</tr>
+<tr>
+ <td>UT</td>
+ <td>Update timestamp. Specifies the day/time when the most recent update to
+this application was uploaded and published. <p>The server returns this extra
+only for LICENSED_OLD_KEYS responses, to allow the Policy to determine how much
+time has elapsed since an update was published with new licensing keys before
+denying the user access to the application. </p></td>
+</tr>
+
+</table>
+
+<p>The sections below provide more information about the server-provided
+settings and how to use them. </p>
+
+<h4>License validity period</h4>
+
+<p>The Android Market licensing server sets a license validity period for all
+downloaded applications. The period expresses the interval of time over which an
+application's license status should be considered as unchanging and cacheable by
+a licensing Policy in the application. The licensing server includes the
+validity period in its response to all license checks, appending an
+end-of-validity timestamp to the response as an extra under the key "VT". A
+Policy can extract the VT key value and use it to conditionally allow access to
+the application without rechecking the license, until the validity period
+expires. </p>
+
+<p>The license validity signals to a licensing Policy when it must recheck the
+licensing status with the licensing server. It is <em>not</em> intended to imply
+whether an application is actually licensed for use. That is, when an
+application's license validity period expires, this does not mean that the
+application is no longer licensed for use &mdash; rather, it indicates only that
+the Policy must recheck the licensing status with the server. It follows that,
+as long as the license validity period is not expired, it is acceptable for the
+Policy to cache the initial license status locally and return the cached license
+status instead of sending a new license check to the server.</p>
+
+<p>The licensing server manages the validity period as a means of helping the
+application properly enforce licensing across the refund period offered by
+Android Market for paid applications. It sets the validity period based on
+whether the application was purchased and, if so, how long ago. Specifically,
+the server sets a validity period as follows:</p>
+
+<ul>
+<li>For a paid application, the server sets the initial license validity period
+so that the license reponse remains valid for as long as the application is
+refundable. A licensing Policy in the application may cache the
+result of the initial license check and does not need to recheck the license
+until the validity period has expired.</li>
+<li>When an application is no longer refundable, the server
+sets a longer validity period &mdash; typically a number of days. </li>
+<li>For a free application, the server sets the validity period to a very high
+value (<code>long.MAX_VALUE</code>). This ensures that, provided the Policy has
+cached the validity timestamp locally, it will not need to recheck the
+license status of the application in the future.</li>
+</ul>
+
+<p>The ServerManagedPolicy implementation uses the extracted timestamp
+(<code>mValidityTimestamp</code>) as a primary condition for determining whether
+to recheck the license status with the server before allowing the user access to
+the application. </p>
+
+<h4>Retry period and maximum retry count</h4>
+
+<p>In some cases, system or network conditions can prevent an application's
+license check from reaching the licensing server, or prevent the server's
+response from reaching the Android Market client application. For example, the
+user might launch an application when there is no cell network or data
+connection available &mdash; such as when on an airplane &mdash; or when the
+network connection is unstable or the cell signal is weak. </p>
+
+<p>When network problems prevent or interrupt a license check, the Android
+Market client notifies the application by returning a "RETRY" response code to
+the Policy's <code>processServerResponse()</code> method. In the case of system
+problems, such as when the application is unable to bind with Android Market's
+ILicensingService implementation, the LicenseChecker library itself calls the
+Policy <code>processServerResonse()</code> method with a "RETRY" response code.
+</p>
+
+<p>In general, the RETRY response code is a signal to the application that an
+error has occurred that has prevented a license check from completing.
+
+<p>The Android Market server helps an application to manage licensing under
+error conditions by setting a retry "grace period" and a recommended maximum
+retries count. The server includes these values in all license check responses,
+appending them as extras under the keys "GT" and "GR". </p>
+
+<p>The application Policy can extract the GT and GR extras and use them to
+conditionally allow access to the application, as follows:</p>
+
+<ul>
+<li>For a license check that results in a RETRY response, the Policy should
+cache the RETRY response code and increment a count of RETRY responses.</li>
+<li>The Policy should allow the user to access the application, provided that
+either the retry grace period is still active or the maximum retries count has
+not been reached.</li>
+</ul>
+
+<p>The ServerManagedPolicy uses the server-supplied GT and GR values as
+described above. The example below shows the conditional handling of the retry
+responses in the <code>allow()</code> method. The count of RETRY responses is
+maintained in the <code>processServerResponse()</code> method, not shown. </p>
+
+
+<pre> public boolean allowAccess() {
+ long ts = System.currentTimeMillis();
+ if (mLastResponse == LicenseResponse.LICENSED) {
+ // Check if the LICENSED response occurred within the validity timeout.
+ if (ts &lt;= mValidityTimestamp) {
+ // Cached LICENSED response is still valid.
+ return true;
+ }
+ } else if (mLastResponse == LicenseResponse.RETRY &amp;&amp;
+ ts &lt; mLastResponseTime + MILLIS_PER_MINUTE) {
+ // Only allow access if we are within the retry period or we haven't used up our
+ // max retries.
+ return (ts &lt;= mRetryUntil || mRetryCount &lt;= mMaxRetries);
+ }
+ return false;
+ }</pre>
+
diff --git a/docs/html/guide/publishing/preparing.jd b/docs/html/guide/publishing/preparing.jd
index c1c635185c46..442c12acc2ca 100644
--- a/docs/html/guide/publishing/preparing.jd
+++ b/docs/html/guide/publishing/preparing.jd
@@ -39,13 +39,14 @@ Applications</a> document. </p>
<ol>
<li>Test your application extensively on an actual device </li>
<li>Consider adding an End User License Agreement in your application</li>
+<li>Consider adding licensing support</li>
<li>Specify an icon and label in the application's manifest</li>
<li>Turn off logging and debugging and clean up data/files</li>
</ol>
<p>Before you do the final compile of your application:</p>
-<ol start="5">
+<ol start="6">
<li>Version your application</li>
<li>Obtain a suitable cryptographic key</li>
<li>Register for a Maps API Key, if your application is using MapView elements</li>
@@ -53,7 +54,7 @@ Applications</a> document. </p>
<p><em>Compile your application...</em></p>
<p>After compiling your application:</p>
-<ol start="8">
+<ol start="9">
<li>Sign your application</li>
<li>Test your compiled application</li>
</ol>
@@ -101,7 +102,19 @@ application</h3>
<p>To protect your person, organization, and intellectual property, you may want
to provide an End User License Agreement (EULA) with your application.
-<h3 id="iconlabel">3. Specify an icon and label in the application's manifest</h3>
+<h3 id="eula">3. Consider adding support for Android Market Licensing</h3>
+
+<p>If you are publishing a paid application through Android Market, consider
+adding support for Android Market Licensing. Licensing lets you control access
+to your application based on whether the current user has purchased it.
+Using Android Market Licensing is optional.
+
+<p>For complete information about Android Market Licensing Service and how to
+use it in your application, see <a
+href="{@docRoot}guide/publishing/licensing.html">Licensing Your
+Applications</a>.</p>
+
+<h3 id="iconlabel">4. Specify an icon and label in the application's manifest</h3>
<p>The icon and label that you specify in an application's manifest are
important because they are displayed to users as your application's icon and
@@ -116,7 +129,7 @@ display the icon and label to users. </p>
<p>As regards the design of your icon, you should try to make it match as much
as possible the style used by the built-in Android applications.</p>
-<h3 id="logging">4. Turn off logging and debugging and clean up data/files</h3>
+<h3 id="logging">5. Turn off logging and debugging and clean up data/files</h3>
<p>For release, you should make sure that debug facilities are turned off and
that debug and other unnecessary data/files are removed from your application
@@ -133,7 +146,7 @@ code.</li>
<h2 id="finalcompile">Before you do the final compile of your application</h2>
-<h3 id="versionapp">5. Version your application</h3>
+<h3 id="versionapp">6. Version your application</h3>
<p>Before you compile your application, you must make sure that you have defined
a version number for your application, specifying an appropriate value for both
@@ -152,7 +165,7 @@ element in the application's manifest file, using appropriate values. </p>
application, see <a href="{@docRoot}guide/publishing/versioning.html">Versioning
Your Applications</a>.</p>
-<h3 id="cryptokey">6. Obtain a suitable cryptographic key</h3>
+<h3 id="cryptokey">7. Obtain a suitable cryptographic key</h3>
<p>If you have read and followed all of the preparation steps up to this point,
your application is compiled and ready for signing. Inside the .apk, the
@@ -173,7 +186,7 @@ elements.</li>
<li>Sign your application for release, later in the preparation process</li>
</ul>
-<h3 id="mapsApiKey">7. Register for a Maps API Key, if your application is using
+<h3 id="mapsApiKey">8. Register for a Maps API Key, if your application is using
MapView elements</h3>
<div class="sidebox-wrapper">
@@ -231,7 +244,7 @@ you can compile your application for release.</p>
<h2 id="post-compile">After compiling your application</h2>
-<h3 id="signapp">8. Sign your application</h3>
+<h3 id="signapp">9. Sign your application</h3>
<p>Sign your application using your private key and then
align it with the {@code zipalign} tool. Signing your application
@@ -239,7 +252,7 @@ correctly is critically important. Please see
<a href="{@docRoot}guide/publishing/app-signing.html">Signing Your
Applications</a> for complete information. </p>
-<h3 id="testapp">9. Test your compiled and signed application</h3>
+<h3 id="testapp">10. Test your compiled and signed application</h3>
<p>Before you release your compiled application, you should thoroughly test it
on the target mobile device (and target network, if possible). In particular,
diff --git a/docs/html/guide/publishing/publishing.jd b/docs/html/guide/publishing/publishing.jd
index 0c087efa0120..9b470c89fbc6 100644
--- a/docs/html/guide/publishing/publishing.jd
+++ b/docs/html/guide/publishing/publishing.jd
@@ -18,7 +18,8 @@ page.title=Publishing Your Applications
<ol>
<li><a href="#overview">Publishing on Android Market</a>
<ol>
- <li><a href="#marketupgrade">Publishing Updates on Android Market</a>
+ <li><a href="#marketupgrade">Publishing Updates on Android Market</a></li>
+ <li><a href="#marketLicensing">Using Android Market Licensing Service</a></li>
<li><a href="#marketintent">Using Intents to Launch the Market Application</a></li>
</ol></li>
<!--
@@ -30,6 +31,7 @@ page.title=Publishing Your Applications
<h2>See also</h2>
<ol>
+<li><a href="{@docRoot}guide/publishing/licensing.html">Licensing Your Applications</a></li>
<li><a href="{@docRoot}guide/publishing/preparing.html">Preparing to Publish</a></li>
</ol>
@@ -59,7 +61,7 @@ the .apk. Your application is now ready for publishing. </p>
<p>The sections below provide information about publishing your Android
application to mobile device users.</p>
-<h2 id="market">Publishing on Android Market</h2>
+<h2 id="overview">Publishing on Android Market</h2>
<p>Android Market is a hosted service that makes it easy for users to find and
download Android applications to their Android-powered devices, and makes it
@@ -121,7 +123,26 @@ certificate do <em>not</em> match those of the existing version, Market will
consider it a new application and will not offer it to users as an update.</p>
+<h3 id="marketLicensing">Using Android Market Licensing Service</h3>
+<p>Android Market offers a licensing service that lets you enforce licensing
+policies for paid applications that you publish through Android Market. With
+Android Market Licensing, your applications can query Android Market at run time
+to obtain their licensing status for the current user, then allow or disallow
+further use as appropriate. Using the service, you can apply a flexible
+licensing policy on an application-by-application basis &mdash; each
+application can enforce its licensing status in the way most appropriate
+for it. </p>
+
+<p>Any application that you publish through Android Market can use the Android
+Market Licensing Service. The service uses no dedicated framework APIs, you can
+add licensing to any legacy application that uses a minimum API level of 3 or
+higher.</p>
+
+<p>For complete information about Android Market Licensing Service and how to
+use it in your application, see <a
+href="{@docRoot}guide/publishing/licensing.html">Licensing Your
+Applications</a>.</p>
<h3 id="marketintent">Using Intents to Launch the Market Application on
diff --git a/docs/html/images/licensing_add_library.png b/docs/html/images/licensing_add_library.png
new file mode 100644
index 000000000000..90b4435ab017
--- /dev/null
+++ b/docs/html/images/licensing_add_library.png
Binary files differ
diff --git a/docs/html/images/licensing_arch.png b/docs/html/images/licensing_arch.png
new file mode 100644
index 000000000000..ba7484a185e3
--- /dev/null
+++ b/docs/html/images/licensing_arch.png
Binary files differ
diff --git a/docs/html/images/licensing_device_signin.png b/docs/html/images/licensing_device_signin.png
new file mode 100644
index 000000000000..a4f5f8825690
--- /dev/null
+++ b/docs/html/images/licensing_device_signin.png
Binary files differ
diff --git a/docs/html/images/licensing_flow.png b/docs/html/images/licensing_flow.png
new file mode 100644
index 000000000000..b33119eae436
--- /dev/null
+++ b/docs/html/images/licensing_flow.png
Binary files differ
diff --git a/docs/html/images/licensing_gapis_8.png b/docs/html/images/licensing_gapis_8.png
new file mode 100644
index 000000000000..43ad26233261
--- /dev/null
+++ b/docs/html/images/licensing_gapis_8.png
Binary files differ
diff --git a/docs/html/images/licensing_package.png b/docs/html/images/licensing_package.png
new file mode 100644
index 000000000000..5da5632672b6
--- /dev/null
+++ b/docs/html/images/licensing_package.png
Binary files differ
diff --git a/docs/html/images/licensing_public_key.png b/docs/html/images/licensing_public_key.png
new file mode 100644
index 000000000000..163020999485
--- /dev/null
+++ b/docs/html/images/licensing_public_key.png
Binary files differ
diff --git a/docs/html/images/licensing_test_response.png b/docs/html/images/licensing_test_response.png
new file mode 100644
index 000000000000..ead21523ff12
--- /dev/null
+++ b/docs/html/images/licensing_test_response.png
Binary files differ
diff --git a/docs/html/resources/faq/commontasks.jd b/docs/html/resources/faq/commontasks.jd
index 2f09b0019fd9..9c32e9cdf609 100644
--- a/docs/html/resources/faq/commontasks.jd
+++ b/docs/html/resources/faq/commontasks.jd
@@ -158,7 +158,7 @@ It is not necessary to put external JARs in the assets folder.
<ul>
<li>Create an {@link android.app.Dialog app.Dialog} class </li>
<li>Create an {@link android.app.AlertDialog app.AlertDialog} class </li>
- <li>Set the {@link android.R.style#Theme_Dialog} <em>theme</em> attribute to <code>&#064;android:style/Theme.Dialog</code>
+ <li>Set the {@link android.R.style#Theme_Dialog} <em>theme</em> attribute to <code>&#64;android:style/Theme.Dialog</code>
in your AndroidManifest.xml file. For example:
<pre>&lt;activity class=&quot;AddRssItem&quot; android:label=&quot;Add an item&quot; android:theme=&quot;&#064;android:style/Theme.Dialog&quot;/&gt;</pre></li>
</ul>
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index fd0fc1f3067c..32c9a1d5a9b6 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -26,11 +26,12 @@
// ---------------------------------------------------------------------------
namespace android {
+class Flattenable;
class IBinder;
+class IPCThreadState;
class ProcessState;
class String8;
class TextOutput;
-class Flattenable;
struct flat_binder_object; // defined in support_p/binder_module.h
@@ -61,10 +62,13 @@ public:
// Parses the RPC header, returning true if the interface name
// in the header matches the expected interface from the caller.
- // If strict_policy_out is non-NULL, the RPC header's StrictMode policy
- // mask is returned.
+ //
+ // Additionally, enforceInterface does part of the work of
+ // propagating the StrictMode policy mask, populating the current
+ // IPCThreadState, which as an optimization may optionally be
+ // passed in.
bool enforceInterface(const String16& interface,
- int32_t* strict_policy_out = NULL) const;
+ IPCThreadState* threadState = NULL) const;
bool checkInterface(IBinder*) const;
void freeData();
diff --git a/include/gui/Sensor.h b/include/gui/Sensor.h
index e696d6386819..2de07b1325a9 100644
--- a/include/gui/Sensor.h
+++ b/include/gui/Sensor.h
@@ -63,6 +63,7 @@ public:
float getMaxValue() const;
float getResolution() const;
float getPowerUsage() const;
+ int32_t getMinDelay() const;
// Flattenable interface
virtual size_t getFlattenedSize() const;
@@ -81,6 +82,7 @@ private:
float mMaxValue;
float mResolution;
float mPower;
+ int32_t mMinDelay;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/SensorEventQueue.h b/include/gui/SensorEventQueue.h
index ad36dacea629..6581ae3092f6 100644
--- a/include/gui/SensorEventQueue.h
+++ b/include/gui/SensorEventQueue.h
@@ -65,7 +65,7 @@ public:
status_t setEventRate(Sensor const* sensor, nsecs_t ns) const;
// these are here only to support SensorManager.java
- status_t enableSensor(int32_t handle, int32_t ms) const;
+ status_t enableSensor(int32_t handle, int32_t us) const;
status_t disableSensor(int32_t handle) const;
private:
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index e9ff8a35e7df..c967efbcf71d 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -382,10 +382,10 @@ public:
* See EffectApi.h for details on effect command() function, valid command codes
* and formats.
*/
- virtual status_t command(int32_t cmdCode,
- int32_t cmdSize,
+ virtual status_t command(uint32_t cmdCode,
+ uint32_t cmdSize,
void *cmdData,
- int32_t *replySize,
+ uint32_t *replySize,
void *replyData);
@@ -429,10 +429,10 @@ private:
virtual void enableStatusChanged(bool enabled) {
mEffect->enableStatusChanged(enabled);
}
- virtual void commandExecuted(int cmdCode,
- int cmdSize,
+ virtual void commandExecuted(uint32_t cmdCode,
+ uint32_t cmdSize,
void *pCmdData,
- int replySize,
+ uint32_t replySize,
void *pReplyData) {
mEffect->commandExecuted(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
}
@@ -450,7 +450,11 @@ private:
// IEffectClient
void controlStatusChanged(bool controlGranted);
void enableStatusChanged(bool enabled);
- void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData);
+ void commandExecuted(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t replySize,
+ void *pReplyData);
void binderDied();
diff --git a/include/media/EffectApi.h b/include/media/EffectApi.h
index 8c120e53b468..16fb43cf750c 100644
--- a/include/media/EffectApi.h
+++ b/include/media/EffectApi.h
@@ -284,10 +284,10 @@ typedef int32_t (*effect_process_t)(effect_interface_t self,
//
////////////////////////////////////////////////////////////////////////////////
typedef int32_t (*effect_command_t)(effect_interface_t self,
- int32_t cmdCode,
- int32_t cmdSize,
+ uint32_t cmdCode,
+ uint32_t cmdSize,
void *pCmdData,
- int32_t *replySize,
+ uint32_t *replySize,
void *pReplyData);
diff --git a/include/media/EffectBassBoostApi.h b/include/media/EffectBassBoostApi.h
index b24a5f45abba..75f8d7868386 100644
--- a/include/media/EffectBassBoostApi.h
+++ b/include/media/EffectBassBoostApi.h
@@ -23,9 +23,10 @@
extern "C" {
#endif
-// TODO: include OpenSLES_IID.h instead
+#ifndef OPENSL_ES_H_
static const effect_uuid_t SL_IID_BASSBOOST_ = { 0x0634f220, 0xddd4, 0x11db, 0xa0fc, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
const effect_uuid_t * const SL_IID_BASSBOOST = &SL_IID_BASSBOOST_;
+#endif //OPENSL_ES_H_
/* enumerated parameter settings for BassBoost effect */
typedef enum
diff --git a/include/media/EffectEnvironmentalReverbApi.h b/include/media/EffectEnvironmentalReverbApi.h
index d490f71cd2cf..2233e3fbdf75 100644
--- a/include/media/EffectEnvironmentalReverbApi.h
+++ b/include/media/EffectEnvironmentalReverbApi.h
@@ -23,9 +23,10 @@
extern "C" {
#endif
-// TODO: include OpenSLES_IID.h instead
+#ifndef OPENSL_ES_H_
static const effect_uuid_t SL_IID_ENVIRONMENTALREVERB_ = { 0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, { 0x4e, 0x23, 0x4d, 0x6, 0x83, 0x9e } };
const effect_uuid_t * const SL_IID_ENVIRONMENTALREVERB = &SL_IID_ENVIRONMENTALREVERB_;
+#endif //OPENSL_ES_H_
/* enumerated parameter settings for environmental reverb effect */
typedef enum
@@ -45,20 +46,19 @@ typedef enum
REVERB_PARAM_BYPASS
} t_env_reverb_params;
-//t_reverb_properties is equal to SLEnvironmentalReverbSettings defined in OpenSL ES specification.
-typedef struct s_reverb_properties {
+//t_reverb_settings is equal to SLEnvironmentalReverbSettings defined in OpenSL ES specification.
+typedef struct s_reverb_settings {
int16_t roomLevel;
int16_t roomHFLevel;
int32_t decayTime;
int16_t decayHFRatio;
int16_t reflectionsLevel;
int32_t reflectionsDelay;
- int32_t reverbDelay;
int16_t reverbLevel;
+ int32_t reverbDelay;
int16_t diffusion;
int16_t density;
- int16_t padding;
-} t_reverb_properties;
+} __attribute__((packed)) t_reverb_settings;
#if __cplusplus
diff --git a/include/media/EffectEqualizerApi.h b/include/media/EffectEqualizerApi.h
index cb05b3206a6f..0492ea009a1b 100644
--- a/include/media/EffectEqualizerApi.h
+++ b/include/media/EffectEqualizerApi.h
@@ -19,8 +19,10 @@
#include <media/EffectApi.h>
-// for the definition of SL_IID_EQUALIZER
-#include "OpenSLES.h"
+#ifndef OPENSL_ES_H_
+static const effect_uuid_t SL_IID_EQUALIZER_ = { 0x0bed4300, 0xddd6, 0x11db, 0x8f34, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
+const effect_uuid_t * const SL_IID_EQUALIZER = &SL_IID_EQUALIZER_;
+#endif //OPENSL_ES_H_
#if __cplusplus
extern "C" {
@@ -37,9 +39,16 @@ typedef enum
EQ_PARAM_GET_BAND, // Gets the band that has the most effect on the given frequency.
EQ_PARAM_CUR_PRESET, // Gets/Sets the current preset.
EQ_PARAM_GET_NUM_OF_PRESETS, // Gets the total number of presets the equalizer supports.
- EQ_PARAM_GET_PRESET_NAME // Gets the preset name based on the index.
+ EQ_PARAM_GET_PRESET_NAME, // Gets the preset name based on the index.
+ EQ_PARAM_PROPERTIES // Gets/Sets all parameters at a time.
} t_equalizer_params;
+//t_equalizer_settings groups all current equalizer setting for backup and restore.
+typedef struct s_equalizer_settings {
+ uint16_t curPreset;
+ uint16_t numBands;
+ uint16_t bandLevels[];
+} t_equalizer_settings;
#if __cplusplus
} // extern "C"
diff --git a/include/media/EffectPresetReverbApi.h b/include/media/EffectPresetReverbApi.h
index 34ffffe3bbcb..53205bb751a8 100644
--- a/include/media/EffectPresetReverbApi.h
+++ b/include/media/EffectPresetReverbApi.h
@@ -23,10 +23,10 @@
extern "C" {
#endif
-// TODO: include OpenSLES_IID.h instead
-
+#ifndef OPENSL_ES_H_
static const effect_uuid_t SL_IID_PRESETREVERB_ = { 0x47382d60, 0xddd8, 0x11db, 0xbf3a, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
const effect_uuid_t * const SL_IID_PRESETREVERB = &SL_IID_PRESETREVERB_;
+#endif //OPENSL_ES_H_
/* enumerated parameter settings for preset reverb effect */
typedef enum
diff --git a/include/media/EffectVirtualizerApi.h b/include/media/EffectVirtualizerApi.h
index 601c384d54ac..c3d513161279 100644
--- a/include/media/EffectVirtualizerApi.h
+++ b/include/media/EffectVirtualizerApi.h
@@ -23,9 +23,10 @@
extern "C" {
#endif
-// TODO: include OpenSLES_IID.h instead
+#ifndef OPENSL_ES_H_
static const effect_uuid_t SL_IID_VIRTUALIZER_ = { 0x37cc2c00, 0xdddd, 0x11db, 0x8577, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
const effect_uuid_t * const SL_IID_VIRTUALIZER = &SL_IID_VIRTUALIZER_;
+#endif //OPENSL_ES_H_
/* enumerated parameter settings for virtualizer effect */
typedef enum
diff --git a/include/media/EffectVisualizerApi.h b/include/media/EffectVisualizerApi.h
index 1155db8139d1..bef1a4fc5ce2 100644
--- a/include/media/EffectVisualizerApi.h
+++ b/include/media/EffectVisualizerApi.h
@@ -23,10 +23,11 @@
extern "C" {
#endif
-//TODO replace by openSL ES include when available
+#ifndef OPENSL_ES_H_
static const effect_uuid_t SL_IID_VISUALIZATION_ =
{ 0xe46b26a0, 0xdddd, 0x11db, 0x8afd, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
const effect_uuid_t * const SL_IID_VISUALIZATION = &SL_IID_VISUALIZATION_;
+#endif //OPENSL_ES_H_
#define VISUALIZER_CAPTURE_SIZE_MAX 1024 // maximum capture size in samples
#define VISUALIZER_CAPTURE_SIZE_MIN 128 // minimum capture size in samples
diff --git a/include/media/IEffect.h b/include/media/IEffect.h
index 6dad393b35c0..ff04869e06be 100644
--- a/include/media/IEffect.h
+++ b/include/media/IEffect.h
@@ -33,7 +33,11 @@ public:
virtual status_t disable() = 0;
- virtual status_t command(int cmdCode, int cmdSize, void *pCmdData, int *pReplySize, void *pReplyData) = 0;
+ virtual status_t command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *pReplySize,
+ void *pReplyData) = 0;
virtual void disconnect() = 0;
diff --git a/include/media/IEffectClient.h b/include/media/IEffectClient.h
index d22daf81e7f8..2f78c98f172c 100644
--- a/include/media/IEffectClient.h
+++ b/include/media/IEffectClient.h
@@ -31,7 +31,11 @@ public:
virtual void controlStatusChanged(bool controlGranted) = 0;
virtual void enableStatusChanged(bool enabled) = 0;
- virtual void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData) = 0;
+ virtual void commandExecuted(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t replySize,
+ void *pReplyData) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index 633b5436d6b9..1eb178ebc130 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -273,7 +273,6 @@ public:
void setStatus(status_t status);
status_t reallocateAll();
status_t reallocateAllExcept(int buffer);
- status_t assertReallocate(int buffer);
int32_t getQueuedCount() const;
Region getDirtyRegion(int buffer) const;
@@ -356,13 +355,6 @@ private:
inline StatusUpdate(SharedBufferBase* sbb, status_t status);
inline ssize_t operator()();
};
-
- struct ReallocateCondition : public ConditionBase {
- int buf;
- inline ReallocateCondition(SharedBufferBase* sbb, int buf);
- inline bool operator()() const;
- inline const char* name() const { return "ReallocateCondition"; }
- };
};
// ===========================================================================
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index 5be17d3d359f..dab35b3cfa5d 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -60,6 +60,31 @@ namespace android {
class KeyLayoutMap;
/*
+ * A raw event as retrieved from the EventHub.
+ */
+struct RawEvent {
+ nsecs_t when;
+ int32_t deviceId;
+ int32_t type;
+ int32_t scanCode;
+ int32_t keyCode;
+ int32_t value;
+ uint32_t flags;
+};
+
+/* Describes an absolute axis. */
+struct RawAbsoluteAxisInfo {
+ bool valid; // true if the information is valid, false otherwise
+
+ int32_t minValue; // minimum value
+ int32_t maxValue; // maximum value
+ 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; }
+};
+
+/*
* Input device classes.
*/
enum {
@@ -82,7 +107,10 @@ enum {
INPUT_DEVICE_CLASS_DPAD = 0x00000020,
/* The input device is a gamepad (implies keyboard). */
- INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040
+ INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
+
+ /* The input device has switches. */
+ INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
};
/*
@@ -114,8 +142,8 @@ public:
virtual String8 getDeviceName(int32_t deviceId) const = 0;
- virtual int getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
- int* outMaxValue, int* outFlat, int* outFuzz) const = 0;
+ virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const = 0;
virtual status_t scancodeToKeycode(int32_t deviceId, int scancode,
int32_t* outKeycode, uint32_t* outFlags) const = 0;
@@ -131,26 +159,19 @@ public:
* If the device needs to remain awake longer than that, then the caller is responsible
* for taking care of it (say, by poking the power manager user activity timer).
*/
- virtual bool getEvent(int32_t* outDeviceId, int32_t* outType,
- int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
- int32_t* outValue, nsecs_t* outWhen) = 0;
+ virtual bool getEvent(RawEvent* outEvent) = 0;
/*
* Query current input state.
- * deviceId may be -1 to search for the device automatically, filtered by class.
- * deviceClasses may be -1 to ignore device class while searching.
*/
- virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t scanCode) const = 0;
- virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t keyCode) const = 0;
- virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
- int32_t sw) const = 0;
+ virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0;
+ virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
+ virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
/*
* Examine key input devices for specific framework keycode support
*/
- virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes,
+ virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
uint8_t* outFlags) const = 0;
};
@@ -165,33 +186,28 @@ public:
virtual String8 getDeviceName(int32_t deviceId) const;
- virtual int getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
- int* outMaxValue, int* outFlat, int* outFuzz) const;
-
+ virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const;
+
virtual status_t scancodeToKeycode(int32_t deviceId, int scancode,
int32_t* outKeycode, uint32_t* outFlags) const;
virtual void addExcludedDevice(const char* deviceName);
- virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t scanCode) const;
- virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t keyCode) const;
- virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
- int32_t sw) const;
+ virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
+ virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
+ virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const;
- virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const;
+ virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) const;
- virtual bool getEvent(int32_t* outDeviceId, int32_t* outType,
- int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
- int32_t* outValue, nsecs_t* outWhen);
+ virtual bool getEvent(RawEvent* outEvent);
protected:
virtual ~EventHub();
private:
bool openPlatformInput(void);
- int32_t convertDeviceKey_TI_P2(int code);
int open_device(const char *device);
int close_device(const char *device);
@@ -220,6 +236,8 @@ private:
int32_t getScanCodeStateLocked(device_t* device, int32_t scanCode) const;
int32_t getKeyCodeStateLocked(device_t* device, int32_t keyCode) const;
int32_t getSwitchStateLocked(device_t* device, int32_t sw) const;
+ bool markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) const;
// Protect all internal state.
mutable Mutex mLock;
diff --git a/include/ui/Input.h b/include/ui/Input.h
index d9b109197c8b..2385973e6926 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -23,7 +23,10 @@
#include <android/input.h>
#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
#include <utils/Timers.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
/*
* Additional private constants not defined in ndk/ui/input.h.
@@ -47,21 +50,16 @@ struct AInputEvent {
virtual ~AInputEvent() { }
};
-namespace android {
-
/*
- * A raw event as retrieved from the EventHub.
+ * Declare a concrete type for the NDK's input device forward declaration.
*/
-struct RawEvent {
- nsecs_t when;
- int32_t deviceId;
- int32_t type;
- int32_t scanCode;
- int32_t keyCode;
- int32_t value;
- uint32_t flags;
+struct AInputDevice {
+ virtual ~AInputDevice() { }
};
+
+namespace android {
+
/*
* Flags that flow alongside events in the input dispatch system to help with certain
* policy decisions such as waking from device sleep.
@@ -424,6 +422,69 @@ private:
MotionEvent mMotionEvent;
};
+/*
+ * Describes the characteristics and capabilities of an input device.
+ */
+class InputDeviceInfo {
+public:
+ InputDeviceInfo();
+ InputDeviceInfo(const InputDeviceInfo& other);
+ ~InputDeviceInfo();
+
+ struct MotionRange {
+ float min;
+ float max;
+ float flat;
+ float fuzz;
+ };
+
+ void initialize(int32_t id, const String8& name);
+
+ inline int32_t getId() const { return mId; }
+ inline const String8 getName() const { return mName; }
+ inline uint32_t getSources() const { return mSources; }
+
+ const MotionRange* getMotionRange(int32_t rangeType) 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);
+
+ inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
+ inline int32_t getKeyboardType() const { return mKeyboardType; }
+
+private:
+ int32_t mId;
+ String8 mName;
+ uint32_t mSources;
+ int32_t mKeyboardType;
+
+ KeyedVector<int32_t, MotionRange> mMotionRanges;
+};
+
+/*
+ * Provides remote access to information about an input device.
+ *
+ * Note: This is essentially a wrapper for Binder calls into the Window Manager Service.
+ */
+class InputDeviceProxy : public RefBase, public AInputDevice {
+protected:
+ InputDeviceProxy();
+ virtual ~InputDeviceProxy();
+
+public:
+ static void getDeviceIds(Vector<int32_t>& outIds);
+
+ static sp<InputDeviceProxy> getDevice(int32_t id);
+
+ inline const InputDeviceInfo* getInfo() { return & mInfo; }
+
+ // TODO add hasKeys, keymap, etc...
+
+private:
+ InputDeviceInfo mInfo;
+};
+
} // namespace android
diff --git a/include/ui/InputDevice.h b/include/ui/InputDevice.h
deleted file mode 100644
index 3b9c70e2dcea..000000000000
--- a/include/ui/InputDevice.h
+++ /dev/null
@@ -1,353 +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 _UI_INPUT_DEVICE_H
-#define _UI_INPUT_DEVICE_H
-
-#include <ui/EventHub.h>
-#include <ui/Input.h>
-#include <utils/KeyedVector.h>
-#include <utils/threads.h>
-#include <utils/Timers.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/BitSet.h>
-
-#include <stddef.h>
-#include <unistd.h>
-
-/* Maximum pointer id value supported.
- * (This is limited by our use of BitSet32 to track pointer assignments.) */
-#define MAX_POINTER_ID 31
-
-/* Maximum number of historical samples to average. */
-#define AVERAGING_HISTORY_SIZE 5
-
-
-namespace android {
-
-extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
-extern int32_t rotateKeyCode(int32_t keyCode, int32_t orientation);
-
-
-/*
- * An input device structure tracks the state of a single input device.
- *
- * This structure is only used by ReaderThread and is not intended to be shared with
- * DispatcherThread (because that would require locking). This works out fine because
- * DispatcherThread is only interested in cooked event data anyways and does not need
- * any of the low-level data from InputDevice.
- */
-struct InputDevice {
- struct AbsoluteAxisInfo {
- bool valid; // set to true if axis parameters are known, false otherwise
-
- int32_t minValue; // minimum value
- int32_t maxValue; // maximum value
- int32_t range; // range of values, equal to maxValue - minValue
- 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
- };
-
- struct VirtualKey {
- int32_t keyCode;
- int32_t scanCode;
- uint32_t flags;
-
- // computed hit box, specified in touch screen coords based on known display size
- int32_t hitLeft;
- int32_t hitTop;
- int32_t hitRight;
- int32_t hitBottom;
-
- inline bool isHit(int32_t x, int32_t y) const {
- return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
- }
- };
-
- struct KeyboardState {
- struct Current {
- int32_t metaState;
- nsecs_t downTime; // time of most recent key down
- } current;
-
- void reset();
- };
-
- struct TrackballState {
- struct Accumulator {
- enum {
- FIELD_BTN_MOUSE = 1,
- FIELD_REL_X = 2,
- FIELD_REL_Y = 4
- };
-
- uint32_t fields;
-
- bool btnMouse;
- int32_t relX;
- int32_t relY;
-
- inline void clear() {
- fields = 0;
- }
-
- inline bool isDirty() {
- return fields != 0;
- }
- } accumulator;
-
- struct Current {
- bool down;
- nsecs_t downTime;
- } current;
-
- struct Precalculated {
- float xScale;
- float yScale;
- float xPrecision;
- float yPrecision;
- } precalculated;
-
- void reset();
- };
-
- struct SingleTouchScreenState {
- struct Accumulator {
- enum {
- FIELD_BTN_TOUCH = 1,
- FIELD_ABS_X = 2,
- FIELD_ABS_Y = 4,
- FIELD_ABS_PRESSURE = 8,
- FIELD_ABS_TOOL_WIDTH = 16
- };
-
- uint32_t fields;
-
- bool btnTouch;
- int32_t absX;
- int32_t absY;
- int32_t absPressure;
- int32_t absToolWidth;
-
- inline void clear() {
- fields = 0;
- }
-
- inline bool isDirty() {
- return fields != 0;
- }
- } accumulator;
-
- struct Current {
- bool down;
- int32_t x;
- int32_t y;
- int32_t pressure;
- int32_t size;
- } current;
-
- void reset();
- };
-
- struct MultiTouchScreenState {
- struct Accumulator {
- enum {
- FIELD_ABS_MT_POSITION_X = 1,
- FIELD_ABS_MT_POSITION_Y = 2,
- FIELD_ABS_MT_TOUCH_MAJOR = 4,
- FIELD_ABS_MT_TOUCH_MINOR = 8,
- FIELD_ABS_MT_WIDTH_MAJOR = 16,
- FIELD_ABS_MT_WIDTH_MINOR = 32,
- FIELD_ABS_MT_ORIENTATION = 64,
- FIELD_ABS_MT_TRACKING_ID = 128
- };
-
- uint32_t pointerCount;
- struct Pointer {
- uint32_t fields;
-
- int32_t absMTPositionX;
- int32_t absMTPositionY;
- int32_t absMTTouchMajor;
- int32_t absMTTouchMinor;
- int32_t absMTWidthMajor;
- int32_t absMTWidthMinor;
- int32_t absMTOrientation;
- int32_t absMTTrackingId;
-
- inline void clear() {
- fields = 0;
- }
- } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks
-
- inline void clear() {
- pointerCount = 0;
- pointers[0].clear();
- }
-
- inline bool isDirty() {
- return pointerCount != 0;
- }
- } accumulator;
-
- void reset();
- };
-
- struct PointerData {
- uint32_t id;
- int32_t x;
- int32_t y;
- int32_t pressure;
- int32_t size;
- int32_t touchMajor;
- int32_t touchMinor;
- int32_t toolMajor;
- int32_t toolMinor;
- int32_t orientation;
- };
-
- struct TouchData {
- uint32_t pointerCount;
- PointerData pointers[MAX_POINTERS];
- BitSet32 idBits;
- uint32_t idToIndex[MAX_POINTER_ID + 1];
-
- void copyFrom(const TouchData& other);
-
- inline void clear() {
- pointerCount = 0;
- idBits.clear();
- }
- };
-
- // common state used for both single-touch and multi-touch screens after the initial
- // touch decoding has been performed
- struct TouchScreenState {
- Vector<VirtualKey> virtualKeys;
-
- struct Parameters {
- bool useBadTouchFilter;
- bool useJumpyTouchFilter;
- bool useAveragingTouchFilter;
-
- AbsoluteAxisInfo xAxis;
- AbsoluteAxisInfo yAxis;
- AbsoluteAxisInfo pressureAxis;
- AbsoluteAxisInfo sizeAxis;
- AbsoluteAxisInfo orientationAxis;
- } parameters;
-
- // The touch data of the current sample being processed.
- TouchData currentTouch;
-
- // The touch data of the previous sample that was processed. This is updated
- // incrementally while the current sample is being processed.
- TouchData lastTouch;
-
- // The time the primary pointer last went down.
- nsecs_t downTime;
-
- struct CurrentVirtualKeyState {
- enum Status {
- STATUS_UP,
- STATUS_DOWN,
- STATUS_CANCELED
- };
-
- Status status;
- nsecs_t downTime;
- int32_t keyCode;
- int32_t scanCode;
- } currentVirtualKey;
-
- struct AveragingTouchFilterState {
- // Individual history tracks are stored by pointer id
- uint32_t historyStart[MAX_POINTERS];
- uint32_t historyEnd[MAX_POINTERS];
- struct {
- struct {
- int32_t x;
- int32_t y;
- int32_t pressure;
- } pointers[MAX_POINTERS];
- } historyData[AVERAGING_HISTORY_SIZE];
- } averagingTouchFilter;
-
- struct JumpTouchFilterState {
- int32_t jumpyPointsDropped;
- } jumpyTouchFilter;
-
- struct Precalculated {
- int32_t xOrigin;
- float xScale;
-
- int32_t yOrigin;
- float yScale;
-
- int32_t pressureOrigin;
- float pressureScale;
-
- int32_t sizeOrigin;
- float sizeScale;
-
- float orientationScale;
- } precalculated;
-
- void reset();
-
- bool applyBadTouchFilter();
- bool applyJumpyTouchFilter();
- void applyAveragingTouchFilter();
- void calculatePointerIds();
-
- bool isPointInsideDisplay(int32_t x, int32_t y) const;
- const InputDevice::VirtualKey* findVirtualKeyHit() const;
- };
-
- InputDevice(int32_t id, uint32_t classes, String8 name);
-
- int32_t id;
- uint32_t classes;
- String8 name;
- bool ignored;
-
- KeyboardState keyboard;
- TrackballState trackball;
- TouchScreenState touchScreen;
- union {
- SingleTouchScreenState singleTouchScreen;
- MultiTouchScreenState multiTouchScreen;
- };
-
- void reset();
-
- inline bool isKeyboard() const { return classes & INPUT_DEVICE_CLASS_KEYBOARD; }
- inline bool isAlphaKey() const { return classes & INPUT_DEVICE_CLASS_ALPHAKEY; }
- inline bool isTrackball() const { return classes & INPUT_DEVICE_CLASS_TRACKBALL; }
- inline bool isDPad() const { return classes & INPUT_DEVICE_CLASS_DPAD; }
- inline bool isSingleTouchScreen() const { return (classes
- & (INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT))
- == INPUT_DEVICE_CLASS_TOUCHSCREEN; }
- inline bool isMultiTouchScreen() const { return classes
- & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT; }
- inline bool isTouchScreen() const { return classes
- & (INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT); }
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_DEVICE_H
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 674852aeb64d..d3495fee5dcb 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -55,6 +55,22 @@ enum {
INPUT_EVENT_INJECTION_TIMED_OUT = 3
};
+/*
+ * Constants used to determine the input event injection synchronization mode.
+ */
+enum {
+ /* Injection is asynchronous and is assumed always to be successful. */
+ INPUT_EVENT_INJECTION_SYNC_NONE = 0,
+
+ /* Waits for previous events to be dispatched so that the input dispatcher can determine
+ * whether input event injection willbe permitted based on the current input focus.
+ * Does not wait for the input event to finish processing. */
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1,
+
+ /* Waits for the input event to be completely processed. */
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2,
+};
+
/*
* An input target specifies how an input event is to be dispatched to a particular window
@@ -176,15 +192,14 @@ public:
float xPrecision, float yPrecision, nsecs_t downTime) = 0;
/* Injects an input event and optionally waits for sync.
- * This method may block even if sync is false because it must wait for previous events
- * to be dispatched before it can determine whether input event injection will be
- * permitted based on the current input focus.
+ * The synchronization mode determines whether the method blocks while waiting for
+ * input injection to proceed.
* Returns one of the INPUT_EVENT_INJECTION_XXX constants.
*
* This method may be called on any thread (usually by the input manager).
*/
virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
/* Preempts input dispatch in progress by making pending synchronous
* dispatches asynchronous instead. This method is generally called during a focus
@@ -241,7 +256,7 @@ public:
float xPrecision, float yPrecision, nsecs_t downTime);
virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
virtual void preemptInputDispatch();
@@ -267,11 +282,13 @@ private:
int32_t type;
nsecs_t eventTime;
- int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
- int32_t injectorPid; // -1 if not injected
- int32_t injectorUid; // -1 if not injected
+ int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
+ bool injectionIsAsync; // set to true if injection is not waiting for the result
+ int32_t injectorPid; // -1 if not injected
+ int32_t injectorUid; // -1 if not injected
bool dispatchInProgress; // initially false, set to true while dispatching
+ int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress
inline bool isInjected() { return injectorPid >= 0; }
};
@@ -340,6 +357,10 @@ private:
// headMotionSample will be initialized to tailMotionSample and tailMotionSample
// will be set to NULL.
MotionSample* tailMotionSample;
+
+ inline bool isSyncTarget() {
+ return targetFlags & InputTarget::FLAG_SYNC;
+ }
};
// A command entry captures state and behavior for an action to be performed in the
@@ -497,8 +518,7 @@ private:
// Since there can only ever be at most one such target at a time, if there is one,
// it must be at the tail because nothing else can be enqueued after it.
inline bool hasPendingSyncTarget() {
- return ! outboundQueue.isEmpty()
- && (outboundQueue.tail.prev->targetFlags & InputTarget::FLAG_SYNC);
+ return ! outboundQueue.isEmpty() && outboundQueue.tail.prev->isSyncTarget();
}
// Gets the time since the current event was originally obtained from the input driver.
@@ -559,11 +579,12 @@ private:
// Event injection and synchronization.
Condition mInjectionResultAvailableCondition;
- Condition mFullySynchronizedCondition;
- bool isFullySynchronizedLocked();
EventEntry* createEntryFromInputEventLocked(const InputEvent* event);
void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
+ Condition mInjectionSyncFinishedCondition;
+ void decrementPendingSyncDispatchesLocked(EventEntry* entry);
+
// Key repeat tracking.
// XXX Move this up to the input reader instead.
struct KeyRepeatState {
diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h
index e7552381162c..4012c69135f1 100644
--- a/include/ui/InputManager.h
+++ b/include/ui/InputManager.h
@@ -79,13 +79,12 @@ public:
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
/* Injects an input event and optionally waits for sync.
- * This method may block even if sync is false because it must wait for previous events
- * to be dispatched before it can determine whether input event injection will be
- * permitted based on the current input focus.
+ * The synchronization mode determines whether the method blocks while waiting for
+ * input injection to proceed.
* Returns one of the INPUT_EVENT_INJECTION_XXX constants.
*/
virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
/* Preempts input dispatch in progress by making pending synchronous
* dispatches asynchronous instead. This method is generally called during a focus
@@ -96,22 +95,28 @@ public:
virtual void preemptInputDispatch() = 0;
/* Gets input device configuration. */
- virtual void getInputConfiguration(InputConfiguration* outConfiguration) const = 0;
+ virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0;
- /*
- * Queries current input state.
- * deviceId may be -1 to search for the device automatically, filtered by class.
- * deviceClasses may be -1 to ignore device class while searching.
+ /* Gets information about the specified input device.
+ * Returns OK if the device information was obtained or NAME_NOT_FOUND if there
+ * was no such device.
*/
- virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t scanCode) const = 0;
- virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t keyCode) const = 0;
- virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
- int32_t sw) const = 0;
+ virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0;
+
+ /* Gets the list of all registered device ids. */
+ virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0;
+
+ /* Queries current input state. */
+ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode) = 0;
+ virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode) = 0;
+ virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+ int32_t sw) = 0;
/* Determines whether physical keys exist for the given framework-domain key codes. */
- virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const = 0;
+ virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
};
class InputManager : public InputManagerInterface {
@@ -136,18 +141,21 @@ public:
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
virtual void preemptInputDispatch();
- virtual void getInputConfiguration(InputConfiguration* outConfiguration) const;
- virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t scanCode) const;
- virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t keyCode) const;
- virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
- int32_t sw) const;
- virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const;
+ virtual void getInputConfiguration(InputConfiguration* outConfiguration);
+ virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo);
+ virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds);
+ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode);
+ virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode);
+ virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+ int32_t sw);
+ virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
private:
sp<InputReaderInterface> mReader;
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 14bea6504c9e..d7ec8ea64dfb 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -19,7 +19,6 @@
#include <ui/EventHub.h>
#include <ui/Input.h>
-#include <ui/InputDevice.h>
#include <ui/InputDispatcher.h>
#include <utils/KeyedVector.h>
#include <utils/threads.h>
@@ -33,6 +32,10 @@
namespace android {
+class InputDevice;
+class InputMapper;
+
+
/*
* Input reader policy interface.
*
@@ -68,14 +71,6 @@ public:
// The input dispatcher should perform special filtering in preparation for
// a pending app switch.
ACTION_APP_SWITCH_COMING = 0x00000002,
-
- // The input dispatcher should add POLICY_FLAG_WOKE_HERE to the policy flags it
- // passes through the dispatch pipeline.
- ACTION_WOKE_HERE = 0x00000004,
-
- // The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it
- // passes through the dispatch pipeline.
- ACTION_BRIGHT_HERE = 0x00000008,
};
/* Describes a virtual key. */
@@ -101,38 +96,30 @@ public:
/* Intercepts a key event.
* The policy can use this method as an opportunity to perform power management functions
- * and early event preprocessing.
+ * and early event preprocessing such as updating policy flags.
*
* Returns a policy action constant such as ACTION_DISPATCH.
*/
virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
- bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) = 0;
+ bool down, int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) = 0;
- /* Intercepts a trackball event.
+ /* Intercepts a switch event.
* The policy can use this method as an opportunity to perform power management functions
- * and early event preprocessing.
+ * and early event preprocessing such as updating policy flags.
*
- * Returns a policy action constant such as ACTION_DISPATCH.
+ * Switches are not dispatched to applications so this method should
+ * usually return ACTION_NONE.
*/
- virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
- bool rolled) = 0;
+ virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
+ uint32_t& policyFlags) = 0;
- /* Intercepts a touch event.
+ /* Intercepts a generic touch, trackball or other event.
* The policy can use this method as an opportunity to perform power management functions
- * and early event preprocessing.
+ * and early event preprocessing such as updating policy flags.
*
* Returns a policy action constant such as ACTION_DISPATCH.
*/
- virtual int32_t interceptTouch(nsecs_t when) = 0;
-
- /* Intercepts a switch event.
- * The policy can use this method as an opportunity to perform power management functions
- * and early event preprocessing.
- *
- * Switches are not dispatched to applications so this method should
- * usually return ACTION_NONE.
- */
- virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) = 0;
+ virtual int32_t interceptGeneric(nsecs_t when, uint32_t& policyFlags) = 0;
/* Determines whether to turn on some hacks we have to improve the touch interaction with a
* certain device whose screen currently is not all that good.
@@ -167,32 +154,52 @@ public:
*/
virtual void loopOnce() = 0;
- /* Gets the current virtual key. Returns false if not down.
+ /* Gets the current input device configuration.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual bool getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const = 0;
+ virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0;
- /* Gets the current input device configuration.
+ /* Gets information about the specified input device.
+ * Returns OK if the device information was obtained or NAME_NOT_FOUND if there
+ * was no such device.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual void getCurrentInputConfiguration(InputConfiguration* outConfiguration) const = 0;
+ virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0;
- /*
- * Query current input state.
- * deviceId may be -1 to search for the device automatically, filtered by class.
- * deviceClasses may be -1 to ignore device class while searching.
- */
- virtual int32_t getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t scanCode) const = 0;
- virtual int32_t getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t keyCode) const = 0;
- virtual int32_t getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses,
- int32_t sw) const = 0;
+ /* Gets the list of all registered device ids. */
+ virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0;
+
+ /* Query current input state. */
+ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode) = 0;
+ virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode) = 0;
+ virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+ int32_t sw) = 0;
/* Determine whether physical keys exist for the given framework-domain key codes. */
- virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const = 0;
+ virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
+};
+
+
+/* Internal interface used by individual input devices to access global input device state
+ * and parameters maintained by the input reader.
+ */
+class InputReaderContext {
+protected:
+ InputReaderContext() { }
+ virtual ~InputReaderContext() { }
+
+public:
+ virtual void updateGlobalMetaState() = 0;
+ virtual int32_t getGlobalMetaState() = 0;
+
+ virtual InputReaderPolicyInterface* getPolicy() = 0;
+ virtual InputDispatcherInterface* getDispatcher() = 0;
+ virtual EventHubInterface* getEventHub() = 0;
};
@@ -201,10 +208,11 @@ public:
* event filtering in low power states, are controlled by a separate policy object.
*
* IMPORTANT INVARIANT:
- * Because the policy can potentially block or cause re-entrance into the input reader,
- * the input reader never calls into the policy while holding its internal locks.
+ * Because the policy and dispatcher can potentially block or cause re-entrance into
+ * the input reader, the input reader never calls into other components while holding
+ * an exclusive internal lock.
*/
-class InputReader : public InputReaderInterface {
+class InputReader : public InputReaderInterface, private InputReaderContext {
public:
InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
@@ -213,107 +221,69 @@ public:
virtual void loopOnce();
- virtual bool getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const;
+ virtual void getInputConfiguration(InputConfiguration* outConfiguration);
- virtual void getCurrentInputConfiguration(InputConfiguration* outConfiguration) const;
+ virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo);
+ virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds);
- virtual int32_t getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t scanCode) const;
- virtual int32_t getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t keyCode) const;
- virtual int32_t getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses,
- int32_t sw) const;
+ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode);
+ virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode);
+ virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+ int32_t sw);
- virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const;
+ virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
private:
- // Lock that must be acquired while manipulating state that may be concurrently accessed
- // from other threads by input state query methods. It should be held for as short a
- // time as possible.
- //
- // Exported state:
- // - global virtual key code and scan code
- // - device list and immutable properties of devices such as id, name, and class
- // (but not other internal device state)
- mutable Mutex mExportedStateLock;
-
- // current virtual key information (lock mExportedStateLock)
- int32_t mExportedVirtualKeyCode;
- int32_t mExportedVirtualScanCode;
-
- // current input configuration (lock mExportedStateLock)
- InputConfiguration mExportedInputConfiguration;
-
- // combined key meta state
- int32_t mGlobalMetaState;
-
sp<EventHubInterface> mEventHub;
sp<InputReaderPolicyInterface> mPolicy;
sp<InputDispatcherInterface> mDispatcher;
+ virtual InputReaderPolicyInterface* getPolicy() { return mPolicy.get(); }
+ virtual InputDispatcherInterface* getDispatcher() { return mDispatcher.get(); }
+ virtual EventHubInterface* getEventHub() { return mEventHub.get(); }
+
+ // This reader/writer lock guards the list of input devices.
+ // The writer lock must be held whenever the list of input devices is modified
+ // and then promptly released.
+ // The reader lock must be held whenever the list of input devices is traversed or an
+ // input device in the list is accessed.
+ // This lock only protects the registry and prevents inadvertent deletion of device objects
+ // that are in use. Individual devices are responsible for guarding their own internal state
+ // as needed for concurrent operation.
+ RWLock mDeviceRegistryLock;
KeyedVector<int32_t, InputDevice*> mDevices;
- // display properties needed to translate touch screen coordinates into display coordinates
- int32_t mDisplayOrientation;
- int32_t mDisplayWidth;
- int32_t mDisplayHeight;
-
- // low-level input event decoding
+ // low-level input event decoding and device management
void process(const RawEvent* rawEvent);
- void handleDeviceAdded(const RawEvent* rawEvent);
- void handleDeviceRemoved(const RawEvent* rawEvent);
- void handleSync(const RawEvent* rawEvent);
- void handleKey(const RawEvent* rawEvent);
- void handleRelativeMotion(const RawEvent* rawEvent);
- void handleAbsoluteMotion(const RawEvent* rawEvent);
- void handleSwitch(const RawEvent* rawEvent);
-
- // input policy processing and dispatch
- void onKey(nsecs_t when, InputDevice* device, bool down,
- int32_t keyCode, int32_t scanCode, uint32_t policyFlags);
- void onSwitch(nsecs_t when, InputDevice* device, int32_t switchCode, int32_t switchValue);
- void onSingleTouchScreenStateChanged(nsecs_t when, InputDevice* device);
- void onMultiTouchScreenStateChanged(nsecs_t when, InputDevice* device);
- void onTouchScreenChanged(nsecs_t when, InputDevice* device, bool havePointerIds);
- void onTrackballStateChanged(nsecs_t when, InputDevice* device);
- void onConfigurationChanged(nsecs_t when);
-
- bool applyStandardInputDispatchPolicyActions(nsecs_t when,
- int32_t policyActions, uint32_t* policyFlags);
-
- bool consumeVirtualKeyTouches(nsecs_t when, InputDevice* device, uint32_t policyFlags);
- void dispatchVirtualKey(nsecs_t when, InputDevice* device, uint32_t policyFlags,
- int32_t keyEventAction, int32_t keyEventFlags);
- void dispatchTouches(nsecs_t when, InputDevice* device, uint32_t policyFlags);
- void dispatchTouch(nsecs_t when, InputDevice* device, uint32_t policyFlags,
- InputDevice::TouchData* touch, BitSet32 idBits, uint32_t changedId,
- int32_t motionEventAction);
-
- // display
- void resetDisplayProperties();
- bool refreshDisplayProperties();
-
- // device management
- InputDevice* getDevice(int32_t deviceId);
- InputDevice* getNonIgnoredDevice(int32_t deviceId);
+
void addDevice(nsecs_t when, int32_t deviceId);
- void removeDevice(nsecs_t when, InputDevice* device);
- void configureDevice(InputDevice* device);
- void configureDeviceForCurrentDisplaySize(InputDevice* device);
- void configureVirtualKeys(InputDevice* device);
- void configureAbsoluteAxisInfo(InputDevice* device, int axis, const char* name,
- InputDevice::AbsoluteAxisInfo* out);
+ void removeDevice(nsecs_t when, int32_t deviceId);
+ InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes);
void configureExcludedDevices();
- // global meta state management for all devices
- void resetGlobalMetaState();
- int32_t globalMetaState();
+ void consumeEvent(const RawEvent* rawEvent);
+
+ void handleConfigurationChanged(nsecs_t when);
- // virtual key management
- void updateExportedVirtualKeyState();
+ // state management for all devices
+ Mutex mStateLock;
- // input configuration management
- void updateExportedInputConfiguration();
+ int32_t mGlobalMetaState;
+ virtual void updateGlobalMetaState();
+ virtual int32_t getGlobalMetaState();
+
+ InputConfiguration mInputConfiguration;
+ void updateInputConfiguration();
+
+ // state queries
+ typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
+ int32_t getState(int32_t deviceId, uint32_t sourceMask, int32_t code,
+ GetStateFunc getStateFunc);
+ bool markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags);
};
@@ -329,6 +299,527 @@ private:
virtual bool threadLoop();
};
+
+/* Represents the state of a single input device. */
+class InputDevice {
+public:
+ InputDevice(InputReaderContext* context, int32_t id, const String8& name);
+ ~InputDevice();
+
+ inline InputReaderContext* getContext() { return mContext; }
+ inline int32_t getId() { return mId; }
+ inline const String8& getName() { return mName; }
+ inline uint32_t getSources() { return mSources; }
+
+ inline bool isIgnored() { return mMappers.isEmpty(); }
+
+ void addMapper(InputMapper* mapper);
+ void configure();
+ void reset();
+ void process(const RawEvent* rawEvent);
+
+ void getDeviceInfo(InputDeviceInfo* outDeviceInfo);
+ int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+ int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+ int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+ bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags);
+
+ int32_t getMetaState();
+
+private:
+ InputReaderContext* mContext;
+ int32_t mId;
+
+ Vector<InputMapper*> mMappers;
+
+ String8 mName;
+ uint32_t mSources;
+
+ typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
+ int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
+};
+
+
+/* An input mapper transforms raw input events into cooked event data.
+ * A single input device can have multiple associated input mappers in order to interpret
+ * different classes of events.
+ */
+class InputMapper {
+public:
+ InputMapper(InputDevice* device);
+ virtual ~InputMapper();
+
+ inline InputDevice* getDevice() { return mDevice; }
+ inline int32_t getDeviceId() { return mDevice->getId(); }
+ inline const String8 getDeviceName() { return mDevice->getName(); }
+ inline InputReaderContext* getContext() { return mContext; }
+ inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); }
+ inline InputDispatcherInterface* getDispatcher() { return mContext->getDispatcher(); }
+ inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
+
+ virtual uint32_t getSources() = 0;
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void configure();
+ virtual void reset();
+ virtual void process(const RawEvent* rawEvent) = 0;
+
+ virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+ virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+ virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags);
+
+ virtual int32_t getMetaState();
+
+protected:
+ InputDevice* mDevice;
+ InputReaderContext* mContext;
+
+ bool applyStandardPolicyActions(nsecs_t when, int32_t policyActions);
+};
+
+
+class SwitchInputMapper : public InputMapper {
+public:
+ SwitchInputMapper(InputDevice* device);
+ virtual ~SwitchInputMapper();
+
+ virtual uint32_t getSources();
+ virtual void process(const RawEvent* rawEvent);
+
+ virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+
+private:
+ void processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue);
+};
+
+
+class KeyboardInputMapper : public InputMapper {
+public:
+ KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId, uint32_t sources,
+ int32_t keyboardType);
+ virtual ~KeyboardInputMapper();
+
+ virtual uint32_t getSources();
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void reset();
+ virtual void process(const RawEvent* rawEvent);
+
+ virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+ virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags);
+
+ virtual int32_t getMetaState();
+
+private:
+ struct KeyDown {
+ int32_t keyCode;
+ int32_t scanCode;
+ };
+
+ int32_t mAssociatedDisplayId;
+ uint32_t mSources;
+ int32_t mKeyboardType;
+
+ Vector<KeyDown> mKeyDowns; // keys that are down
+ int32_t mMetaState;
+ nsecs_t mDownTime; // time of most recent key down
+
+ void initialize();
+
+ bool isKeyboardOrGamepadKey(int32_t scanCode);
+ void processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode,
+ uint32_t policyFlags);
+
+ ssize_t findKeyDown(int32_t scanCode);
+};
+
+
+class TrackballInputMapper : public InputMapper {
+public:
+ TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId);
+ virtual ~TrackballInputMapper();
+
+ virtual uint32_t getSources();
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void reset();
+ virtual void process(const RawEvent* rawEvent);
+
+private:
+ // Amount that trackball needs to move in order to generate a key event.
+ static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
+
+ int32_t mAssociatedDisplayId;
+
+ struct Accumulator {
+ enum {
+ FIELD_BTN_MOUSE = 1,
+ FIELD_REL_X = 2,
+ FIELD_REL_Y = 4
+ };
+
+ uint32_t fields;
+
+ bool btnMouse;
+ int32_t relX;
+ int32_t relY;
+
+ inline void clear() {
+ fields = 0;
+ }
+
+ inline bool isDirty() {
+ return fields != 0;
+ }
+ } mAccumulator;
+
+ bool mDown;
+ nsecs_t mDownTime;
+
+ float mXScale;
+ float mYScale;
+ float mXPrecision;
+ float mYPrecision;
+
+ void initialize();
+
+ void sync(nsecs_t when);
+};
+
+
+class TouchInputMapper : public InputMapper {
+public:
+ TouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+ virtual ~TouchInputMapper();
+
+ virtual uint32_t getSources();
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void configure();
+ virtual void reset();
+
+ virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+ virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags);
+
+protected:
+ /* Maximum pointer id value supported.
+ * (This is limited by our use of BitSet32 to track pointer assignments.) */
+ static const uint32_t MAX_POINTER_ID = 31;
+
+ struct VirtualKey {
+ int32_t keyCode;
+ int32_t scanCode;
+ uint32_t flags;
+
+ // computed hit box, specified in touch screen coords based on known display size
+ int32_t hitLeft;
+ int32_t hitTop;
+ int32_t hitRight;
+ int32_t hitBottom;
+
+ inline bool isHit(int32_t x, int32_t y) const {
+ return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
+ }
+ };
+
+ struct PointerData {
+ uint32_t id;
+ int32_t x;
+ int32_t y;
+ int32_t pressure;
+ int32_t size;
+ int32_t touchMajor;
+ int32_t touchMinor;
+ int32_t toolMajor;
+ int32_t toolMinor;
+ int32_t orientation;
+ };
+
+ struct TouchData {
+ uint32_t pointerCount;
+ PointerData pointers[MAX_POINTERS];
+ BitSet32 idBits;
+ uint32_t idToIndex[MAX_POINTER_ID + 1];
+
+ void copyFrom(const TouchData& other) {
+ pointerCount = other.pointerCount;
+ idBits = other.idBits;
+
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ pointers[i] = other.pointers[i];
+ idToIndex[i] = other.idToIndex[i];
+ }
+ }
+
+ inline void clear() {
+ pointerCount = 0;
+ idBits.clear();
+ }
+ };
+
+ int32_t mAssociatedDisplayId;
+ Vector<VirtualKey> mVirtualKeys;
+
+ // Immutable configuration parameters.
+ struct Parameters {
+ bool useBadTouchFilter;
+ bool useJumpyTouchFilter;
+ bool useAveragingTouchFilter;
+ } mParameters;
+
+ // Raw axis information.
+ struct Axes {
+ RawAbsoluteAxisInfo x;
+ RawAbsoluteAxisInfo y;
+ RawAbsoluteAxisInfo pressure;
+ RawAbsoluteAxisInfo size;
+ RawAbsoluteAxisInfo touchMajor;
+ RawAbsoluteAxisInfo touchMinor;
+ RawAbsoluteAxisInfo toolMajor;
+ RawAbsoluteAxisInfo toolMinor;
+ RawAbsoluteAxisInfo orientation;
+ } mAxes;
+
+ // The surface orientation and width and height set by configureSurface().
+ int32_t mSurfaceOrientation;
+ int32_t mSurfaceWidth, mSurfaceHeight;
+
+ // Translation and scaling factors, orientation-independent.
+ int32_t mXOrigin;
+ float mXScale;
+ float mXPrecision;
+
+ int32_t mYOrigin;
+ float mYScale;
+ float mYPrecision;
+
+ int32_t mPressureOrigin;
+ float mPressureScale;
+
+ int32_t mSizeOrigin;
+ float mSizeScale;
+
+ float mOrientationScale;
+
+ // Oriented motion ranges for input device info.
+ struct OrientedRanges {
+ InputDeviceInfo::MotionRange x;
+ InputDeviceInfo::MotionRange y;
+ InputDeviceInfo::MotionRange pressure;
+ InputDeviceInfo::MotionRange size;
+ InputDeviceInfo::MotionRange touchMajor;
+ InputDeviceInfo::MotionRange touchMinor;
+ InputDeviceInfo::MotionRange toolMajor;
+ InputDeviceInfo::MotionRange toolMinor;
+ InputDeviceInfo::MotionRange orientation;
+ } mOrientedRanges;
+
+ // Oriented dimensions and precision.
+ float mOrientedSurfaceWidth, mOrientedSurfaceHeight;
+ float mOrientedXPrecision, mOrientedYPrecision;
+
+ // The touch data of the current sample being processed.
+ TouchData mCurrentTouch;
+
+ // The touch data of the previous sample that was processed. This is updated
+ // incrementally while the current sample is being processed.
+ TouchData mLastTouch;
+
+ // The time the primary pointer last went down.
+ nsecs_t mDownTime;
+
+ struct CurrentVirtualKeyState {
+ bool down;
+ nsecs_t downTime;
+ int32_t keyCode;
+ int32_t scanCode;
+ } mCurrentVirtualKey;
+
+ // Lock for virtual key state.
+ Mutex mVirtualKeyLock; // methods use "Lvk" suffix
+
+ virtual void configureAxes();
+ virtual bool configureSurface();
+ virtual void configureVirtualKeys();
+
+ enum TouchResult {
+ // Dispatch the touch normally.
+ DISPATCH_TOUCH,
+ // Do not dispatch the touch, but keep tracking the current stroke.
+ SKIP_TOUCH,
+ // Do not dispatch the touch, and drop all information associated with the current stoke
+ // so the next movement will appear as a new down.
+ DROP_STROKE
+ };
+
+ void syncTouch(nsecs_t when, bool havePointerIds);
+
+private:
+ /* Maximum number of historical samples to average. */
+ static const uint32_t AVERAGING_HISTORY_SIZE = 5;
+
+ /* Slop distance for jumpy pointer detection.
+ * The vertical range of the screen divided by this is our epsilon value. */
+ static const uint32_t JUMPY_EPSILON_DIVISOR = 212;
+
+ /* Number of jumpy points to drop for touchscreens that need it. */
+ static const uint32_t JUMPY_TRANSITION_DROPS = 3;
+ static const uint32_t JUMPY_DROP_LIMIT = 3;
+
+ /* Maximum squared distance for averaging.
+ * If moving farther than this, turn of averaging to avoid lag in response. */
+ static const uint64_t AVERAGING_DISTANCE_LIMIT = 75 * 75;
+
+ struct AveragingTouchFilterState {
+ // Individual history tracks are stored by pointer id
+ uint32_t historyStart[MAX_POINTERS];
+ uint32_t historyEnd[MAX_POINTERS];
+ struct {
+ struct {
+ int32_t x;
+ int32_t y;
+ int32_t pressure;
+ } pointers[MAX_POINTERS];
+ } historyData[AVERAGING_HISTORY_SIZE];
+ } mAveragingTouchFilter;
+
+ struct JumpTouchFilterState {
+ uint32_t jumpyPointsDropped;
+ } mJumpyTouchFilter;
+
+ struct PointerDistanceHeapElement {
+ uint32_t currentPointerIndex : 8;
+ uint32_t lastPointerIndex : 8;
+ uint64_t distance : 48; // squared distance
+ };
+
+ void initialize();
+
+ TouchResult consumeOffScreenTouches(nsecs_t when, uint32_t policyFlags);
+ void dispatchTouches(nsecs_t when, uint32_t policyFlags);
+ void dispatchTouch(nsecs_t when, uint32_t policyFlags, TouchData* touch,
+ BitSet32 idBits, uint32_t changedId, int32_t motionEventAction);
+
+ bool isPointInsideSurface(int32_t x, int32_t y);
+ const VirtualKey* findVirtualKeyHitLvk(int32_t x, int32_t y);
+
+ bool applyBadTouchFilter();
+ bool applyJumpyTouchFilter();
+ void applyAveragingTouchFilter();
+ void calculatePointerIds();
+};
+
+
+class SingleTouchInputMapper : public TouchInputMapper {
+public:
+ SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+ virtual ~SingleTouchInputMapper();
+
+ virtual void reset();
+ virtual void process(const RawEvent* rawEvent);
+
+protected:
+ virtual void configureAxes();
+
+private:
+ struct Accumulator {
+ enum {
+ FIELD_BTN_TOUCH = 1,
+ FIELD_ABS_X = 2,
+ FIELD_ABS_Y = 4,
+ FIELD_ABS_PRESSURE = 8,
+ FIELD_ABS_TOOL_WIDTH = 16
+ };
+
+ uint32_t fields;
+
+ bool btnTouch;
+ int32_t absX;
+ int32_t absY;
+ int32_t absPressure;
+ int32_t absToolWidth;
+
+ inline void clear() {
+ fields = 0;
+ }
+
+ inline bool isDirty() {
+ return fields != 0;
+ }
+ } mAccumulator;
+
+ bool mDown;
+ int32_t mX;
+ int32_t mY;
+ int32_t mPressure;
+ int32_t mSize;
+
+ void initialize();
+
+ void sync(nsecs_t when);
+};
+
+
+class MultiTouchInputMapper : public TouchInputMapper {
+public:
+ MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+ virtual ~MultiTouchInputMapper();
+
+ virtual void reset();
+ virtual void process(const RawEvent* rawEvent);
+
+protected:
+ virtual void configureAxes();
+
+private:
+ struct Accumulator {
+ enum {
+ FIELD_ABS_MT_POSITION_X = 1,
+ FIELD_ABS_MT_POSITION_Y = 2,
+ FIELD_ABS_MT_TOUCH_MAJOR = 4,
+ FIELD_ABS_MT_TOUCH_MINOR = 8,
+ FIELD_ABS_MT_WIDTH_MAJOR = 16,
+ FIELD_ABS_MT_WIDTH_MINOR = 32,
+ FIELD_ABS_MT_ORIENTATION = 64,
+ FIELD_ABS_MT_TRACKING_ID = 128
+ };
+
+ uint32_t pointerCount;
+ struct Pointer {
+ uint32_t fields;
+
+ int32_t absMTPositionX;
+ int32_t absMTPositionY;
+ int32_t absMTTouchMajor;
+ int32_t absMTTouchMinor;
+ int32_t absMTWidthMajor;
+ int32_t absMTWidthMinor;
+ int32_t absMTOrientation;
+ int32_t absMTTrackingId;
+
+ inline void clear() {
+ fields = 0;
+ }
+ } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks
+
+ inline void clear() {
+ pointerCount = 0;
+ pointers[0].clear();
+ }
+
+ inline bool isDirty() {
+ return pointerCount != 0;
+ }
+ } mAccumulator;
+
+ void initialize();
+
+ void sync(nsecs_t when);
+};
+
} // namespace android
#endif // _UI_INPUT_READER_H
diff --git a/include/utils/Asset.h b/include/utils/Asset.h
index 5908bccedd64..2a09095bdefe 100644
--- a/include/utils/Asset.h
+++ b/include/utils/Asset.h
@@ -61,15 +61,6 @@ public:
ACCESS_BUFFER,
} AccessMode;
- enum {
- /* data larger than this does not get uncompressed into a buffer */
-#ifdef HAVE_ANDROID_OS
- UNCOMPRESS_DATA_MAX = 1 * 1024 * 1024
-#else
- UNCOMPRESS_DATA_MAX = 2 * 1024 * 1024
-#endif
- };
-
/*
* Read data from the current offset. Returns the actual number of
* bytes read, 0 on EOF, or -1 on error.
@@ -317,6 +308,8 @@ private:
FileMap* mMap; // for memory-mapped input
int mFd; // for file input
+ class StreamingZipInflater* mZipInflater; // for streaming large compressed assets
+
unsigned char* mBuf; // for getBuffer()
};
diff --git a/include/utils/StreamingZipInflater.h b/include/utils/StreamingZipInflater.h
new file mode 100644
index 000000000000..16867d8db26d
--- /dev/null
+++ b/include/utils/StreamingZipInflater.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LIBS_STREAMINGZIPINFLATER_H
+#define __LIBS_STREAMINGZIPINFLATER_H
+
+#include <unistd.h>
+#include <inttypes.h>
+#include <zlib.h>
+
+namespace android {
+
+class StreamingZipInflater {
+public:
+ static const size_t INPUT_CHUNK_SIZE = 64 * 1024;
+ static const size_t OUTPUT_CHUNK_SIZE = 64 * 1024;
+
+ // Flavor that pages in the compressed data from a fd
+ StreamingZipInflater(int fd, off_t compDataStart, size_t uncompSize, size_t compSize);
+
+ // Flavor that gets the compressed data from an in-memory buffer
+ StreamingZipInflater(class FileMap* dataMap, size_t uncompSize);
+
+ ~StreamingZipInflater();
+
+ // read 'count' bytes of uncompressed data from the current position. outBuf may
+ // be NULL, in which case the data is consumed and discarded.
+ ssize_t read(void* outBuf, size_t count);
+
+ // seeking backwards requires uncompressing fom the beginning, so is very
+ // expensive. seeking forwards only requires uncompressing from the current
+ // position to the destination.
+ off_t seekAbsolute(off_t absoluteInputPosition);
+
+private:
+ void initInflateState();
+ int readNextChunk();
+
+ // where to find the uncompressed data
+ int mFd;
+ off_t mInFileStart; // where the compressed data lives in the file
+ class FileMap* mDataMap;
+
+ z_stream mInflateState;
+ bool mStreamNeedsInit;
+
+ // output invariants for this asset
+ uint8_t* mOutBuf; // output buf for decompressed bytes
+ size_t mOutBufSize; // allocated size of mOutBuf
+ size_t mOutTotalSize; // total uncompressed size of the blob
+
+ // current output state bookkeeping
+ off_t mOutCurPosition; // current position in total offset
+ size_t mOutLastDecoded; // last decoded byte + 1 in mOutbuf
+ size_t mOutDeliverable; // next undelivered byte of decoded output in mOutBuf
+
+ // input invariants
+ uint8_t* mInBuf;
+ size_t mInBufSize; // allocated size of mInBuf;
+ size_t mInTotalSize; // total size of compressed data for this blob
+
+ // input state bookkeeping
+ size_t mInNextChunkOffset; // offset from start of blob at which the next input chunk lies
+ // the z_stream contains state about input block consumption
+};
+
+}
+
+#endif
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 60babad379fd..18f75df2ea2f 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -458,13 +458,13 @@ bool Parcel::checkInterface(IBinder* binder) const
}
bool Parcel::enforceInterface(const String16& interface,
- int32_t* strict_policy_out) const
+ IPCThreadState* threadState) const
{
- int32_t strict_policy = readInt32();
- IPCThreadState::self()->setStrictModePolicy(strict_policy);
- if (strict_policy_out != NULL) {
- *strict_policy_out = strict_policy;
+ int32_t strictPolicy = readInt32();
+ if (threadState == NULL) {
+ threadState = IPCThreadState::self();
}
+ threadState->setStrictModePolicy(strictPolicy);
const String16 str(readString16());
if (str == interface) {
return true;
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
index cb85df9bb04d..b1f37ffed2a8 100644
--- a/libs/gui/Sensor.cpp
+++ b/libs/gui/Sensor.cpp
@@ -32,7 +32,7 @@ namespace android {
Sensor::Sensor()
: mHandle(0), mType(0),
mMinValue(0), mMaxValue(0), mResolution(0),
- mPower(0)
+ mPower(0), mMinDelay(0)
{
}
@@ -46,6 +46,7 @@ Sensor::Sensor(struct sensor_t const* hwSensor)
mMaxValue = hwSensor->maxRange; // FIXME: maxValue
mResolution = hwSensor->resolution;
mPower = hwSensor->power;
+ mMinDelay = hwSensor->minDelay;
}
Sensor::~Sensor()
@@ -84,12 +85,17 @@ float Sensor::getPowerUsage() const {
return mPower;
}
+int32_t Sensor::getMinDelay() const {
+ return mMinDelay;
+}
+
size_t Sensor::getFlattenedSize() const
{
return sizeof(int32_t) + ((mName.length() + 3) & ~3) +
sizeof(int32_t) + ((mVendor.length() + 3) & ~3) +
sizeof(int32_t) * 2 +
- sizeof(float) * 4;
+ sizeof(float) * 4 +
+ sizeof(int32_t);
}
size_t Sensor::getFdCount() const
@@ -132,6 +138,7 @@ status_t Sensor::flatten(void* buffer, size_t size,
offset += write(buffer, offset, mMaxValue);
offset += write(buffer, offset, mResolution);
offset += write(buffer, offset, mPower);
+ offset += write(buffer, offset, mMinDelay);
return NO_ERROR;
}
@@ -169,6 +176,7 @@ status_t Sensor::unflatten(void const* buffer, size_t size,
offset += read(buffer, offset, &mMaxValue);
offset += read(buffer, offset, &mResolution);
offset += read(buffer, offset, &mPower);
+ offset += read(buffer, offset, &mMinDelay);
return NO_ERROR;
}
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp
index 4b468424912b..3396f25e714a 100644
--- a/libs/gui/SensorEventQueue.cpp
+++ b/libs/gui/SensorEventQueue.cpp
@@ -114,10 +114,10 @@ status_t SensorEventQueue::disableSensor(Sensor const* sensor) const {
return mSensorEventConnection->enableDisable(sensor->getHandle(), false);
}
-status_t SensorEventQueue::enableSensor(int32_t handle, int32_t ms) const {
+status_t SensorEventQueue::enableSensor(int32_t handle, int32_t us) const {
status_t err = mSensorEventConnection->enableDisable(handle, true);
if (err == NO_ERROR) {
- mSensorEventConnection->setEventRate(handle, ms2ns(ms));
+ mSensorEventConnection->setEventRate(handle, us2ns(us));
}
return err;
}
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index d67a589ac90a..156a7db3c190 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -243,21 +243,6 @@ bool SharedBufferClient::LockCondition::operator()() const {
(stack.queued > 0 && stack.inUse != buf));
}
-SharedBufferServer::ReallocateCondition::ReallocateCondition(
- SharedBufferBase* sbb, int buf) : ConditionBase(sbb), buf(buf) {
-}
-bool SharedBufferServer::ReallocateCondition::operator()() const {
- int32_t head = stack.head;
- if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX) {
- // if stack.head is messed up, we cannot allow the server to
- // crash (since stack.head is mapped on the client side)
- stack.status = BAD_VALUE;
- return false;
- }
- // TODO: we should also check that buf has been dequeued
- return (buf != stack.index[head]);
-}
-
// ----------------------------------------------------------------------------
SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb)
@@ -558,21 +543,6 @@ int32_t SharedBufferServer::getQueuedCount() const
return stack.queued;
}
-status_t SharedBufferServer::assertReallocate(int buf)
-{
- /*
- * NOTE: it's safe to hold mLock for read while waiting for
- * the ReallocateCondition because that condition is not updated
- * by the thread that holds mLock for write.
- */
- RWLock::AutoRLock _l(mLock);
-
- // TODO: need to validate "buf"
- ReallocateCondition condition(this, buf);
- status_t err = waitForCondition(condition);
- return err;
-}
-
Region SharedBufferServer::getDirtyRegion(int buf) const
{
SharedBufferStack& stack( *mSharedStack );
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 4243bbf47288..9f493483bf95 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -12,7 +12,6 @@ LOCAL_SRC_FILES:= \
KeyLayoutMap.cpp \
KeyCharacterMap.cpp \
Input.cpp \
- InputDevice.cpp \
InputDispatcher.cpp \
InputManager.cpp \
InputReader.cpp \
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 33dd3732bb6d..124f7b3cd217 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -137,9 +137,14 @@ uint32_t EventHub::getDeviceClasses(int32_t deviceId) const
return device->classes;
}
-int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
- int* outMaxValue, int* outFlat, int* outFuzz) const
-{
+status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const {
+ outAxisInfo->valid = false;
+ outAxisInfo->minValue = 0;
+ outAxisInfo->maxValue = 0;
+ outAxisInfo->flat = 0;
+ outAxisInfo->fuzz = 0;
+
AutoMutex _l(mLock);
device_t* device = getDevice(deviceId);
if (device == NULL) return -1;
@@ -147,38 +152,28 @@ int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
struct input_absinfo info;
if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) {
- LOGE("Error reading absolute controller %d for device %s fd %d\n",
+ LOGW("Error reading absolute controller %d for device %s fd %d\n",
axis, device->name.string(), mFDs[id_to_index(device->id)].fd);
- return -1;
+ return -errno;
}
- *outMinValue = info.minimum;
- *outMaxValue = info.maximum;
- *outFlat = info.flat;
- *outFuzz = info.fuzz;
- return 0;
+
+ if (info.minimum != info.maximum) {
+ outAxisInfo->valid = true;
+ outAxisInfo->minValue = info.minimum;
+ outAxisInfo->maxValue = info.maximum;
+ outAxisInfo->flat = info.flat;
+ outAxisInfo->fuzz = info.fuzz;
+ }
+ return OK;
}
-int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t scanCode) const {
+int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
if (scanCode >= 0 && scanCode <= KEY_MAX) {
AutoMutex _l(mLock);
- if (deviceId == -1) {
- for (int i = 0; i < mNumDevicesById; i++) {
- device_t* device = mDevicesById[i].device;
- if (device != NULL && (device->classes & deviceClasses) != 0) {
- int32_t result = getScanCodeStateLocked(device, scanCode);
- if (result >= AKEY_STATE_DOWN) {
- return result;
- }
- }
- }
- return AKEY_STATE_UP;
- } else {
- device_t* device = getDevice(deviceId);
- if (device != NULL) {
- return getScanCodeStateLocked(device, scanCode);
- }
+ device_t* device = getDevice(deviceId);
+ if (device != NULL) {
+ return getScanCodeStateLocked(device, scanCode);
}
}
return AKEY_STATE_UNKNOWN;
@@ -194,25 +189,12 @@ int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) con
return AKEY_STATE_UNKNOWN;
}
-int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t keyCode) const {
+int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
+ AutoMutex _l(mLock);
- if (deviceId == -1) {
- for (int i = 0; i < mNumDevicesById; i++) {
- device_t* device = mDevicesById[i].device;
- if (device != NULL && (device->classes & deviceClasses) != 0) {
- int32_t result = getKeyCodeStateLocked(device, keyCode);
- if (result >= AKEY_STATE_DOWN) {
- return result;
- }
- }
- }
- return AKEY_STATE_UP;
- } else {
- device_t* device = getDevice(deviceId);
- if (device != NULL) {
- return getKeyCodeStateLocked(device, keyCode);
- }
+ device_t* device = getDevice(deviceId);
+ if (device != NULL) {
+ return getKeyCodeStateLocked(device, keyCode);
}
return AKEY_STATE_UNKNOWN;
}
@@ -243,24 +225,15 @@ int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const
return AKEY_STATE_UNKNOWN;
}
-int32_t EventHub::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const {
+int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
#ifdef EV_SW
if (sw >= 0 && sw <= SW_MAX) {
AutoMutex _l(mLock);
- if (deviceId == -1) {
- deviceId = mSwitches[sw];
- if (deviceId == 0) {
- return AKEY_STATE_UNKNOWN;
- }
- }
-
device_t* device = getDevice(deviceId);
- if (device == NULL) {
- return AKEY_STATE_UNKNOWN;
+ if (device != NULL) {
+ return getSwitchStateLocked(device, sw);
}
-
- return getSwitchStateLocked(device, sw);
}
#endif
return AKEY_STATE_UNKNOWN;
@@ -276,6 +249,42 @@ int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
return AKEY_STATE_UNKNOWN;
}
+bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) const {
+ AutoMutex _l(mLock);
+
+ device_t* device = getDevice(deviceId);
+ if (device != NULL) {
+ return markSupportedKeyCodesLocked(device, numCodes, keyCodes, outFlags);
+ }
+ return false;
+}
+
+bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) const {
+ if (device->layoutMap == NULL || device->keyBitmask == NULL) {
+ return false;
+ }
+
+ Vector<int32_t> scanCodes;
+ for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
+ scanCodes.clear();
+
+ status_t err = device->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes);
+ if (! err) {
+ // check the possible scan codes identified by the layout map against the
+ // map of codes actually emitted by the driver
+ for (size_t sc = 0; sc < scanCodes.size(); sc++) {
+ if (test_bit(scanCodes[sc], device->keyBitmask)) {
+ outFlags[codeIndex] = 1;
+ break;
+ }
+ }
+ }
+ }
+ return true;
+}
+
status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode,
int32_t* outKeycode, uint32_t* outFlags) const
{
@@ -324,17 +333,15 @@ EventHub::device_t* EventHub::getDevice(int32_t deviceId) const
return NULL;
}
-bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
- int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
- int32_t* outValue, nsecs_t* outWhen)
+bool EventHub::getEvent(RawEvent* outEvent)
{
- *outDeviceId = 0;
- *outType = 0;
- *outScancode = 0;
- *outKeycode = 0;
- *outFlags = 0;
- *outValue = 0;
- *outWhen = 0;
+ outEvent->deviceId = 0;
+ outEvent->type = 0;
+ outEvent->scanCode = 0;
+ outEvent->keyCode = 0;
+ outEvent->flags = 0;
+ outEvent->value = 0;
+ outEvent->when = 0;
status_t err;
@@ -359,20 +366,27 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
LOGV("Reporting device closed: id=0x%x, name=%s\n",
device->id, device->path.string());
mClosingDevices = device->next;
- *outDeviceId = device->id;
- if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
- *outType = DEVICE_REMOVED;
+ if (device->id == mFirstKeyboardId) {
+ outEvent->deviceId = 0;
+ } else {
+ outEvent->deviceId = device->id;
+ }
+ outEvent->type = DEVICE_REMOVED;
delete device;
return true;
}
+
if (mOpeningDevices != NULL) {
device_t* device = mOpeningDevices;
LOGV("Reporting device opened: id=0x%x, name=%s\n",
device->id, device->path.string());
mOpeningDevices = device->next;
- *outDeviceId = device->id;
- if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
- *outType = DEVICE_ADDED;
+ if (device->id == mFirstKeyboardId) {
+ outEvent->deviceId = 0;
+ } else {
+ outEvent->deviceId = device->id;
+ }
+ outEvent->type = DEVICE_ADDED;
return true;
}
@@ -399,27 +413,36 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
if(mFDs[i].revents & POLLIN) {
res = read(mFDs[i].fd, &iev, sizeof(iev));
if (res == sizeof(iev)) {
+ device_t* device = mDevices[i];
LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d",
- mDevices[i]->path.string(),
+ device->path.string(),
(int) iev.time.tv_sec, (int) iev.time.tv_usec,
iev.type, iev.code, iev.value);
- *outDeviceId = mDevices[i]->id;
- if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
- *outType = iev.type;
- *outScancode = iev.code;
+ if (device->id == mFirstKeyboardId) {
+ outEvent->deviceId = 0;
+ } else {
+ outEvent->deviceId = device->id;
+ }
+ outEvent->type = iev.type;
+ outEvent->scanCode = iev.code;
if (iev.type == EV_KEY) {
- err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags);
- LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n",
- iev.code, *outKeycode, *outFlags, err);
+ err = device->layoutMap->map(iev.code,
+ & outEvent->keyCode, & outEvent->flags);
+ LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
+ iev.code, outEvent->keyCode, outEvent->flags, err);
if (err != 0) {
- *outKeycode = AKEYCODE_UNKNOWN;
- *outFlags = 0;
+ outEvent->keyCode = AKEYCODE_UNKNOWN;
+ outEvent->flags = 0;
}
} else {
- *outKeycode = iev.code;
+ outEvent->keyCode = iev.code;
}
- *outValue = iev.value;
- *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
+ outEvent->value = iev.value;
+
+ // Use an event timestamp in the same timebase as
+ // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()
+ // as expected by the rest of the system.
+ outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
return true;
} else {
if (res<0) {
@@ -479,37 +502,6 @@ bool EventHub::openPlatformInput(void)
return true;
}
-/*
- * Inspect the known devices to determine whether physical keys exist for the given
- * framework-domain key codes.
- */
-bool EventHub::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const {
- for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
- outFlags[codeIndex] = 0;
-
- // check each available hardware device for support for this keycode
- Vector<int32_t> scanCodes;
- for (int n = 0; (n < mFDCount) && (outFlags[codeIndex] == 0); n++) {
- if (mDevices[n]) {
- status_t err = mDevices[n]->layoutMap->findScancodes(
- keyCodes[codeIndex], &scanCodes);
- if (!err) {
- // check the possible scan codes identified by the layout map against the
- // map of codes actually emitted by the driver
- for (size_t sc = 0; sc < scanCodes.size(); sc++) {
- if (test_bit(scanCodes[sc], mDevices[n]->keyBitmask)) {
- outFlags[codeIndex] = 1;
- break;
- }
- }
- }
- }
- }
- }
-
- return true;
-}
-
// ----------------------------------------------------------------------------
static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) {
@@ -715,16 +707,21 @@ int EventHub::open_device(const char *deviceName)
// figure out the switches this device reports
uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
memset(sw_bitmask, 0, sizeof(sw_bitmask));
+ bool hasSwitches = false;
if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) {
for (int i=0; i<EV_SW; i++) {
//LOGI("Device 0x%x sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask));
if (test_bit(i, sw_bitmask)) {
+ hasSwitches = true;
if (mSwitches[i] == 0) {
mSwitches[i] = device->id;
}
}
}
}
+ if (hasSwitches) {
+ device->classes |= INPUT_DEVICE_CLASS_SWITCH;
+ }
#endif
if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 5253c72aa382..5fbaf0961bfb 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -168,4 +168,63 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) {
mYOffset += yOffset;
}
+// class InputDeviceInfo
+
+InputDeviceInfo::InputDeviceInfo() {
+ initialize(-1, String8("uninitialized device info"));
+}
+
+InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
+ mId(other.mId), mName(other.mName), mSources(other.mSources),
+ mKeyboardType(other.mKeyboardType),
+ mMotionRanges(other.mMotionRanges) {
+}
+
+InputDeviceInfo::~InputDeviceInfo() {
+}
+
+void InputDeviceInfo::initialize(int32_t id, const String8& name) {
+ mId = id;
+ mName = name;
+ mSources = 0;
+ mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
+ mMotionRanges.clear();
+}
+
+const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t rangeType) const {
+ ssize_t index = mMotionRanges.indexOfKey(rangeType);
+ return index >= 0 ? & mMotionRanges.valueAt(index) : NULL;
+}
+
+void InputDeviceInfo::addSource(uint32_t source) {
+ mSources |= source;
+}
+
+void InputDeviceInfo::addMotionRange(int32_t rangeType, float min, float max,
+ float flat, float fuzz) {
+ MotionRange range = { min, max, flat, fuzz };
+ addMotionRange(rangeType, range);
+}
+
+void InputDeviceInfo::addMotionRange(int32_t rangeType, const MotionRange& range) {
+ mMotionRanges.add(rangeType, range);
+}
+
+// class InputDeviceProxy
+
+InputDeviceProxy::InputDeviceProxy() {
+}
+
+InputDeviceProxy::~InputDeviceProxy() {
+}
+
+void InputDeviceProxy::getDeviceIds(Vector<int32_t>& outIds) {
+ // TODO use Binder
+}
+
+sp<InputDeviceProxy> InputDeviceProxy::getDevice(int32_t id) {
+ // TODO use Binder
+ return NULL;
+}
+
} // namespace android
diff --git a/libs/ui/InputDevice.cpp b/libs/ui/InputDevice.cpp
deleted file mode 100644
index b2a4d6c8fdd5..000000000000
--- a/libs/ui/InputDevice.cpp
+++ /dev/null
@@ -1,729 +0,0 @@
-//
-// Copyright 2010 The Android Open Source Project
-//
-// The input reader.
-//
-#define LOG_TAG "InputDevice"
-
-//#define LOG_NDEBUG 0
-
-// Log debug messages for each raw event received from the EventHub.
-#define DEBUG_RAW_EVENTS 0
-
-// Log debug messages about touch screen filtering hacks.
-#define DEBUG_HACKS 0
-
-// Log debug messages about virtual key processing.
-#define DEBUG_VIRTUAL_KEYS 0
-
-// Log debug messages about pointers.
-#define DEBUG_POINTERS 0
-
-// Log debug messages about pointer assignment calculations.
-#define DEBUG_POINTER_ASSIGNMENT 0
-
-#include <cutils/log.h>
-#include <ui/InputDevice.h>
-
-#include <stddef.h>
-#include <unistd.h>
-#include <errno.h>
-#include <limits.h>
-
-/* Slop distance for jumpy pointer detection.
- * The vertical range of the screen divided by this is our epsilon value. */
-#define JUMPY_EPSILON_DIVISOR 212
-
-/* Number of jumpy points to drop for touchscreens that need it. */
-#define JUMPY_TRANSITION_DROPS 3
-#define JUMPY_DROP_LIMIT 3
-
-/* Maximum squared distance for averaging.
- * If moving farther than this, turn of averaging to avoid lag in response. */
-#define AVERAGING_DISTANCE_LIMIT (75 * 75)
-
-
-namespace android {
-
-// --- Static Functions ---
-
-template<typename T>
-inline static T abs(const T& value) {
- return value < 0 ? - value : value;
-}
-
-template<typename T>
-inline static T min(const T& a, const T& b) {
- return a < b ? a : b;
-}
-
-template<typename T>
-inline static void swap(T& a, T& b) {
- T temp = a;
- a = b;
- b = temp;
-}
-
-
-// --- InputDevice ---
-
-InputDevice::InputDevice(int32_t id, uint32_t classes, String8 name) :
- id(id), classes(classes), name(name), ignored(false) {
-}
-
-void InputDevice::reset() {
- if (isKeyboard()) {
- keyboard.reset();
- }
-
- if (isTrackball()) {
- trackball.reset();
- }
-
- if (isMultiTouchScreen()) {
- multiTouchScreen.reset();
- } else if (isSingleTouchScreen()) {
- singleTouchScreen.reset();
- }
-
- if (isTouchScreen()) {
- touchScreen.reset();
- }
-}
-
-
-// --- InputDevice::TouchData ---
-
-void InputDevice::TouchData::copyFrom(const TouchData& other) {
- pointerCount = other.pointerCount;
- idBits = other.idBits;
-
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointers[i] = other.pointers[i];
- idToIndex[i] = other.idToIndex[i];
- }
-}
-
-
-// --- InputDevice::KeyboardState ---
-
-void InputDevice::KeyboardState::reset() {
- current.metaState = AMETA_NONE;
- current.downTime = 0;
-}
-
-
-// --- InputDevice::TrackballState ---
-
-void InputDevice::TrackballState::reset() {
- accumulator.clear();
- current.down = false;
- current.downTime = 0;
-}
-
-
-// --- InputDevice::TouchScreenState ---
-
-void InputDevice::TouchScreenState::reset() {
- lastTouch.clear();
- downTime = 0;
- currentVirtualKey.status = CurrentVirtualKeyState::STATUS_UP;
-
- for (uint32_t i = 0; i < MAX_POINTERS; i++) {
- averagingTouchFilter.historyStart[i] = 0;
- averagingTouchFilter.historyEnd[i] = 0;
- }
-
- jumpyTouchFilter.jumpyPointsDropped = 0;
-}
-
-struct PointerDistanceHeapElement {
- uint32_t currentPointerIndex : 8;
- uint32_t lastPointerIndex : 8;
- uint64_t distance : 48; // squared distance
-};
-
-void InputDevice::TouchScreenState::calculatePointerIds() {
- uint32_t currentPointerCount = currentTouch.pointerCount;
- uint32_t lastPointerCount = lastTouch.pointerCount;
-
- if (currentPointerCount == 0) {
- // No pointers to assign.
- currentTouch.idBits.clear();
- } else if (lastPointerCount == 0) {
- // All pointers are new.
- currentTouch.idBits.clear();
- for (uint32_t i = 0; i < currentPointerCount; i++) {
- currentTouch.pointers[i].id = i;
- currentTouch.idToIndex[i] = i;
- currentTouch.idBits.markBit(i);
- }
- } else if (currentPointerCount == 1 && lastPointerCount == 1) {
- // Only one pointer and no change in count so it must have the same id as before.
- uint32_t id = lastTouch.pointers[0].id;
- currentTouch.pointers[0].id = id;
- currentTouch.idToIndex[id] = 0;
- currentTouch.idBits.value = BitSet32::valueForBit(id);
- } else {
- // General case.
- // We build a heap of squared euclidean distances between current and last pointers
- // associated with the current and last pointer indices. Then, we find the best
- // match (by distance) for each current pointer.
- PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
-
- uint32_t heapSize = 0;
- for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
- currentPointerIndex++) {
- for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
- lastPointerIndex++) {
- int64_t deltaX = currentTouch.pointers[currentPointerIndex].x
- - lastTouch.pointers[lastPointerIndex].x;
- int64_t deltaY = currentTouch.pointers[currentPointerIndex].y
- - lastTouch.pointers[lastPointerIndex].y;
-
- uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
-
- // Insert new element into the heap (sift up).
- heap[heapSize].currentPointerIndex = currentPointerIndex;
- heap[heapSize].lastPointerIndex = lastPointerIndex;
- heap[heapSize].distance = distance;
- heapSize += 1;
- }
- }
-
- // Heapify
- for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
- startIndex -= 1;
- for (uint32_t parentIndex = startIndex; ;) {
- uint32_t childIndex = parentIndex * 2 + 1;
- if (childIndex >= heapSize) {
- break;
- }
-
- if (childIndex + 1 < heapSize
- && heap[childIndex + 1].distance < heap[childIndex].distance) {
- childIndex += 1;
- }
-
- if (heap[parentIndex].distance <= heap[childIndex].distance) {
- break;
- }
-
- swap(heap[parentIndex], heap[childIndex]);
- parentIndex = childIndex;
- }
- }
-
-#if DEBUG_POINTER_ASSIGNMENT
- LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize);
- for (size_t i = 0; i < heapSize; i++) {
- LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
- i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
- heap[i].distance);
- }
-#endif
-
- // Pull matches out by increasing order of distance.
- // To avoid reassigning pointers that have already been matched, the loop keeps track
- // of which last and current pointers have been matched using the matchedXXXBits variables.
- // It also tracks the used pointer id bits.
- BitSet32 matchedLastBits(0);
- BitSet32 matchedCurrentBits(0);
- BitSet32 usedIdBits(0);
- bool first = true;
- for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) {
- for (;;) {
- if (first) {
- // The first time through the loop, we just consume the root element of
- // the heap (the one with smallest distance).
- first = false;
- } else {
- // Previous iterations consumed the root element of the heap.
- // Pop root element off of the heap (sift down).
- heapSize -= 1;
- assert(heapSize > 0);
-
- // Sift down.
- heap[0] = heap[heapSize];
- for (uint32_t parentIndex = 0; ;) {
- uint32_t childIndex = parentIndex * 2 + 1;
- if (childIndex >= heapSize) {
- break;
- }
-
- if (childIndex + 1 < heapSize
- && heap[childIndex + 1].distance < heap[childIndex].distance) {
- childIndex += 1;
- }
-
- if (heap[parentIndex].distance <= heap[childIndex].distance) {
- break;
- }
-
- swap(heap[parentIndex], heap[childIndex]);
- parentIndex = childIndex;
- }
-
-#if DEBUG_POINTER_ASSIGNMENT
- LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize);
- for (size_t i = 0; i < heapSize; i++) {
- LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
- i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
- heap[i].distance);
- }
-#endif
- }
-
- uint32_t currentPointerIndex = heap[0].currentPointerIndex;
- if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
-
- uint32_t lastPointerIndex = heap[0].lastPointerIndex;
- if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
-
- matchedCurrentBits.markBit(currentPointerIndex);
- matchedLastBits.markBit(lastPointerIndex);
-
- uint32_t id = lastTouch.pointers[lastPointerIndex].id;
- currentTouch.pointers[currentPointerIndex].id = id;
- currentTouch.idToIndex[id] = currentPointerIndex;
- usedIdBits.markBit(id);
-
-#if DEBUG_POINTER_ASSIGNMENT
- LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
- lastPointerIndex, currentPointerIndex, id, heap[0].distance);
-#endif
- break;
- }
- }
-
- // Assign fresh ids to new pointers.
- if (currentPointerCount > lastPointerCount) {
- for (uint32_t i = currentPointerCount - lastPointerCount; ;) {
- uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit();
- uint32_t id = usedIdBits.firstUnmarkedBit();
-
- currentTouch.pointers[currentPointerIndex].id = id;
- currentTouch.idToIndex[id] = currentPointerIndex;
- usedIdBits.markBit(id);
-
-#if DEBUG_POINTER_ASSIGNMENT
- LOGD("calculatePointerIds - assigned: cur=%d, id=%d",
- currentPointerIndex, id);
-#endif
-
- if (--i == 0) break; // done
- matchedCurrentBits.markBit(currentPointerIndex);
- }
- }
-
- // Fix id bits.
- currentTouch.idBits = usedIdBits;
- }
-}
-
-/* Special hack for devices that have bad screen data: if one of the
- * points has moved more than a screen height from the last position,
- * then drop it. */
-bool InputDevice::TouchScreenState::applyBadTouchFilter() {
- // This hack requires valid axis parameters.
- if (! parameters.yAxis.valid) {
- return false;
- }
-
- uint32_t pointerCount = currentTouch.pointerCount;
-
- // Nothing to do if there are no points.
- if (pointerCount == 0) {
- return false;
- }
-
- // Don't do anything if a finger is going down or up. We run
- // here before assigning pointer IDs, so there isn't a good
- // way to do per-finger matching.
- if (pointerCount != lastTouch.pointerCount) {
- return false;
- }
-
- // We consider a single movement across more than a 7/16 of
- // the long size of the screen to be bad. This was a magic value
- // determined by looking at the maximum distance it is feasible
- // to actually move in one sample.
- int32_t maxDeltaY = parameters.yAxis.range * 7 / 16;
-
- // XXX The original code in InputDevice.java included commented out
- // code for testing the X axis. Note that when we drop a point
- // we don't actually restore the old X either. Strange.
- // The old code also tries to track when bad points were previously
- // detected but it turns out that due to the placement of a "break"
- // at the end of the loop, we never set mDroppedBadPoint to true
- // so it is effectively dead code.
- // Need to figure out if the old code is busted or just overcomplicated
- // but working as intended.
-
- // Look through all new points and see if any are farther than
- // acceptable from all previous points.
- for (uint32_t i = pointerCount; i-- > 0; ) {
- int32_t y = currentTouch.pointers[i].y;
- int32_t closestY = INT_MAX;
- int32_t closestDeltaY = 0;
-
-#if DEBUG_HACKS
- LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y);
-#endif
-
- for (uint32_t j = pointerCount; j-- > 0; ) {
- int32_t lastY = lastTouch.pointers[j].y;
- int32_t deltaY = abs(y - lastY);
-
-#if DEBUG_HACKS
- LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d",
- j, lastY, deltaY);
-#endif
-
- if (deltaY < maxDeltaY) {
- goto SkipSufficientlyClosePoint;
- }
- if (deltaY < closestDeltaY) {
- closestDeltaY = deltaY;
- closestY = lastY;
- }
- }
-
- // Must not have found a close enough match.
-#if DEBUG_HACKS
- LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d",
- i, y, closestY, closestDeltaY, maxDeltaY);
-#endif
-
- currentTouch.pointers[i].y = closestY;
- return true; // XXX original code only corrects one point
-
- SkipSufficientlyClosePoint: ;
- }
-
- // No change.
- return false;
-}
-
-/* Special hack for devices that have bad screen data: drop points where
- * the coordinate value for one axis has jumped to the other pointer's location.
- */
-bool InputDevice::TouchScreenState::applyJumpyTouchFilter() {
- // This hack requires valid axis parameters.
- if (! parameters.yAxis.valid) {
- return false;
- }
-
- uint32_t pointerCount = currentTouch.pointerCount;
- if (lastTouch.pointerCount != pointerCount) {
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Different pointer count %d -> %d",
- lastTouch.pointerCount, pointerCount);
- for (uint32_t i = 0; i < pointerCount; i++) {
- LOGD(" Pointer %d (%d, %d)", i,
- currentTouch.pointers[i].x, currentTouch.pointers[i].y);
- }
-#endif
-
- if (jumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
- if (lastTouch.pointerCount == 1 && pointerCount == 2) {
- // Just drop the first few events going from 1 to 2 pointers.
- // They're bad often enough that they're not worth considering.
- currentTouch.pointerCount = 1;
- jumpyTouchFilter.jumpyPointsDropped += 1;
-
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Pointer 2 dropped");
-#endif
- return true;
- } else if (lastTouch.pointerCount == 2 && pointerCount == 1) {
- // The event when we go from 2 -> 1 tends to be messed up too
- currentTouch.pointerCount = 2;
- currentTouch.pointers[0] = lastTouch.pointers[0];
- currentTouch.pointers[1] = lastTouch.pointers[1];
- jumpyTouchFilter.jumpyPointsDropped += 1;
-
-#if DEBUG_HACKS
- for (int32_t i = 0; i < 2; i++) {
- LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i,
- currentTouch.pointers[i].x, currentTouch.pointers[i].y);
- }
-#endif
- return true;
- }
- }
- // Reset jumpy points dropped on other transitions or if limit exceeded.
- jumpyTouchFilter.jumpyPointsDropped = 0;
-
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Transition - drop limit reset");
-#endif
- return false;
- }
-
- // We have the same number of pointers as last time.
- // A 'jumpy' point is one where the coordinate value for one axis
- // has jumped to the other pointer's location. No need to do anything
- // else if we only have one pointer.
- if (pointerCount < 2) {
- return false;
- }
-
- if (jumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) {
- int jumpyEpsilon = parameters.yAxis.range / JUMPY_EPSILON_DIVISOR;
-
- // We only replace the single worst jumpy point as characterized by pointer distance
- // in a single axis.
- int32_t badPointerIndex = -1;
- int32_t badPointerReplacementIndex = -1;
- int32_t badPointerDistance = INT_MIN; // distance to be corrected
-
- for (uint32_t i = pointerCount; i-- > 0; ) {
- int32_t x = currentTouch.pointers[i].x;
- int32_t y = currentTouch.pointers[i].y;
-
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y);
-#endif
-
- // Check if a touch point is too close to another's coordinates
- bool dropX = false, dropY = false;
- for (uint32_t j = 0; j < pointerCount; j++) {
- if (i == j) {
- continue;
- }
-
- if (abs(x - currentTouch.pointers[j].x) <= jumpyEpsilon) {
- dropX = true;
- break;
- }
-
- if (abs(y - currentTouch.pointers[j].y) <= jumpyEpsilon) {
- dropY = true;
- break;
- }
- }
- if (! dropX && ! dropY) {
- continue; // not jumpy
- }
-
- // Find a replacement candidate by comparing with older points on the
- // complementary (non-jumpy) axis.
- int32_t distance = INT_MIN; // distance to be corrected
- int32_t replacementIndex = -1;
-
- if (dropX) {
- // X looks too close. Find an older replacement point with a close Y.
- int32_t smallestDeltaY = INT_MAX;
- for (uint32_t j = 0; j < pointerCount; j++) {
- int32_t deltaY = abs(y - lastTouch.pointers[j].y);
- if (deltaY < smallestDeltaY) {
- smallestDeltaY = deltaY;
- replacementIndex = j;
- }
- }
- distance = abs(x - lastTouch.pointers[replacementIndex].x);
- } else {
- // Y looks too close. Find an older replacement point with a close X.
- int32_t smallestDeltaX = INT_MAX;
- for (uint32_t j = 0; j < pointerCount; j++) {
- int32_t deltaX = abs(x - lastTouch.pointers[j].x);
- if (deltaX < smallestDeltaX) {
- smallestDeltaX = deltaX;
- replacementIndex = j;
- }
- }
- distance = abs(y - lastTouch.pointers[replacementIndex].y);
- }
-
- // If replacing this pointer would correct a worse error than the previous ones
- // considered, then use this replacement instead.
- if (distance > badPointerDistance) {
- badPointerIndex = i;
- badPointerReplacementIndex = replacementIndex;
- badPointerDistance = distance;
- }
- }
-
- // Correct the jumpy pointer if one was found.
- if (badPointerIndex >= 0) {
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)",
- badPointerIndex,
- lastTouch.pointers[badPointerReplacementIndex].x,
- lastTouch.pointers[badPointerReplacementIndex].y);
-#endif
-
- currentTouch.pointers[badPointerIndex].x =
- lastTouch.pointers[badPointerReplacementIndex].x;
- currentTouch.pointers[badPointerIndex].y =
- lastTouch.pointers[badPointerReplacementIndex].y;
- jumpyTouchFilter.jumpyPointsDropped += 1;
- return true;
- }
- }
-
- jumpyTouchFilter.jumpyPointsDropped = 0;
- return false;
-}
-
-/* Special hack for devices that have bad screen data: aggregate and
- * compute averages of the coordinate data, to reduce the amount of
- * jitter seen by applications. */
-void InputDevice::TouchScreenState::applyAveragingTouchFilter() {
- for (uint32_t currentIndex = 0; currentIndex < currentTouch.pointerCount; currentIndex++) {
- uint32_t id = currentTouch.pointers[currentIndex].id;
- int32_t x = currentTouch.pointers[currentIndex].x;
- int32_t y = currentTouch.pointers[currentIndex].y;
- int32_t pressure = currentTouch.pointers[currentIndex].pressure;
-
- if (lastTouch.idBits.hasBit(id)) {
- // Pointer was down before and is still down now.
- // Compute average over history trace.
- uint32_t start = averagingTouchFilter.historyStart[id];
- uint32_t end = averagingTouchFilter.historyEnd[id];
-
- int64_t deltaX = x - averagingTouchFilter.historyData[end].pointers[id].x;
- int64_t deltaY = y - averagingTouchFilter.historyData[end].pointers[id].y;
- uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
-
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld",
- id, distance);
-#endif
-
- if (distance < AVERAGING_DISTANCE_LIMIT) {
- // Increment end index in preparation for recording new historical data.
- end += 1;
- if (end > AVERAGING_HISTORY_SIZE) {
- end = 0;
- }
-
- // If the end index has looped back to the start index then we have filled
- // the historical trace up to the desired size so we drop the historical
- // data at the start of the trace.
- if (end == start) {
- start += 1;
- if (start > AVERAGING_HISTORY_SIZE) {
- start = 0;
- }
- }
-
- // Add the raw data to the historical trace.
- averagingTouchFilter.historyStart[id] = start;
- averagingTouchFilter.historyEnd[id] = end;
- averagingTouchFilter.historyData[end].pointers[id].x = x;
- averagingTouchFilter.historyData[end].pointers[id].y = y;
- averagingTouchFilter.historyData[end].pointers[id].pressure = pressure;
-
- // Average over all historical positions in the trace by total pressure.
- int32_t averagedX = 0;
- int32_t averagedY = 0;
- int32_t totalPressure = 0;
- for (;;) {
- int32_t historicalX = averagingTouchFilter.historyData[start].pointers[id].x;
- int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].y;
- int32_t historicalPressure = averagingTouchFilter.historyData[start]
- .pointers[id].pressure;
-
- averagedX += historicalX * historicalPressure;
- averagedY += historicalY * historicalPressure;
- totalPressure += historicalPressure;
-
- if (start == end) {
- break;
- }
-
- start += 1;
- if (start > AVERAGING_HISTORY_SIZE) {
- start = 0;
- }
- }
-
- averagedX /= totalPressure;
- averagedY /= totalPressure;
-
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - "
- "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure,
- averagedX, averagedY);
-#endif
-
- currentTouch.pointers[currentIndex].x = averagedX;
- currentTouch.pointers[currentIndex].y = averagedY;
- } else {
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id);
-#endif
- }
- } else {
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id);
-#endif
- }
-
- // Reset pointer history.
- averagingTouchFilter.historyStart[id] = 0;
- averagingTouchFilter.historyEnd[id] = 0;
- averagingTouchFilter.historyData[0].pointers[id].x = x;
- averagingTouchFilter.historyData[0].pointers[id].y = y;
- averagingTouchFilter.historyData[0].pointers[id].pressure = pressure;
- }
-}
-
-bool InputDevice::TouchScreenState::isPointInsideDisplay(int32_t x, int32_t y) const {
- if (! parameters.xAxis.valid || ! parameters.yAxis.valid) {
- // Assume all points on a touch screen without valid axis parameters are
- // inside the display.
- return true;
- }
-
- return x >= parameters.xAxis.minValue
- && x <= parameters.xAxis.maxValue
- && y >= parameters.yAxis.minValue
- && y <= parameters.yAxis.maxValue;
-}
-
-const InputDevice::VirtualKey* InputDevice::TouchScreenState::findVirtualKeyHit() const {
- int32_t x = currentTouch.pointers[0].x;
- int32_t y = currentTouch.pointers[0].y;
- for (size_t i = 0; i < virtualKeys.size(); i++) {
- const InputDevice::VirtualKey& virtualKey = virtualKeys[i];
-
-#if DEBUG_VIRTUAL_KEYS
- LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
- "left=%d, top=%d, right=%d, bottom=%d",
- x, y,
- virtualKey.keyCode, virtualKey.scanCode,
- virtualKey.hitLeft, virtualKey.hitTop,
- virtualKey.hitRight, virtualKey.hitBottom);
-#endif
-
- if (virtualKey.isHit(x, y)) {
- return & virtualKey;
- }
- }
-
- return NULL;
-}
-
-
-// --- InputDevice::SingleTouchScreenState ---
-
-void InputDevice::SingleTouchScreenState::reset() {
- accumulator.clear();
- current.down = false;
- current.x = 0;
- current.y = 0;
- current.pressure = 0;
- current.size = 0;
-}
-
-
-// --- InputDevice::MultiTouchScreenState ---
-
-void InputDevice::MultiTouchScreenState::reset() {
- accumulator.clear();
-}
-
-} // namespace android
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index a55864be1c5e..b53f140f63fe 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -184,11 +184,6 @@ void InputDispatcher::dispatchOnce() {
// Run any deferred commands.
skipPoll |= runCommandsLockedInterruptible();
-
- // Wake up synchronization waiters, if needed.
- if (isFullySynchronizedLocked()) {
- mFullySynchronizedCondition.broadcast();
- }
} // release lock
// If we dispatched anything, don't poll just now. Wait for the next iteration.
@@ -560,6 +555,10 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
dispatchEntry->headMotionSample = NULL;
dispatchEntry->tailMotionSample = NULL;
+ if (dispatchEntry->isSyncTarget()) {
+ eventEntry->pendingSyncDispatches += 1;
+ }
+
// Handle the case where we could not stream a new motion sample because the consumer has
// already consumed the motion event (otherwise the corresponding dispatch entry would
// still be in the outbound queue for this connection). We set the head motion sample
@@ -789,6 +788,9 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
}
// Finished.
connection->outboundQueue.dequeueAtHead();
+ if (dispatchEntry->isSyncTarget()) {
+ decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+ }
mAllocator.releaseDispatchEntry(dispatchEntry);
} else {
// If the head is not in progress, then we must have already dequeued the in
@@ -854,6 +856,9 @@ void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime,
if (! connection->outboundQueue.isEmpty()) {
do {
DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
+ if (dispatchEntry->isSyncTarget()) {
+ decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+ }
mAllocator.releaseDispatchEntry(dispatchEntry);
} while (! connection->outboundQueue.isEmpty());
@@ -1097,7 +1102,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t
Connection* connection = mActiveConnections.itemAt(i);
if (! connection->outboundQueue.isEmpty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev;
- if (dispatchEntry->targetFlags & InputTarget::FLAG_SYNC) {
+ if (dispatchEntry->isSyncTarget()) {
if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
goto NoBatchingOrStreaming;
}
@@ -1148,11 +1153,11 @@ NoBatchingOrStreaming:;
}
int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) {
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
#if DEBUG_INBOUND_EVENT_DETAILS
LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
- "sync=%d, timeoutMillis=%d",
- event->getType(), injectorPid, injectorUid, sync, timeoutMillis);
+ "syncMode=%d, timeoutMillis=%d",
+ event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis);
#endif
nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
@@ -1167,6 +1172,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
injectedEntry->injectorPid = injectorPid;
injectedEntry->injectorUid = injectorUid;
+ if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
+ injectedEntry->injectionIsAsync = true;
+ }
+
wasEmpty = mInboundQueue.isEmpty();
mInboundQueue.enqueueAtTail(injectedEntry);
@@ -1180,37 +1189,59 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
{ // acquire lock
AutoMutex _l(mLock);
- for (;;) {
- injectionResult = injectedEntry->injectionResult;
- if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
- break;
- }
-
- nsecs_t remainingTimeout = endTime - now();
- if (remainingTimeout <= 0) {
- injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
- sync = false;
- break;
- }
-
- mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
- }
+ if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
+ injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ } else {
+ for (;;) {
+ injectionResult = injectedEntry->injectionResult;
+ if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
+ break;
+ }
- if (sync) {
- while (! isFullySynchronizedLocked()) {
nsecs_t remainingTimeout = endTime - now();
if (remainingTimeout <= 0) {
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Timed out waiting for injection result "
+ "to become available.");
+#endif
injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
break;
}
- mFullySynchronizedCondition.waitRelative(mLock, remainingTimeout);
+ mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
+ }
+
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
+ && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
+ while (injectedEntry->pendingSyncDispatches != 0) {
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Waiting for %d pending synchronous dispatches.",
+ injectedEntry->pendingSyncDispatches);
+#endif
+ nsecs_t remainingTimeout = endTime - now();
+ if (remainingTimeout <= 0) {
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Timed out waiting for pending synchronous "
+ "dispatches to finish.");
+#endif
+ injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ break;
+ }
+
+ mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout);
+ }
}
}
mAllocator.releaseEventEntry(injectedEntry);
} // release lock
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Finished with result %d. "
+ "injectorPid=%d, injectorUid=%d",
+ injectionResult, injectorPid, injectorUid);
+#endif
+
return injectionResult;
}
@@ -1222,13 +1253,35 @@ void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t inject
injectionResult, entry->injectorPid, entry->injectorUid);
#endif
+ if (entry->injectionIsAsync) {
+ // Log the outcome since the injector did not wait for the injection result.
+ switch (injectionResult) {
+ case INPUT_EVENT_INJECTION_SUCCEEDED:
+ LOGV("Asynchronous input event injection succeeded.");
+ break;
+ case INPUT_EVENT_INJECTION_FAILED:
+ LOGW("Asynchronous input event injection failed.");
+ break;
+ case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+ LOGW("Asynchronous input event injection permission denied.");
+ break;
+ case INPUT_EVENT_INJECTION_TIMED_OUT:
+ LOGW("Asynchronous input event injection timed out.");
+ break;
+ }
+ }
+
entry->injectionResult = injectionResult;
mInjectionResultAvailableCondition.broadcast();
}
}
-bool InputDispatcher::isFullySynchronizedLocked() {
- return mInboundQueue.isEmpty() && mActiveConnections.isEmpty();
+void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) {
+ entry->pendingSyncDispatches -= 1;
+
+ if (entry->isInjected() && entry->pendingSyncDispatches == 0) {
+ mInjectionSyncFinishedCondition.broadcast();
+ }
}
InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked(
@@ -1498,8 +1551,10 @@ void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t
entry->dispatchInProgress = false;
entry->eventTime = eventTime;
entry->injectionResult = INPUT_EVENT_INJECTION_PENDING;
+ entry->injectionIsAsync = false;
entry->injectorPid = -1;
entry->injectorUid = -1;
+ entry->pendingSyncDispatches = 0;
}
InputDispatcher::ConfigurationChangedEntry*
diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp
index e1d15a4ba977..ed4f07b277b7 100644
--- a/libs/ui/InputManager.cpp
+++ b/libs/ui/InputManager.cpp
@@ -81,34 +81,43 @@ status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChann
}
int32_t InputManager::injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) {
- return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, sync, timeoutMillis);
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
+ return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
}
void InputManager::preemptInputDispatch() {
mDispatcher->preemptInputDispatch();
}
-void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const {
- mReader->getCurrentInputConfiguration(outConfiguration);
+void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) {
+ mReader->getInputConfiguration(outConfiguration);
}
-int32_t InputManager::getScanCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t scanCode) const {
- return mReader->getCurrentScanCodeState(deviceId, deviceClasses, scanCode);
+status_t InputManager::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) {
+ return mReader->getInputDeviceInfo(deviceId, outDeviceInfo);
}
-int32_t InputManager::getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t keyCode) const {
- return mReader->getCurrentKeyCodeState(deviceId, deviceClasses, keyCode);
+void InputManager::getInputDeviceIds(Vector<int32_t>& outDeviceIds) {
+ mReader->getInputDeviceIds(outDeviceIds);
}
-int32_t InputManager::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const {
- return mReader->getCurrentSwitchState(deviceId, deviceClasses, sw);
+int32_t InputManager::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode) {
+ return mReader->getScanCodeState(deviceId, sourceMask, scanCode);
}
-bool InputManager::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const {
- return mReader->hasKeys(numCodes, keyCodes, outFlags);
+int32_t InputManager::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode) {
+ return mReader->getKeyCodeState(deviceId, sourceMask, keyCode);
+}
+
+int32_t InputManager::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) {
+ return mReader->getSwitchState(deviceId, sourceMask, sw);
+}
+
+bool InputManager::hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
+ return mReader->hasKeys(deviceId, sourceMask, numCodes, keyCodes, outFlags);
}
} // namespace android
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 30e391ff61d6..56e29778ee93 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -31,10 +31,6 @@
#include <limits.h>
#include <math.h>
-/** Amount that trackball needs to move in order to generate a key event. */
-#define TRACKBALL_MOVEMENT_THRESHOLD 6
-
-
namespace android {
// --- Static Functions ---
@@ -115,17 +111,21 @@ int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
return keyCode;
}
+static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) {
+ return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0;
+}
+
// --- InputReader ---
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputDispatcherInterface>& dispatcher) :
- mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher) {
+ mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher),
+ mGlobalMetaState(0) {
configureExcludedDevices();
- resetGlobalMetaState();
- resetDisplayProperties();
- updateExportedVirtualKeyState();
+ updateGlobalMetaState();
+ updateInputConfiguration();
}
InputReader::~InputReader() {
@@ -136,12 +136,7 @@ InputReader::~InputReader() {
void InputReader::loopOnce() {
RawEvent rawEvent;
- mEventHub->getEvent(& rawEvent.deviceId, & rawEvent.type, & rawEvent.scanCode,
- & rawEvent.keyCode, & rawEvent.flags, & rawEvent.value, & rawEvent.when);
-
- // Replace the event timestamp so it is in same timebase as java.lang.System.nanoTime()
- // and android.os.SystemClock.uptimeMillis() as expected by the rest of the system.
- rawEvent.when = systemTime(SYSTEM_TIME_MONOTONIC);
+ mEventHub->getEvent(& rawEvent);
#if DEBUG_RAW_EVENTS
LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d",
@@ -155,621 +150,1371 @@ void InputReader::loopOnce() {
void InputReader::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
- handleDeviceAdded(rawEvent);
+ addDevice(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED:
- handleDeviceRemoved(rawEvent);
+ removeDevice(rawEvent->when, rawEvent->deviceId);
break;
- case EV_SYN:
- handleSync(rawEvent);
+ default:
+ consumeEvent(rawEvent);
break;
+ }
+}
- case EV_KEY:
- handleKey(rawEvent);
- break;
+void InputReader::addDevice(nsecs_t when, int32_t deviceId) {
+ String8 name = mEventHub->getDeviceName(deviceId);
+ uint32_t classes = mEventHub->getDeviceClasses(deviceId);
- case EV_REL:
- handleRelativeMotion(rawEvent);
- break;
+ InputDevice* device = createDevice(deviceId, name, classes);
+ device->configure();
- case EV_ABS:
- handleAbsoluteMotion(rawEvent);
- break;
+ bool added = false;
+ { // acquire device registry writer lock
+ RWLock::AutoWLock _wl(mDeviceRegistryLock);
- case EV_SW:
- handleSwitch(rawEvent);
- break;
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ mDevices.add(deviceId, device);
+ added = true;
+ }
+ } // release device registry writer lock
+
+ if (! added) {
+ LOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
+ delete device;
+ return;
}
+
+ if (device->isIgnored()) {
+ LOGI("Device added: id=0x%x, name=%s (ignored non-input device)",
+ deviceId, name.string());
+ } else {
+ LOGI("Device added: id=0x%x, name=%s, sources=%08x",
+ deviceId, name.string(), device->getSources());
+ }
+
+ handleConfigurationChanged(when);
}
-void InputReader::handleDeviceAdded(const RawEvent* rawEvent) {
- InputDevice* device = getDevice(rawEvent->deviceId);
- if (device) {
- LOGW("Ignoring spurious device added event for deviceId %d.", rawEvent->deviceId);
+void InputReader::removeDevice(nsecs_t when, int32_t deviceId) {
+ bool removed = false;
+ InputDevice* device = NULL;
+ { // acquire device registry writer lock
+ RWLock::AutoWLock _wl(mDeviceRegistryLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ device = mDevices.valueAt(deviceIndex);
+ mDevices.removeItemsAt(deviceIndex, 1);
+ removed = true;
+ }
+ } // release device registry writer lock
+
+ if (! removed) {
+ LOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
return;
}
- addDevice(rawEvent->when, rawEvent->deviceId);
+ device->reset();
+
+ if (device->isIgnored()) {
+ LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)",
+ device->getId(), device->getName().string());
+ } else {
+ LOGI("Device removed: id=0x%x, name=%s, sources=%08x",
+ device->getId(), device->getName().string(), device->getSources());
+ }
+
+ delete device;
+
+ handleConfigurationChanged(when);
}
-void InputReader::handleDeviceRemoved(const RawEvent* rawEvent) {
- InputDevice* device = getDevice(rawEvent->deviceId);
- if (! device) {
- LOGW("Ignoring spurious device removed event for deviceId %d.", rawEvent->deviceId);
- return;
+InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) {
+ InputDevice* device = new InputDevice(this, deviceId, name);
+
+ const int32_t associatedDisplayId = 0; // FIXME: hardcoded for current single-display devices
+
+ // Switch-like devices.
+ if (classes & INPUT_DEVICE_CLASS_SWITCH) {
+ device->addMapper(new SwitchInputMapper(device));
}
- removeDevice(rawEvent->when, device);
+ // Keyboard-like devices.
+ uint32_t keyboardSources = 0;
+ int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
+ if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+ keyboardSources |= AINPUT_SOURCE_KEYBOARD;
+ }
+ if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
+ keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
+ }
+ 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,
+ associatedDisplayId, keyboardSources, keyboardType));
+ }
+
+ // Trackball-like devices.
+ if (classes & INPUT_DEVICE_CLASS_TRACKBALL) {
+ device->addMapper(new TrackballInputMapper(device, associatedDisplayId));
+ }
+
+ // Touchscreen-like devices.
+ if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT) {
+ device->addMapper(new MultiTouchInputMapper(device, associatedDisplayId));
+ } else if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN) {
+ device->addMapper(new SingleTouchInputMapper(device, associatedDisplayId));
+ }
+
+ return device;
}
-void InputReader::handleSync(const RawEvent* rawEvent) {
- InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId);
- if (! device) return;
+void InputReader::consumeEvent(const RawEvent* rawEvent) {
+ int32_t deviceId = rawEvent->deviceId;
- if (rawEvent->scanCode == SYN_MT_REPORT) {
- // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
- // We drop pointers with pressure <= 0 since that indicates they are not down.
- if (device->isMultiTouchScreen()) {
- uint32_t pointerIndex = device->multiTouchScreen.accumulator.pointerCount;
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
- if (device->multiTouchScreen.accumulator.pointers[pointerIndex].fields) {
- if (pointerIndex == MAX_POINTERS) {
- LOGW("MultiTouch device driver returned more than maximum of %d pointers.",
- MAX_POINTERS);
- } else {
- pointerIndex += 1;
- device->multiTouchScreen.accumulator.pointerCount = pointerIndex;
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ LOGW("Discarding event for unknown deviceId %d.", deviceId);
+ return;
+ }
+
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (device->isIgnored()) {
+ //LOGD("Discarding event for ignored deviceId %d.", deviceId);
+ return;
+ }
+
+ device->process(rawEvent);
+ } // release device registry reader lock
+}
+
+void InputReader::handleConfigurationChanged(nsecs_t when) {
+ // Reset global meta state because it depends on the list of all configured devices.
+ updateGlobalMetaState();
+
+ // Update input configuration.
+ updateInputConfiguration();
+
+ // Enqueue configuration changed.
+ mDispatcher->notifyConfigurationChanged(when);
+}
+
+void InputReader::configureExcludedDevices() {
+ Vector<String8> excludedDeviceNames;
+ mPolicy->getExcludedDeviceNames(excludedDeviceNames);
+
+ for (size_t i = 0; i < excludedDeviceNames.size(); i++) {
+ mEventHub->addExcludedDevice(excludedDeviceNames[i]);
+ }
+}
+
+void InputReader::updateGlobalMetaState() {
+ { // acquire state lock
+ AutoMutex _l(mStateLock);
+
+ mGlobalMetaState = 0;
+
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ mGlobalMetaState |= device->getMetaState();
+ }
+ } // release device registry reader lock
+ } // release state lock
+}
+
+int32_t InputReader::getGlobalMetaState() {
+ { // acquire state lock
+ AutoMutex _l(mStateLock);
+
+ return mGlobalMetaState;
+ } // release state lock
+}
+
+void InputReader::updateInputConfiguration() {
+ { // acquire state lock
+ AutoMutex _l(mStateLock);
+
+ int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH;
+ int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS;
+ int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV;
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ InputDeviceInfo deviceInfo;
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ device->getDeviceInfo(& deviceInfo);
+ uint32_t sources = deviceInfo.getSources();
+
+ if ((sources & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN) {
+ touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER;
+ }
+ if ((sources & AINPUT_SOURCE_TRACKBALL) == AINPUT_SOURCE_TRACKBALL) {
+ navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL;
+ } else if ((sources & AINPUT_SOURCE_DPAD) == AINPUT_SOURCE_DPAD) {
+ navigationConfig = InputConfiguration::NAVIGATION_DPAD;
+ }
+ if (deviceInfo.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
+ keyboardConfig = InputConfiguration::KEYBOARD_QWERTY;
}
}
+ } // release device registry reader lock
+
+ mInputConfiguration.touchScreen = touchScreenConfig;
+ mInputConfiguration.keyboard = keyboardConfig;
+ mInputConfiguration.navigation = navigationConfig;
+ } // release state lock
+}
+
+void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) {
+ { // acquire state lock
+ AutoMutex _l(mStateLock);
+
+ *outConfiguration = mInputConfiguration;
+ } // release state lock
+}
+
+status_t InputReader::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) {
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
- device->multiTouchScreen.accumulator.pointers[pointerIndex].clear();
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ return NAME_NOT_FOUND;
}
- } else if (rawEvent->scanCode == SYN_REPORT) {
- // General Sync: The driver has returned all data for the current event update.
- if (device->isMultiTouchScreen()) {
- if (device->multiTouchScreen.accumulator.isDirty()) {
- onMultiTouchScreenStateChanged(rawEvent->when, device);
- device->multiTouchScreen.accumulator.clear();
+
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (device->isIgnored()) {
+ return NAME_NOT_FOUND;
+ }
+
+ device->getDeviceInfo(outDeviceInfo);
+ return OK;
+ } // release device registy reader lock
+}
+
+void InputReader::getInputDeviceIds(Vector<int32_t>& outDeviceIds) {
+ outDeviceIds.clear();
+
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ size_t numDevices = mDevices.size();
+ for (size_t i = 0; i < numDevices; i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (! device->isIgnored()) {
+ outDeviceIds.add(device->getId());
}
- } else if (device->isSingleTouchScreen()) {
- if (device->singleTouchScreen.accumulator.isDirty()) {
- onSingleTouchScreenStateChanged(rawEvent->when, device);
- device->singleTouchScreen.accumulator.clear();
+ }
+ } // release device registy reader lock
+}
+
+int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode) {
+ return getState(deviceId, sourceMask, keyCode, & InputDevice::getKeyCodeState);
+}
+
+int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode) {
+ return getState(deviceId, sourceMask, scanCode, & InputDevice::getScanCodeState);
+}
+
+int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) {
+ return getState(deviceId, sourceMask, switchCode, & InputDevice::getSwitchState);
+}
+
+int32_t InputReader::getState(int32_t deviceId, uint32_t sourceMask, int32_t code,
+ GetStateFunc getStateFunc) {
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ int32_t result = AKEY_STATE_UNKNOWN;
+ if (deviceId >= 0) {
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result = (device->*getStateFunc)(sourceMask, code);
+ }
+ }
+ } else {
+ size_t numDevices = mDevices.size();
+ for (size_t i = 0; i < numDevices; i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result = (device->*getStateFunc)(sourceMask, code);
+ if (result >= AKEY_STATE_DOWN) {
+ return result;
+ }
+ }
}
}
+ return result;
+ } // release device registy reader lock
+}
+
+bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
+ memset(outFlags, 0, numCodes);
+ return markSupportedKeyCodes(deviceId, sourceMask, numCodes, keyCodes, outFlags);
+}
- if (device->trackball.accumulator.isDirty()) {
- onTrackballStateChanged(rawEvent->when, device);
- device->trackball.accumulator.clear();
+bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+ bool result = false;
+ if (deviceId >= 0) {
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result = device->markSupportedKeyCodes(sourceMask,
+ numCodes, keyCodes, outFlags);
+ }
+ }
+ } else {
+ size_t numDevices = mDevices.size();
+ for (size_t i = 0; i < numDevices; i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result |= device->markSupportedKeyCodes(sourceMask,
+ numCodes, keyCodes, outFlags);
+ }
+ }
}
- }
+ return result;
+ } // release device registy reader lock
}
-void InputReader::handleKey(const RawEvent* rawEvent) {
- InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId);
- if (! device) return;
- bool down = rawEvent->value != 0;
- int32_t scanCode = rawEvent->scanCode;
+// --- InputReaderThread ---
- if (device->isSingleTouchScreen()) {
- switch (rawEvent->scanCode) {
- case BTN_TOUCH:
- device->singleTouchScreen.accumulator.fields |=
- InputDevice::SingleTouchScreenState::Accumulator::FIELD_BTN_TOUCH;
- device->singleTouchScreen.accumulator.btnTouch = down;
- return;
- }
+InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
+ Thread(/*canCallJava*/ true), mReader(reader) {
+}
+
+InputReaderThread::~InputReaderThread() {
+}
+
+bool InputReaderThread::threadLoop() {
+ mReader->loopOnce();
+ return true;
+}
+
+
+// --- InputDevice ---
+
+InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name) :
+ mContext(context), mId(id), mName(name), mSources(0) {
+}
+
+InputDevice::~InputDevice() {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ delete mMappers[i];
}
+ mMappers.clear();
+}
- if (device->isTrackball()) {
- switch (rawEvent->scanCode) {
- case BTN_MOUSE:
- device->trackball.accumulator.fields |=
- InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE;
- device->trackball.accumulator.btnMouse = down;
+void InputDevice::addMapper(InputMapper* mapper) {
+ mMappers.add(mapper);
+}
- // Process the trackball change now since we may not receive a sync immediately.
- onTrackballStateChanged(rawEvent->when, device);
- device->trackball.accumulator.clear();
- return;
- }
+void InputDevice::configure() {
+ mSources = 0;
+
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->configure();
+ mSources |= mapper->getSources();
+ }
+}
+
+void InputDevice::reset() {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->reset();
}
+}
- if (device->isKeyboard()) {
- int32_t keyCode = rawEvent->keyCode;
- onKey(rawEvent->when, device, down, keyCode, scanCode, rawEvent->flags);
+void InputDevice::process(const RawEvent* rawEvent) {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->process(rawEvent);
}
}
-void InputReader::handleRelativeMotion(const RawEvent* rawEvent) {
- InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId);
- if (! device) return;
+void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
+ outDeviceInfo->initialize(mId, mName);
- if (device->isTrackball()) {
- switch (rawEvent->scanCode) {
- case REL_X:
- device->trackball.accumulator.fields |=
- InputDevice::TrackballState::Accumulator::FIELD_REL_X;
- device->trackball.accumulator.relX = rawEvent->value;
- break;
- case REL_Y:
- device->trackball.accumulator.fields |=
- InputDevice::TrackballState::Accumulator::FIELD_REL_Y;
- device->trackball.accumulator.relY = rawEvent->value;
- break;
- }
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->populateDeviceInfo(outDeviceInfo);
}
}
-void InputReader::handleAbsoluteMotion(const RawEvent* rawEvent) {
- InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId);
- if (! device) return;
+int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState);
+}
- if (device->isMultiTouchScreen()) {
- uint32_t pointerIndex = device->multiTouchScreen.accumulator.pointerCount;
- InputDevice::MultiTouchScreenState::Accumulator::Pointer* pointer =
- & device->multiTouchScreen.accumulator.pointers[pointerIndex];
+int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ return getState(sourceMask, scanCode, & InputMapper::getScanCodeState);
+}
- switch (rawEvent->scanCode) {
- case ABS_MT_POSITION_X:
- pointer->fields |=
- InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_X;
- pointer->absMTPositionX = rawEvent->value;
- break;
- case ABS_MT_POSITION_Y:
- pointer->fields |=
- InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_Y;
- pointer->absMTPositionY = rawEvent->value;
- break;
- case ABS_MT_TOUCH_MAJOR:
- pointer->fields |=
- InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MAJOR;
- pointer->absMTTouchMajor = rawEvent->value;
- break;
- case ABS_MT_TOUCH_MINOR:
- pointer->fields |=
- InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MINOR;
- pointer->absMTTouchMinor = rawEvent->value;
- break;
- case ABS_MT_WIDTH_MAJOR:
- pointer->fields |=
- InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
- pointer->absMTWidthMajor = rawEvent->value;
- break;
- case ABS_MT_WIDTH_MINOR:
- pointer->fields |=
- InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MINOR;
- pointer->absMTWidthMinor = rawEvent->value;
- break;
- case ABS_MT_ORIENTATION:
- pointer->fields |=
- InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_ORIENTATION;
- pointer->absMTOrientation = rawEvent->value;
- break;
- case ABS_MT_TRACKING_ID:
- pointer->fields |=
- InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TRACKING_ID;
- pointer->absMTTrackingId = rawEvent->value;
- break;
+int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+ return getState(sourceMask, switchCode, & InputMapper::getSwitchState);
+}
+
+int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
+ int32_t result = AKEY_STATE_UNKNOWN;
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+ result = (mapper->*getStateFunc)(sourceMask, code);
+ if (result >= AKEY_STATE_DOWN) {
+ return result;
+ }
}
- } else if (device->isSingleTouchScreen()) {
- switch (rawEvent->scanCode) {
- case ABS_X:
- device->singleTouchScreen.accumulator.fields |=
- InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X;
- device->singleTouchScreen.accumulator.absX = rawEvent->value;
- break;
- case ABS_Y:
- device->singleTouchScreen.accumulator.fields |=
- InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y;
- device->singleTouchScreen.accumulator.absY = rawEvent->value;
- break;
- case ABS_PRESSURE:
- device->singleTouchScreen.accumulator.fields |=
- InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE;
- device->singleTouchScreen.accumulator.absPressure = rawEvent->value;
- break;
- case ABS_TOOL_WIDTH:
- device->singleTouchScreen.accumulator.fields |=
- InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH;
- device->singleTouchScreen.accumulator.absToolWidth = rawEvent->value;
- break;
+ }
+ return result;
+}
+
+bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ bool result = false;
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+ result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
}
}
+ return result;
+}
+
+int32_t InputDevice::getMetaState() {
+ int32_t result = 0;
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ result |= mapper->getMetaState();
+ }
+ return result;
+}
+
+
+// --- InputMapper ---
+
+InputMapper::InputMapper(InputDevice* device) :
+ mDevice(device), mContext(device->getContext()) {
+}
+
+InputMapper::~InputMapper() {
+}
+
+void InputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ info->addSource(getSources());
+}
+
+void InputMapper::configure() {
+}
+
+void InputMapper::reset() {
+}
+
+int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+ return AKEY_STATE_UNKNOWN;
+}
+
+bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ return false;
+}
+
+int32_t InputMapper::getMetaState() {
+ return 0;
+}
+
+bool InputMapper::applyStandardPolicyActions(nsecs_t when, int32_t policyActions) {
+ if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) {
+ getDispatcher()->notifyAppSwitchComing(when);
+ }
+
+ return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
}
-void InputReader::handleSwitch(const RawEvent* rawEvent) {
- InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId);
- if (! device) return;
- onSwitch(rawEvent->when, device, rawEvent->scanCode, rawEvent->value);
+// --- SwitchInputMapper ---
+
+SwitchInputMapper::SwitchInputMapper(InputDevice* device) :
+ InputMapper(device) {
}
-void InputReader::onKey(nsecs_t when, InputDevice* device,
- bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) {
- /* Refresh display properties so we can rotate key codes according to display orientation */
+SwitchInputMapper::~SwitchInputMapper() {
+}
- if (! refreshDisplayProperties()) {
- return;
+uint32_t SwitchInputMapper::getSources() {
+ return 0;
+}
+
+void SwitchInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_SW:
+ processSwitch(rawEvent->when, rawEvent->scanCode, rawEvent->value);
+ break;
}
+}
- /* Update device state */
+void SwitchInputMapper::processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) {
+ uint32_t policyFlags = 0;
+ int32_t policyActions = getPolicy()->interceptSwitch(
+ when, switchCode, switchValue, policyFlags);
- int32_t oldMetaState = device->keyboard.current.metaState;
- int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState);
- if (oldMetaState != newMetaState) {
- device->keyboard.current.metaState = newMetaState;
- resetGlobalMetaState();
+ applyStandardPolicyActions(when, policyActions);
+}
+
+int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+ return getEventHub()->getSwitchState(getDeviceId(), switchCode);
+}
+
+
+// --- KeyboardInputMapper ---
+
+KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId,
+ uint32_t sources, int32_t keyboardType) :
+ InputMapper(device), mAssociatedDisplayId(associatedDisplayId), mSources(sources),
+ mKeyboardType(keyboardType) {
+ initialize();
+}
+
+KeyboardInputMapper::~KeyboardInputMapper() {
+}
+
+void KeyboardInputMapper::initialize() {
+ mMetaState = AMETA_NONE;
+ mDownTime = 0;
+}
+
+uint32_t KeyboardInputMapper::getSources() {
+ return mSources;
+}
+
+void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ info->setKeyboardType(mKeyboardType);
+}
+
+void KeyboardInputMapper::reset() {
+ // Synthesize key up event on reset if keys are currently down.
+ while (! mKeyDowns.isEmpty()) {
+ const KeyDown& keyDown = mKeyDowns.top();
+ nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+ processKey(when, false, keyDown.keyCode, keyDown.scanCode, 0);
+ }
+
+ InputMapper::reset();
+
+ // Reinitialize.
+ initialize();
+ getContext()->updateGlobalMetaState();
+}
+
+void KeyboardInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_KEY: {
+ int32_t scanCode = rawEvent->scanCode;
+ if (isKeyboardOrGamepadKey(scanCode)) {
+ processKey(rawEvent->when, rawEvent->value != 0, rawEvent->keyCode, scanCode,
+ rawEvent->flags);
+ }
+ break;
+ }
}
+}
- // FIXME if we send a down event about a rotated key press we should ensure that we send
- // a corresponding up event about the rotated key press even if the orientation
- // has changed in the meantime
- keyCode = rotateKeyCode(keyCode, mDisplayOrientation);
+bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
+ return scanCode < BTN_MOUSE
+ || scanCode >= KEY_OK
+ || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI);
+}
+void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode,
+ uint32_t policyFlags) {
if (down) {
- device->keyboard.current.downTime = when;
+ // Rotate key codes according to orientation.
+ if (mAssociatedDisplayId >= 0) {
+ int32_t orientation;
+ if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
+ return;
+ }
+
+ keyCode = rotateKeyCode(keyCode, orientation);
+ }
+
+ // Add key down.
+ ssize_t keyDownIndex = findKeyDown(scanCode);
+ if (keyDownIndex >= 0) {
+ // key repeat, be sure to use same keycode as before in case of rotation
+ keyCode = mKeyDowns.top().keyCode;
+ } else {
+ // key down
+ mKeyDowns.push();
+ KeyDown& keyDown = mKeyDowns.editTop();
+ keyDown.keyCode = keyCode;
+ keyDown.scanCode = scanCode;
+ }
+ } else {
+ // Remove key down.
+ ssize_t keyDownIndex = findKeyDown(scanCode);
+ if (keyDownIndex >= 0) {
+ // key up, be sure to use same keycode as before in case of rotation
+ keyCode = mKeyDowns.top().keyCode;
+ mKeyDowns.removeAt(size_t(keyDownIndex));
+ } else {
+ // key was not actually down
+ LOGI("Dropping key up from device %s because the key was not down. "
+ "keyCode=%d, scanCode=%d",
+ getDeviceName().string(), keyCode, scanCode);
+ return;
+ }
}
- /* Apply policy */
+ int32_t oldMetaState = mMetaState;
+ int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState);
+ if (oldMetaState != newMetaState) {
+ mMetaState = newMetaState;
+ getContext()->updateGlobalMetaState();
+ }
- int32_t policyActions = mPolicy->interceptKey(when, device->id,
- down, keyCode, scanCode, policyFlags);
+ /* Apply policy. */
- if (! applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) {
+ int32_t policyActions = getPolicy()->interceptKey(when,
+ getDeviceId(), down, keyCode, scanCode, policyFlags);
+
+ if (! applyStandardPolicyActions(when, policyActions)) {
return; // event dropped
}
- /* Enqueue key event for dispatch */
+ /* Enqueue key event for dispatch. */
int32_t keyEventAction;
if (down) {
- device->keyboard.current.downTime = when;
+ mDownTime = when;
keyEventAction = AKEY_EVENT_ACTION_DOWN;
} else {
keyEventAction = AKEY_EVENT_ACTION_UP;
}
int32_t keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM;
- if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) {
+ if (policyFlags & POLICY_FLAG_WOKE_HERE) {
keyEventFlags = keyEventFlags | AKEY_EVENT_FLAG_WOKE_HERE;
}
- mDispatcher->notifyKey(when, device->id, AINPUT_SOURCE_KEYBOARD, policyFlags,
+ getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
keyEventAction, keyEventFlags, keyCode, scanCode,
- device->keyboard.current.metaState,
- device->keyboard.current.downTime);
+ mMetaState, mDownTime);
}
-void InputReader::onSwitch(nsecs_t when, InputDevice* device, int32_t switchCode,
- int32_t switchValue) {
- int32_t policyActions = mPolicy->interceptSwitch(when, switchCode, switchValue);
+ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
+ size_t n = mKeyDowns.size();
+ for (size_t i = 0; i < n; i++) {
+ if (mKeyDowns[i].scanCode == scanCode) {
+ return i;
+ }
+ }
+ return -1;
+}
- uint32_t policyFlags = 0;
- applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags);
+int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ return getEventHub()->getKeyCodeState(getDeviceId(), keyCode);
}
-void InputReader::onMultiTouchScreenStateChanged(nsecs_t when,
- InputDevice* device) {
- static const uint32_t REQUIRED_FIELDS =
- InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_X
- | InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_POSITION_Y
- | InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MAJOR
- | InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
+int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+}
- /* Refresh display properties so we can map touch screen coords into display coords */
+bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags);
+}
- if (! refreshDisplayProperties()) {
- return;
+int32_t KeyboardInputMapper::getMetaState() {
+ return mMetaState;
+}
+
+
+// --- TrackballInputMapper ---
+
+TrackballInputMapper::TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+ InputMapper(device), mAssociatedDisplayId(associatedDisplayId) {
+ mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+ mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+ mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+ mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+
+ initialize();
+}
+
+TrackballInputMapper::~TrackballInputMapper() {
+}
+
+uint32_t TrackballInputMapper::getSources() {
+ return AINPUT_SOURCE_TRACKBALL;
+}
+
+void TrackballInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ info->addMotionRange(AINPUT_MOTION_RANGE_X, -1.0f, 1.0f, 0.0f, mXScale);
+ info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale);
+}
+
+void TrackballInputMapper::initialize() {
+ mAccumulator.clear();
+
+ mDown = false;
+ mDownTime = 0;
+}
+
+void TrackballInputMapper::reset() {
+ // Synthesize trackball button up event on reset if trackball button is currently down.
+ if (mDown) {
+ nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+ mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
+ mAccumulator.btnMouse = false;
+ sync(when);
}
- /* Update device state */
+ InputMapper::reset();
- InputDevice::MultiTouchScreenState* in = & device->multiTouchScreen;
- InputDevice::TouchData* out = & device->touchScreen.currentTouch;
+ // Reinitialize.
+ initialize();
+}
- uint32_t inCount = in->accumulator.pointerCount;
- uint32_t outCount = 0;
- bool havePointerIds = true;
+void TrackballInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_KEY:
+ switch (rawEvent->scanCode) {
+ case BTN_MOUSE:
+ mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
+ mAccumulator.btnMouse = rawEvent->value != 0;
- out->clear();
+ sync(rawEvent->when);
+ mAccumulator.clear();
+ break;
+ }
+ break;
- for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) {
- uint32_t fields = in->accumulator.pointers[inIndex].fields;
+ case EV_REL:
+ switch (rawEvent->scanCode) {
+ case REL_X:
+ mAccumulator.fields |= Accumulator::FIELD_REL_X;
+ mAccumulator.relX = rawEvent->value;
+ break;
+ case REL_Y:
+ mAccumulator.fields |= Accumulator::FIELD_REL_Y;
+ mAccumulator.relY = rawEvent->value;
+ break;
+ }
+ break;
- if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) {
-#if DEBUG_POINTERS
- LOGD("Pointers: Missing required multitouch pointer fields: index=%d, fields=%d",
- inIndex, fields);
- continue;
-#endif
+ case EV_SYN:
+ switch (rawEvent->scanCode) {
+ case SYN_REPORT:
+ if (mAccumulator.isDirty()) {
+ sync(rawEvent->when);
+ mAccumulator.clear();
+ }
+ break;
}
+ break;
+ }
+}
- if (in->accumulator.pointers[inIndex].absMTTouchMajor <= 0) {
- // Pointer is not down. Drop it.
- continue;
+void TrackballInputMapper::sync(nsecs_t when) {
+ /* Get display properties so for rotation based on display orientation. */
+
+ int32_t orientation;
+ if (mAssociatedDisplayId >= 0) {
+ if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
+ return;
}
+ } else {
+ orientation = InputReaderPolicyInterface::ROTATION_0;
+ }
- out->pointers[outCount].x = in->accumulator.pointers[inIndex].absMTPositionX;
- out->pointers[outCount].y = in->accumulator.pointers[inIndex].absMTPositionY;
+ /* Update saved trackball state */
- out->pointers[outCount].touchMajor = in->accumulator.pointers[inIndex].absMTTouchMajor;
- out->pointers[outCount].touchMinor = (fields
- & InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_TOUCH_MINOR) != 0
- ? in->accumulator.pointers[inIndex].absMTTouchMinor
- : in->accumulator.pointers[inIndex].absMTTouchMajor;
+ uint32_t fields = mAccumulator.fields;
+ bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE;
- out->pointers[outCount].toolMajor = in->accumulator.pointers[inIndex].absMTWidthMajor;
- out->pointers[outCount].toolMinor = (fields
- & InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_WIDTH_MINOR) != 0
- ? in->accumulator.pointers[inIndex].absMTWidthMinor
- : in->accumulator.pointers[inIndex].absMTWidthMajor;
+ if (downChanged) {
+ if (mAccumulator.btnMouse) {
+ mDown = true;
+ mDownTime = when;
+ } else {
+ mDown = false;
+ }
+ }
- out->pointers[outCount].orientation = (fields
- & InputDevice::MultiTouchScreenState::Accumulator::FIELD_ABS_MT_ORIENTATION) != 0
- ? in->accumulator.pointers[inIndex].absMTOrientation : 0;
+ /* Apply policy */
- // Derive an approximation of pressure and size.
- // FIXME assignment of pressure may be incorrect, probably better to let
- // pressure = touch / width. Later on we pass width to MotionEvent as a size, which
- // isn't quite right either. Should be using touch for that.
- out->pointers[outCount].pressure = in->accumulator.pointers[inIndex].absMTTouchMajor;
- out->pointers[outCount].size = in->accumulator.pointers[inIndex].absMTWidthMajor;
+ uint32_t policyFlags = 0;
+ int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags);
- if (havePointerIds) {
- if (fields & InputDevice::MultiTouchScreenState::Accumulator::
- FIELD_ABS_MT_TRACKING_ID) {
- uint32_t id = uint32_t(in->accumulator.pointers[inIndex].absMTTrackingId);
+ if (! applyStandardPolicyActions(when, policyActions)) {
+ return; // event dropped
+ }
- if (id > MAX_POINTER_ID) {
-#if DEBUG_POINTERS
- LOGD("Pointers: Ignoring driver provided pointer id %d because "
- "it is larger than max supported id %d for optimizations",
- id, MAX_POINTER_ID);
-#endif
- havePointerIds = false;
- }
- else {
- out->pointers[outCount].id = id;
- out->idToIndex[id] = outCount;
- out->idBits.markBit(id);
- }
- } else {
- havePointerIds = false;
- }
- }
+ /* Enqueue motion event for dispatch. */
- outCount += 1;
+ int32_t motionEventAction;
+ if (downChanged) {
+ motionEventAction = mDown ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+ } else {
+ motionEventAction = AMOTION_EVENT_ACTION_MOVE;
+ }
+
+ int32_t pointerId = 0;
+ PointerCoords pointerCoords;
+ pointerCoords.x = fields & Accumulator::FIELD_REL_X
+ ? mAccumulator.relX * mXScale : 0;
+ pointerCoords.y = fields & Accumulator::FIELD_REL_Y
+ ? mAccumulator.relY * mYScale : 0;
+ pointerCoords.pressure = 1.0f; // XXX Consider making this 1.0f if down, 0 otherwise.
+ pointerCoords.size = 0;
+ pointerCoords.touchMajor = 0;
+ pointerCoords.touchMinor = 0;
+ pointerCoords.toolMajor = 0;
+ pointerCoords.toolMinor = 0;
+ pointerCoords.orientation = 0;
+
+ float temp;
+ switch (orientation) {
+ case InputReaderPolicyInterface::ROTATION_90:
+ temp = pointerCoords.x;
+ pointerCoords.x = pointerCoords.y;
+ pointerCoords.y = - temp;
+ break;
+
+ case InputReaderPolicyInterface::ROTATION_180:
+ pointerCoords.x = - pointerCoords.x;
+ pointerCoords.y = - pointerCoords.y;
+ break;
+
+ case InputReaderPolicyInterface::ROTATION_270:
+ temp = pointerCoords.x;
+ pointerCoords.x = - pointerCoords.y;
+ pointerCoords.y = temp;
+ break;
}
- out->pointerCount = outCount;
+ int32_t metaState = mContext->getGlobalMetaState();
+ getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, policyFlags,
+ motionEventAction, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ 1, & pointerId, & pointerCoords, mXPrecision, mYPrecision, mDownTime);
+}
+
- onTouchScreenChanged(when, device, havePointerIds);
+// --- TouchInputMapper ---
+
+TouchInputMapper::TouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+ InputMapper(device), mAssociatedDisplayId(associatedDisplayId),
+ mSurfaceOrientation(-1), mSurfaceWidth(-1), mSurfaceHeight(-1) {
+ initialize();
}
-void InputReader::onSingleTouchScreenStateChanged(nsecs_t when,
- InputDevice* device) {
- /* Refresh display properties so we can map touch screen coords into display coords */
+TouchInputMapper::~TouchInputMapper() {
+}
- if (! refreshDisplayProperties()) {
- return;
+uint32_t TouchInputMapper::getSources() {
+ return mAssociatedDisplayId >= 0 ? AINPUT_SOURCE_TOUCHSCREEN : AINPUT_SOURCE_TOUCHPAD;
+}
+
+void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ // FIXME: Should ensure the surface information is up to date so that orientation changes
+ // are noticed immediately. Unfortunately we will need to add some extra locks here
+ // to prevent race conditions.
+ // configureSurface();
+
+ info->addMotionRange(AINPUT_MOTION_RANGE_X, mOrientedRanges.x);
+ info->addMotionRange(AINPUT_MOTION_RANGE_Y, mOrientedRanges.y);
+ info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, mOrientedRanges.pressure);
+ info->addMotionRange(AINPUT_MOTION_RANGE_SIZE, mOrientedRanges.size);
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR, mOrientedRanges.touchMajor);
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR, mOrientedRanges.touchMinor);
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR, mOrientedRanges.toolMajor);
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR, mOrientedRanges.toolMinor);
+ info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION, mOrientedRanges.orientation);
+}
+
+void TouchInputMapper::initialize() {
+ mLastTouch.clear();
+ mDownTime = 0;
+ mCurrentVirtualKey.down = false;
+
+ for (uint32_t i = 0; i < MAX_POINTERS; i++) {
+ mAveragingTouchFilter.historyStart[i] = 0;
+ mAveragingTouchFilter.historyEnd[i] = 0;
}
- /* Update device state */
+ mJumpyTouchFilter.jumpyPointsDropped = 0;
+}
- InputDevice::SingleTouchScreenState* in = & device->singleTouchScreen;
- InputDevice::TouchData* out = & device->touchScreen.currentTouch;
+void TouchInputMapper::configure() {
+ InputMapper::configure();
- uint32_t fields = in->accumulator.fields;
+ // Configure basic parameters.
+ mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents();
+ mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents();
+ mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
- if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_BTN_TOUCH) {
- in->current.down = in->accumulator.btnTouch;
+ // Configure absolute axis information.
+ configureAxes();
+
+ // Configure pressure factors.
+ if (mAxes.pressure.valid) {
+ mPressureOrigin = mAxes.pressure.minValue;
+ mPressureScale = 1.0f / mAxes.pressure.getRange();
+ } else {
+ mPressureOrigin = 0;
+ mPressureScale = 1.0f;
}
- if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X) {
- in->current.x = in->accumulator.absX;
+ mOrientedRanges.pressure.min = 0.0f;
+ mOrientedRanges.pressure.max = 1.0f;
+ mOrientedRanges.pressure.flat = 0.0f;
+ mOrientedRanges.pressure.fuzz = mPressureScale;
+
+ // Configure size factors.
+ if (mAxes.size.valid) {
+ mSizeOrigin = mAxes.size.minValue;
+ mSizeScale = 1.0f / mAxes.size.getRange();
+ } else {
+ mSizeOrigin = 0;
+ mSizeScale = 1.0f;
}
- if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y) {
- in->current.y = in->accumulator.absY;
+ mOrientedRanges.size.min = 0.0f;
+ mOrientedRanges.size.max = 1.0f;
+ mOrientedRanges.size.flat = 0.0f;
+ mOrientedRanges.size.fuzz = mSizeScale;
+
+ // Configure orientation factors.
+ if (mAxes.orientation.valid && mAxes.orientation.maxValue > 0) {
+ mOrientationScale = float(M_PI_2) / mAxes.orientation.maxValue;
+ } else {
+ mOrientationScale = 0.0f;
}
- if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE) {
- in->current.pressure = in->accumulator.absPressure;
+ mOrientedRanges.orientation.min = - M_PI_2;
+ mOrientedRanges.orientation.max = M_PI_2;
+ mOrientedRanges.orientation.flat = 0;
+ mOrientedRanges.orientation.fuzz = mOrientationScale;
+
+ // Configure surface dimensions and orientation.
+ configureSurface();
+}
+
+void TouchInputMapper::configureAxes() {
+ mAxes.x.valid = false;
+ mAxes.y.valid = false;
+ mAxes.pressure.valid = false;
+ mAxes.size.valid = false;
+ mAxes.touchMajor.valid = false;
+ mAxes.touchMinor.valid = false;
+ mAxes.toolMajor.valid = false;
+ mAxes.toolMinor.valid = false;
+ mAxes.orientation.valid = false;
+}
+
+bool TouchInputMapper::configureSurface() {
+ // Update orientation and dimensions if needed.
+ int32_t orientation;
+ int32_t width, height;
+ if (mAssociatedDisplayId >= 0) {
+ if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, & width, & height, & orientation)) {
+ return false;
+ }
+ } else {
+ orientation = InputReaderPolicyInterface::ROTATION_0;
+ width = mAxes.x.getRange();
+ height = mAxes.y.getRange();
}
- if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH) {
- in->current.size = in->accumulator.absToolWidth;
+ bool orientationChanged = mSurfaceOrientation != orientation;
+ if (orientationChanged) {
+ mSurfaceOrientation = orientation;
}
- out->clear();
+ bool sizeChanged = mSurfaceWidth != width || mSurfaceHeight != height;
+ if (sizeChanged) {
+ mSurfaceWidth = width;
+ mSurfaceHeight = height;
+
+ // Compute size-dependent translation and scaling factors and place virtual keys.
+ if (mAxes.x.valid && mAxes.y.valid) {
+ mXOrigin = mAxes.x.minValue;
+ mYOrigin = mAxes.y.minValue;
+
+ LOGI("Device configured: id=0x%x, name=%s (display size was changed)",
+ getDeviceId(), getDeviceName().string());
+
+ mXScale = float(width) / mAxes.x.getRange();
+ mYScale = float(height) / mAxes.y.getRange();
+ mXPrecision = 1.0f / mXScale;
+ mYPrecision = 1.0f / mYScale;
+
+ configureVirtualKeys();
+ } else {
+ mXOrigin = 0;
+ mYOrigin = 0;
+ mXScale = 1.0f;
+ mYScale = 1.0f;
+ mXPrecision = 1.0f;
+ mYPrecision = 1.0f;
+ }
+
+ // Configure touch and tool area ranges.
+ float diagonal = sqrt(float(width * width + height * height));
+ float diagonalFuzz = sqrt(mXScale * mXScale + mYScale * mYScale);
+
+ mOrientedRanges.touchMajor.min = 0.0f;
+ mOrientedRanges.touchMajor.max = diagonal;
+ mOrientedRanges.touchMajor.flat = 0.0f;
+ mOrientedRanges.touchMajor.fuzz = diagonalFuzz;
+ mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
+
+ mOrientedRanges.toolMinor = mOrientedRanges.toolMajor = mOrientedRanges.touchMajor;
+ }
+
+ if (orientationChanged || sizeChanged) {
+ // Compute oriented surface dimensions, precision, and scales.
+ float orientedXScale, orientedYScale;
+ switch (mSurfaceOrientation) {
+ case InputReaderPolicyInterface::ROTATION_90:
+ case InputReaderPolicyInterface::ROTATION_270:
+ mOrientedSurfaceWidth = mSurfaceHeight;
+ mOrientedSurfaceHeight = mSurfaceWidth;
+ mOrientedXPrecision = mYPrecision;
+ mOrientedYPrecision = mXPrecision;
+ orientedXScale = mYScale;
+ orientedYScale = mXScale;
+ break;
+ default:
+ mOrientedSurfaceWidth = mSurfaceWidth;
+ mOrientedSurfaceHeight = mSurfaceHeight;
+ mOrientedXPrecision = mXPrecision;
+ mOrientedYPrecision = mYPrecision;
+ orientedXScale = mXScale;
+ orientedYScale = mYScale;
+ break;
+ }
+
+ // Configure position ranges.
+ mOrientedRanges.x.min = 0;
+ mOrientedRanges.x.max = mOrientedSurfaceWidth;
+ mOrientedRanges.x.flat = 0;
+ mOrientedRanges.x.fuzz = orientedXScale;
- if (in->current.down) {
- out->pointerCount = 1;
- out->pointers[0].id = 0;
- out->pointers[0].x = in->current.x;
- out->pointers[0].y = in->current.y;
- out->pointers[0].pressure = in->current.pressure;
- out->pointers[0].size = in->current.size;
- out->pointers[0].touchMajor = in->current.pressure;
- out->pointers[0].touchMinor = in->current.pressure;
- out->pointers[0].toolMajor = in->current.size;
- out->pointers[0].toolMinor = in->current.size;
- out->pointers[0].orientation = 0;
- out->idToIndex[0] = 0;
- out->idBits.markBit(0);
+ mOrientedRanges.y.min = 0;
+ mOrientedRanges.y.max = mOrientedSurfaceHeight;
+ mOrientedRanges.y.flat = 0;
+ mOrientedRanges.y.fuzz = orientedYScale;
}
- onTouchScreenChanged(when, device, true);
+ return true;
}
-void InputReader::onTouchScreenChanged(nsecs_t when,
- InputDevice* device, bool havePointerIds) {
- /* Apply policy */
+void TouchInputMapper::configureVirtualKeys() {
+ assert(mAxes.x.valid && mAxes.y.valid);
+
+ Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions;
+ getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions);
+
+ { // acquire virtual key lock
+ AutoMutex _l(mVirtualKeyLock);
+
+ mVirtualKeys.clear();
+
+ if (virtualKeyDefinitions.size() == 0) {
+ return;
+ }
+
+ mVirtualKeys.setCapacity(virtualKeyDefinitions.size());
+
+ int32_t touchScreenLeft = mAxes.x.minValue;
+ int32_t touchScreenTop = mAxes.y.minValue;
+ int32_t touchScreenWidth = mAxes.x.getRange();
+ int32_t touchScreenHeight = mAxes.y.getRange();
- int32_t policyActions = mPolicy->interceptTouch(when);
+ for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
+ const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition =
+ virtualKeyDefinitions[i];
+
+ mVirtualKeys.add();
+ VirtualKey& virtualKey = mVirtualKeys.editTop();
+
+ virtualKey.scanCode = virtualKeyDefinition.scanCode;
+ int32_t keyCode;
+ uint32_t flags;
+ if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode,
+ & keyCode, & flags)) {
+ LOGW(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode);
+ mVirtualKeys.pop(); // drop the key
+ continue;
+ }
+
+ virtualKey.keyCode = keyCode;
+ virtualKey.flags = flags;
+
+ // convert the key definition's display coordinates into touch coordinates for a hit box
+ int32_t halfWidth = virtualKeyDefinition.width / 2;
+ int32_t halfHeight = virtualKeyDefinition.height / 2;
+
+ virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth)
+ * touchScreenWidth / mSurfaceWidth + touchScreenLeft;
+ virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth)
+ * touchScreenWidth / mSurfaceWidth + touchScreenLeft;
+ virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight)
+ * touchScreenHeight / mSurfaceHeight + touchScreenTop;
+ virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
+ * touchScreenHeight / mSurfaceHeight + touchScreenTop;
+
+ LOGI(" VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d",
+ virtualKey.scanCode, virtualKey.keyCode,
+ virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom);
+ }
+ } // release virtual key lock
+}
+
+void TouchInputMapper::reset() {
+ // Synthesize touch up event if touch is currently down.
+ // This will also take care of finishing virtual key processing if needed.
+ if (mLastTouch.pointerCount != 0) {
+ nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+ mCurrentTouch.clear();
+ syncTouch(when, true);
+ }
+
+ InputMapper::reset();
+
+ // Reinitialize.
+ initialize();
+}
+
+void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
+ /* Refresh associated display information and update our size configuration if needed. */
+
+ if (! configureSurface()) {
+ return;
+ }
+
+ /* Apply policy */
uint32_t policyFlags = 0;
- if (! applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) {
- device->touchScreen.lastTouch.clear();
+ int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags);
+
+ if (! applyStandardPolicyActions(when, policyActions)) {
+ mLastTouch.clear();
return; // event dropped
}
/* Preprocess pointer data */
- if (device->touchScreen.parameters.useBadTouchFilter) {
- if (device->touchScreen.applyBadTouchFilter()) {
+ if (mParameters.useBadTouchFilter) {
+ if (applyBadTouchFilter()) {
havePointerIds = false;
}
}
- if (device->touchScreen.parameters.useJumpyTouchFilter) {
- if (device->touchScreen.applyJumpyTouchFilter()) {
+ if (mParameters.useJumpyTouchFilter) {
+ if (applyJumpyTouchFilter()) {
havePointerIds = false;
}
}
if (! havePointerIds) {
- device->touchScreen.calculatePointerIds();
+ calculatePointerIds();
}
- InputDevice::TouchData temp;
- InputDevice::TouchData* savedTouch;
- if (device->touchScreen.parameters.useAveragingTouchFilter) {
- temp.copyFrom(device->touchScreen.currentTouch);
+ TouchData temp;
+ TouchData* savedTouch;
+ if (mParameters.useAveragingTouchFilter) {
+ temp.copyFrom(mCurrentTouch);
savedTouch = & temp;
- device->touchScreen.applyAveragingTouchFilter();
+ applyAveragingTouchFilter();
} else {
- savedTouch = & device->touchScreen.currentTouch;
+ savedTouch = & mCurrentTouch;
}
- /* Process virtual keys or touches */
+ /* Process touches and virtual keys */
- if (! consumeVirtualKeyTouches(when, device, policyFlags)) {
- dispatchTouches(when, device, policyFlags);
+ TouchResult touchResult = consumeOffScreenTouches(when, policyFlags);
+ if (touchResult == DISPATCH_TOUCH) {
+ dispatchTouches(when, policyFlags);
}
- // Copy current touch to last touch in preparation for the next cycle.
- device->touchScreen.lastTouch.copyFrom(*savedTouch);
+ /* Copy current touch to last touch in preparation for the next cycle. */
+
+ if (touchResult == DROP_STROKE) {
+ mLastTouch.clear();
+ } else {
+ mLastTouch.copyFrom(*savedTouch);
+ }
}
-bool InputReader::consumeVirtualKeyTouches(nsecs_t when,
- InputDevice* device, uint32_t policyFlags) {
- switch (device->touchScreen.currentVirtualKey.status) {
- case InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_CANCELED:
- if (device->touchScreen.currentTouch.pointerCount == 0) {
- // Pointer went up after virtual key canceled.
- device->touchScreen.currentVirtualKey.status =
- InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_UP;
- }
- return true; // consumed
+TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches(
+ nsecs_t when, uint32_t policyFlags) {
+ int32_t keyEventAction, keyEventFlags;
+ int32_t keyCode, scanCode, downTime;
+ TouchResult touchResult;
+
+ { // acquire virtual key lock
+ AutoMutex _l(mVirtualKeyLock);
- case InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN:
- if (device->touchScreen.currentTouch.pointerCount == 0) {
- // Pointer went up while virtual key was down.
- device->touchScreen.currentVirtualKey.status =
- InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_UP;
+ if (mCurrentVirtualKey.down) {
+ if (mCurrentTouch.pointerCount == 0) {
+ // Pointer went up while virtual key was down.
+ mCurrentVirtualKey.down = false;
#if DEBUG_VIRTUAL_KEYS
- LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
- device->touchScreen.currentVirtualKey.keyCode,
- device->touchScreen.currentVirtualKey.scanCode);
+ LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
#endif
- dispatchVirtualKey(when, device, policyFlags, AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
- return true; // consumed
- }
+ keyEventAction = AKEY_EVENT_ACTION_UP;
+ keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
+ touchResult = SKIP_TOUCH;
+ goto DispatchVirtualKey;
+ }
- if (device->touchScreen.currentTouch.pointerCount == 1) {
- const InputDevice::VirtualKey* virtualKey = device->touchScreen.findVirtualKeyHit();
- if (virtualKey
- && virtualKey->keyCode == device->touchScreen.currentVirtualKey.keyCode) {
- // Pointer is still within the space of the virtual key.
- return true; // consumed
+ if (mCurrentTouch.pointerCount == 1) {
+ int32_t x = mCurrentTouch.pointers[0].x;
+ int32_t y = mCurrentTouch.pointers[0].y;
+ const VirtualKey* virtualKey = findVirtualKeyHitLvk(x, y);
+ if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) {
+ // Pointer is still within the space of the virtual key.
+ return SKIP_TOUCH;
+ }
}
- }
- // Pointer left virtual key area or another pointer also went down.
- // Send key cancellation.
- device->touchScreen.currentVirtualKey.status =
- InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_CANCELED;
+ // Pointer left virtual key area or another pointer also went down.
+ // Send key cancellation and drop the stroke so subsequent motions will be
+ // considered fresh downs. This is useful when the user swipes away from the
+ // virtual key area into the main display surface.
+ mCurrentVirtualKey.down = false;
#if DEBUG_VIRTUAL_KEYS
- LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
- device->touchScreen.currentVirtualKey.keyCode,
- device->touchScreen.currentVirtualKey.scanCode);
+ LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
#endif
- dispatchVirtualKey(when, device, policyFlags, AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
- | AKEY_EVENT_FLAG_CANCELED);
- return true; // consumed
-
- default:
- if (device->touchScreen.currentTouch.pointerCount == 1
- && device->touchScreen.lastTouch.pointerCount == 0) {
- // Pointer just went down. Check for virtual key hit.
- const InputDevice::VirtualKey* virtualKey = device->touchScreen.findVirtualKeyHit();
- if (virtualKey) {
- device->touchScreen.currentVirtualKey.status =
- InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN;
- device->touchScreen.currentVirtualKey.downTime = when;
- device->touchScreen.currentVirtualKey.keyCode = virtualKey->keyCode;
- device->touchScreen.currentVirtualKey.scanCode = virtualKey->scanCode;
+ keyEventAction = AKEY_EVENT_ACTION_UP;
+ keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
+ | AKEY_EVENT_FLAG_CANCELED;
+ touchResult = DROP_STROKE;
+ goto DispatchVirtualKey;
+ } else {
+ if (mCurrentTouch.pointerCount >= 1 && mLastTouch.pointerCount == 0) {
+ // Pointer just went down. Handle off-screen touches, if needed.
+ int32_t x = mCurrentTouch.pointers[0].x;
+ int32_t y = mCurrentTouch.pointers[0].y;
+ if (! isPointInsideSurface(x, y)) {
+ // If exactly one pointer went down, check for virtual key hit.
+ // Otherwise we will drop the entire stroke.
+ if (mCurrentTouch.pointerCount == 1) {
+ const VirtualKey* virtualKey = findVirtualKeyHitLvk(x, y);
+ if (virtualKey) {
+ mCurrentVirtualKey.down = true;
+ mCurrentVirtualKey.downTime = when;
+ mCurrentVirtualKey.keyCode = virtualKey->keyCode;
+ mCurrentVirtualKey.scanCode = virtualKey->scanCode;
#if DEBUG_VIRTUAL_KEYS
- LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
- device->touchScreen.currentVirtualKey.keyCode,
- device->touchScreen.currentVirtualKey.scanCode);
+ LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
#endif
- dispatchVirtualKey(when, device, policyFlags, AKEY_EVENT_ACTION_DOWN,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
- return true; // consumed
+ keyEventAction = AKEY_EVENT_ACTION_DOWN;
+ keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM
+ | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
+ touchResult = SKIP_TOUCH;
+ goto DispatchVirtualKey;
+ }
+ }
+ return DROP_STROKE;
+ }
}
+ return DISPATCH_TOUCH;
}
- return false; // not consumed
- }
-}
-void InputReader::dispatchVirtualKey(nsecs_t when,
- InputDevice* device, uint32_t policyFlags,
- int32_t keyEventAction, int32_t keyEventFlags) {
- updateExportedVirtualKeyState();
+ DispatchVirtualKey:
+ // Collect remaining state needed to dispatch virtual key.
+ keyCode = mCurrentVirtualKey.keyCode;
+ scanCode = mCurrentVirtualKey.scanCode;
+ downTime = mCurrentVirtualKey.downTime;
+ } // release virtual key lock
- int32_t keyCode = device->touchScreen.currentVirtualKey.keyCode;
- int32_t scanCode = device->touchScreen.currentVirtualKey.scanCode;
- nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime;
- int32_t metaState = globalMetaState();
+ // Dispatch virtual key.
+ int32_t metaState = mContext->getGlobalMetaState();
if (keyEventAction == AKEY_EVENT_ACTION_DOWN) {
- mPolicy->virtualKeyDownFeedback();
+ getPolicy()->virtualKeyDownFeedback();
}
- int32_t policyActions = mPolicy->interceptKey(when, device->id,
+ int32_t policyActions = getPolicy()->interceptKey(when, getDeviceId(),
keyEventAction == AKEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags);
- if (applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) {
- mDispatcher->notifyKey(when, device->id, AINPUT_SOURCE_KEYBOARD, policyFlags,
+ if (applyStandardPolicyActions(when, policyActions)) {
+ getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
}
+ return touchResult;
}
-void InputReader::dispatchTouches(nsecs_t when,
- InputDevice* device, uint32_t policyFlags) {
- uint32_t currentPointerCount = device->touchScreen.currentTouch.pointerCount;
- uint32_t lastPointerCount = device->touchScreen.lastTouch.pointerCount;
+void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
+ uint32_t currentPointerCount = mCurrentTouch.pointerCount;
+ uint32_t lastPointerCount = mLastTouch.pointerCount;
if (currentPointerCount == 0 && lastPointerCount == 0) {
return; // nothing to do!
}
- BitSet32 currentIdBits = device->touchScreen.currentTouch.idBits;
- BitSet32 lastIdBits = device->touchScreen.lastTouch.idBits;
+ BitSet32 currentIdBits = mCurrentTouch.idBits;
+ BitSet32 lastIdBits = mLastTouch.idBits;
if (currentIdBits == lastIdBits) {
// No pointer id changes so this is a move event.
// The dispatcher takes care of batching moves so we don't have to deal with that here.
int32_t motionEventAction = AMOTION_EVENT_ACTION_MOVE;
- dispatchTouch(when, device, policyFlags, & device->touchScreen.currentTouch,
+ dispatchTouch(when, policyFlags, & mCurrentTouch,
currentIdBits, -1, motionEventAction);
} else {
// There may be pointers going up and pointers going down at the same time when pointer
@@ -791,7 +1536,7 @@ void InputReader::dispatchTouches(nsecs_t when,
motionEventAction = AMOTION_EVENT_ACTION_POINTER_UP;
}
- dispatchTouch(when, device, policyFlags, & device->touchScreen.lastTouch,
+ dispatchTouch(when, policyFlags, & mLastTouch,
oldActiveIdBits, upId, motionEventAction);
}
@@ -804,40 +1549,24 @@ void InputReader::dispatchTouches(nsecs_t when,
int32_t motionEventAction;
if (oldActiveIdBits.isEmpty()) {
motionEventAction = AMOTION_EVENT_ACTION_DOWN;
- device->touchScreen.downTime = when;
+ mDownTime = when;
} else {
motionEventAction = AMOTION_EVENT_ACTION_POINTER_DOWN;
}
- dispatchTouch(when, device, policyFlags, & device->touchScreen.currentTouch,
+ dispatchTouch(when, policyFlags, & mCurrentTouch,
activeIdBits, downId, motionEventAction);
}
}
}
-void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t policyFlags,
- InputDevice::TouchData* touch, BitSet32 idBits, uint32_t changedId,
+void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags,
+ TouchData* touch, BitSet32 idBits, uint32_t changedId,
int32_t motionEventAction) {
- int32_t orientedWidth, orientedHeight;
- switch (mDisplayOrientation) {
- case InputReaderPolicyInterface::ROTATION_90:
- case InputReaderPolicyInterface::ROTATION_270:
- orientedWidth = mDisplayHeight;
- orientedHeight = mDisplayWidth;
- break;
- default:
- orientedWidth = mDisplayWidth;
- orientedHeight = mDisplayHeight;
- break;
- }
-
uint32_t pointerCount = 0;
int32_t pointerIds[MAX_POINTERS];
PointerCoords pointerCoords[MAX_POINTERS];
- const InputDevice::TouchScreenState::Precalculated& precalculated =
- device->touchScreen.precalculated;
-
// Walk through the the active pointers and map touch screen coordinates (TouchData) into
// display coordinates (PointerCoords) and adjust for display orientation.
while (! idBits.isEmpty()) {
@@ -845,55 +1574,57 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli
idBits.clearBit(id);
uint32_t index = touch->idToIndex[id];
- float x = float(touch->pointers[index].x
- - precalculated.xOrigin) * precalculated.xScale;
- float y = float(touch->pointers[index].y
- - precalculated.yOrigin) * precalculated.yScale;
- float pressure = float(touch->pointers[index].pressure
- - precalculated.pressureOrigin) * precalculated.pressureScale;
- float size = float(touch->pointers[index].size
- - precalculated.sizeOrigin) * precalculated.sizeScale;
+ float x = float(touch->pointers[index].x - mXOrigin) * mXScale;
+ float y = float(touch->pointers[index].y - mYOrigin) * mYScale;
+ float pressure = float(touch->pointers[index].pressure - mPressureOrigin) * mPressureScale;
+ float size = float(touch->pointers[index].size - mSizeOrigin) * mSizeScale;
- float orientation = float(touch->pointers[index].orientation)
- * precalculated.orientationScale;
+ float orientation = float(touch->pointers[index].orientation) * mOrientationScale;
- bool vertical = abs(orientation) <= M_PI / 8;
+ float touchMajor, touchMinor, toolMajor, toolMinor;
+ if (abs(orientation) <= M_PI_4) {
+ // Nominally vertical orientation: scale major axis by Y, and scale minor axis by X.
+ touchMajor = float(touch->pointers[index].touchMajor) * mYScale;
+ touchMinor = float(touch->pointers[index].touchMinor) * mXScale;
+ toolMajor = float(touch->pointers[index].toolMajor) * mYScale;
+ toolMinor = float(touch->pointers[index].toolMinor) * mXScale;
+ } else {
+ // Nominally horizontal orientation: scale major axis by X, and scale minor axis by Y.
+ touchMajor = float(touch->pointers[index].touchMajor) * mXScale;
+ touchMinor = float(touch->pointers[index].touchMinor) * mYScale;
+ toolMajor = float(touch->pointers[index].toolMajor) * mXScale;
+ toolMinor = float(touch->pointers[index].toolMinor) * mYScale;
+ }
- switch (mDisplayOrientation) {
+ switch (mSurfaceOrientation) {
case InputReaderPolicyInterface::ROTATION_90: {
float xTemp = x;
x = y;
- y = mDisplayWidth - xTemp;
- vertical = ! vertical;
+ y = mSurfaceWidth - xTemp;
+ orientation -= M_PI_2;
+ if (orientation < - M_PI_2) {
+ orientation += M_PI;
+ }
break;
}
case InputReaderPolicyInterface::ROTATION_180: {
- x = mDisplayWidth - x;
- y = mDisplayHeight - y;
+ x = mSurfaceWidth - x;
+ y = mSurfaceHeight - y;
+ orientation = - orientation;
break;
}
case InputReaderPolicyInterface::ROTATION_270: {
float xTemp = x;
- x = mDisplayHeight - y;
+ x = mSurfaceHeight - y;
y = xTemp;
- vertical = ! vertical;
+ orientation += M_PI_2;
+ if (orientation > M_PI_2) {
+ orientation -= M_PI;
+ }
break;
}
}
- float touchMajor, touchMinor, toolMajor, toolMinor;
- if (vertical) {
- touchMajor = float(touch->pointers[index].touchMajor) * precalculated.yScale;
- touchMinor = float(touch->pointers[index].touchMinor) * precalculated.xScale;
- toolMajor = float(touch->pointers[index].toolMajor) * precalculated.yScale;
- toolMinor = float(touch->pointers[index].toolMinor) * precalculated.xScale;
- } else {
- touchMajor = float(touch->pointers[index].touchMajor) * precalculated.xScale;
- touchMinor = float(touch->pointers[index].touchMinor) * precalculated.yScale;
- toolMajor = float(touch->pointers[index].toolMajor) * precalculated.xScale;
- toolMinor = float(touch->pointers[index].toolMinor) * precalculated.yScale;
- }
-
pointerIds[pointerCount] = int32_t(id);
pointerCoords[pointerCount].x = x;
@@ -919,561 +1650,984 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli
if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) {
if (pointerCoords[0].x <= 0) {
motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT;
- } else if (pointerCoords[0].x >= orientedWidth) {
+ } else if (pointerCoords[0].x >= mOrientedSurfaceWidth) {
motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT;
}
if (pointerCoords[0].y <= 0) {
motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP;
- } else if (pointerCoords[0].y >= orientedHeight) {
+ } else if (pointerCoords[0].y >= mOrientedSurfaceHeight) {
motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM;
}
}
- nsecs_t downTime = device->touchScreen.downTime;
- mDispatcher->notifyMotion(when, device->id, AINPUT_SOURCE_TOUCHSCREEN, policyFlags,
- motionEventAction, globalMetaState(), motionEventEdgeFlags,
+ getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TOUCHSCREEN, policyFlags,
+ motionEventAction, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
pointerCount, pointerIds, pointerCoords,
- 0, 0, downTime);
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime);
}
-void InputReader::onTrackballStateChanged(nsecs_t when,
- InputDevice* device) {
- static const uint32_t DELTA_FIELDS =
- InputDevice::TrackballState::Accumulator::FIELD_REL_X
- | InputDevice::TrackballState::Accumulator::FIELD_REL_Y;
+bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
+ if (mAxes.x.valid && mAxes.y.valid) {
+ return x >= mAxes.x.minValue && x <= mAxes.x.maxValue
+ && y >= mAxes.y.minValue && y <= mAxes.y.maxValue;
+ }
+ return true;
+}
- /* Refresh display properties so we can trackball moves according to display orientation */
+const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHitLvk(int32_t x, int32_t y) {
+ for (size_t i = 0; i < mVirtualKeys.size(); i++) {
+ const VirtualKey& virtualKey = mVirtualKeys[i];
- if (! refreshDisplayProperties()) {
- return;
- }
+#if DEBUG_VIRTUAL_KEYS
+ LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
+ "left=%d, top=%d, right=%d, bottom=%d",
+ x, y,
+ virtualKey.keyCode, virtualKey.scanCode,
+ virtualKey.hitLeft, virtualKey.hitTop,
+ virtualKey.hitRight, virtualKey.hitBottom);
+#endif
- /* Update device state */
+ if (virtualKey.isHit(x, y)) {
+ return & virtualKey;
+ }
+ }
- uint32_t fields = device->trackball.accumulator.fields;
- bool downChanged = fields & InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE;
- bool deltaChanged = fields & DELTA_FIELDS;
+ return NULL;
+}
- bool down;
- if (downChanged) {
- if (device->trackball.accumulator.btnMouse) {
- device->trackball.current.down = true;
- device->trackball.current.downTime = when;
- down = true;
- } else {
- device->trackball.current.down = false;
- down = false;
+void TouchInputMapper::calculatePointerIds() {
+ uint32_t currentPointerCount = mCurrentTouch.pointerCount;
+ uint32_t lastPointerCount = mLastTouch.pointerCount;
+
+ if (currentPointerCount == 0) {
+ // No pointers to assign.
+ mCurrentTouch.idBits.clear();
+ } else if (lastPointerCount == 0) {
+ // All pointers are new.
+ mCurrentTouch.idBits.clear();
+ for (uint32_t i = 0; i < currentPointerCount; i++) {
+ mCurrentTouch.pointers[i].id = i;
+ mCurrentTouch.idToIndex[i] = i;
+ mCurrentTouch.idBits.markBit(i);
}
+ } else if (currentPointerCount == 1 && lastPointerCount == 1) {
+ // Only one pointer and no change in count so it must have the same id as before.
+ uint32_t id = mLastTouch.pointers[0].id;
+ mCurrentTouch.pointers[0].id = id;
+ mCurrentTouch.idToIndex[id] = 0;
+ mCurrentTouch.idBits.value = BitSet32::valueForBit(id);
} else {
- down = device->trackball.current.down;
- }
+ // General case.
+ // We build a heap of squared euclidean distances between current and last pointers
+ // associated with the current and last pointer indices. Then, we find the best
+ // match (by distance) for each current pointer.
+ PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
+
+ uint32_t heapSize = 0;
+ for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
+ currentPointerIndex++) {
+ for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
+ lastPointerIndex++) {
+ int64_t deltaX = mCurrentTouch.pointers[currentPointerIndex].x
+ - mLastTouch.pointers[lastPointerIndex].x;
+ int64_t deltaY = mCurrentTouch.pointers[currentPointerIndex].y
+ - mLastTouch.pointers[lastPointerIndex].y;
+
+ uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
+
+ // Insert new element into the heap (sift up).
+ heap[heapSize].currentPointerIndex = currentPointerIndex;
+ heap[heapSize].lastPointerIndex = lastPointerIndex;
+ heap[heapSize].distance = distance;
+ heapSize += 1;
+ }
+ }
- /* Apply policy */
+ // Heapify
+ for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
+ startIndex -= 1;
+ for (uint32_t parentIndex = startIndex; ;) {
+ uint32_t childIndex = parentIndex * 2 + 1;
+ if (childIndex >= heapSize) {
+ break;
+ }
- int32_t policyActions = mPolicy->interceptTrackball(when, downChanged, down, deltaChanged);
+ if (childIndex + 1 < heapSize
+ && heap[childIndex + 1].distance < heap[childIndex].distance) {
+ childIndex += 1;
+ }
- uint32_t policyFlags = 0;
- if (! applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) {
- return; // event dropped
- }
+ if (heap[parentIndex].distance <= heap[childIndex].distance) {
+ break;
+ }
- /* Enqueue motion event for dispatch */
+ swap(heap[parentIndex], heap[childIndex]);
+ parentIndex = childIndex;
+ }
+ }
- int32_t motionEventAction;
- if (downChanged) {
- motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
- } else {
- motionEventAction = AMOTION_EVENT_ACTION_MOVE;
- }
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize);
+ for (size_t i = 0; i < heapSize; i++) {
+ LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
+ i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+ heap[i].distance);
+ }
+#endif
- int32_t pointerId = 0;
- PointerCoords pointerCoords;
- pointerCoords.x = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_X
- ? device->trackball.accumulator.relX * device->trackball.precalculated.xScale : 0;
- pointerCoords.y = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_Y
- ? device->trackball.accumulator.relY * device->trackball.precalculated.yScale : 0;
- pointerCoords.pressure = 1.0f; // XXX Consider making this 1.0f if down, 0 otherwise.
- pointerCoords.size = 0;
- pointerCoords.touchMajor = 0;
- pointerCoords.touchMinor = 0;
- pointerCoords.toolMajor = 0;
- pointerCoords.toolMinor = 0;
- pointerCoords.orientation = 0;
+ // Pull matches out by increasing order of distance.
+ // To avoid reassigning pointers that have already been matched, the loop keeps track
+ // of which last and current pointers have been matched using the matchedXXXBits variables.
+ // It also tracks the used pointer id bits.
+ BitSet32 matchedLastBits(0);
+ BitSet32 matchedCurrentBits(0);
+ BitSet32 usedIdBits(0);
+ bool first = true;
+ for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) {
+ for (;;) {
+ if (first) {
+ // The first time through the loop, we just consume the root element of
+ // the heap (the one with smallest distance).
+ first = false;
+ } else {
+ // Previous iterations consumed the root element of the heap.
+ // Pop root element off of the heap (sift down).
+ heapSize -= 1;
+ assert(heapSize > 0);
+
+ // Sift down.
+ heap[0] = heap[heapSize];
+ for (uint32_t parentIndex = 0; ;) {
+ uint32_t childIndex = parentIndex * 2 + 1;
+ if (childIndex >= heapSize) {
+ break;
+ }
+
+ if (childIndex + 1 < heapSize
+ && heap[childIndex + 1].distance < heap[childIndex].distance) {
+ childIndex += 1;
+ }
+
+ if (heap[parentIndex].distance <= heap[childIndex].distance) {
+ break;
+ }
+
+ swap(heap[parentIndex], heap[childIndex]);
+ parentIndex = childIndex;
+ }
+
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize);
+ for (size_t i = 0; i < heapSize; i++) {
+ LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
+ i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+ heap[i].distance);
+ }
+#endif
+ }
- float temp;
- switch (mDisplayOrientation) {
- case InputReaderPolicyInterface::ROTATION_90:
- temp = pointerCoords.x;
- pointerCoords.x = pointerCoords.y;
- pointerCoords.y = - temp;
- break;
+ uint32_t currentPointerIndex = heap[0].currentPointerIndex;
+ if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
- case InputReaderPolicyInterface::ROTATION_180:
- pointerCoords.x = - pointerCoords.x;
- pointerCoords.y = - pointerCoords.y;
- break;
+ uint32_t lastPointerIndex = heap[0].lastPointerIndex;
+ if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
- case InputReaderPolicyInterface::ROTATION_270:
- temp = pointerCoords.x;
- pointerCoords.x = - pointerCoords.y;
- pointerCoords.y = temp;
- break;
- }
+ matchedCurrentBits.markBit(currentPointerIndex);
+ matchedLastBits.markBit(lastPointerIndex);
- mDispatcher->notifyMotion(when, device->id, AINPUT_SOURCE_TRACKBALL, policyFlags,
- motionEventAction, globalMetaState(), AMOTION_EVENT_EDGE_FLAG_NONE,
- 1, & pointerId, & pointerCoords,
- device->trackball.precalculated.xPrecision,
- device->trackball.precalculated.yPrecision,
- device->trackball.current.downTime);
-}
+ uint32_t id = mLastTouch.pointers[lastPointerIndex].id;
+ mCurrentTouch.pointers[currentPointerIndex].id = id;
+ mCurrentTouch.idToIndex[id] = currentPointerIndex;
+ usedIdBits.markBit(id);
-void InputReader::onConfigurationChanged(nsecs_t when) {
- // Reset global meta state because it depends on the list of all configured devices.
- resetGlobalMetaState();
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
+ lastPointerIndex, currentPointerIndex, id, heap[0].distance);
+#endif
+ break;
+ }
+ }
- // Reset virtual keys, just in case.
- updateExportedVirtualKeyState();
+ // Assign fresh ids to new pointers.
+ if (currentPointerCount > lastPointerCount) {
+ for (uint32_t i = currentPointerCount - lastPointerCount; ;) {
+ uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit();
+ uint32_t id = usedIdBits.firstUnmarkedBit();
- // Update input configuration.
- updateExportedInputConfiguration();
+ mCurrentTouch.pointers[currentPointerIndex].id = id;
+ mCurrentTouch.idToIndex[id] = currentPointerIndex;
+ usedIdBits.markBit(id);
- // Enqueue configuration changed.
- mDispatcher->notifyConfigurationChanged(when);
-}
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - assigned: cur=%d, id=%d",
+ currentPointerIndex, id);
+#endif
-bool InputReader::applyStandardInputDispatchPolicyActions(nsecs_t when,
- int32_t policyActions, uint32_t* policyFlags) {
- if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) {
- mDispatcher->notifyAppSwitchComing(when);
+ if (--i == 0) break; // done
+ matchedCurrentBits.markBit(currentPointerIndex);
+ }
+ }
+
+ // Fix id bits.
+ mCurrentTouch.idBits = usedIdBits;
}
+}
- if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) {
- *policyFlags |= POLICY_FLAG_WOKE_HERE;
+/* Special hack for devices that have bad screen data: if one of the
+ * points has moved more than a screen height from the last position,
+ * then drop it. */
+bool TouchInputMapper::applyBadTouchFilter() {
+ // This hack requires valid axis parameters.
+ if (! mAxes.y.valid) {
+ return false;
}
- if (policyActions & InputReaderPolicyInterface::ACTION_BRIGHT_HERE) {
- *policyFlags |= POLICY_FLAG_BRIGHT_HERE;
+ uint32_t pointerCount = mCurrentTouch.pointerCount;
+
+ // Nothing to do if there are no points.
+ if (pointerCount == 0) {
+ return false;
}
- return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
-}
+ // Don't do anything if a finger is going down or up. We run
+ // here before assigning pointer IDs, so there isn't a good
+ // way to do per-finger matching.
+ if (pointerCount != mLastTouch.pointerCount) {
+ return false;
+ }
-void InputReader::resetDisplayProperties() {
- mDisplayWidth = mDisplayHeight = -1;
- mDisplayOrientation = -1;
-}
+ // We consider a single movement across more than a 7/16 of
+ // the long size of the screen to be bad. This was a magic value
+ // determined by looking at the maximum distance it is feasible
+ // to actually move in one sample.
+ int32_t maxDeltaY = mAxes.y.getRange() * 7 / 16;
+
+ // XXX The original code in InputDevice.java included commented out
+ // code for testing the X axis. Note that when we drop a point
+ // we don't actually restore the old X either. Strange.
+ // The old code also tries to track when bad points were previously
+ // detected but it turns out that due to the placement of a "break"
+ // at the end of the loop, we never set mDroppedBadPoint to true
+ // so it is effectively dead code.
+ // Need to figure out if the old code is busted or just overcomplicated
+ // but working as intended.
+
+ // Look through all new points and see if any are farther than
+ // acceptable from all previous points.
+ for (uint32_t i = pointerCount; i-- > 0; ) {
+ int32_t y = mCurrentTouch.pointers[i].y;
+ int32_t closestY = INT_MAX;
+ int32_t closestDeltaY = 0;
+
+#if DEBUG_HACKS
+ LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y);
+#endif
-bool InputReader::refreshDisplayProperties() {
- int32_t newWidth, newHeight, newOrientation;
- if (mPolicy->getDisplayInfo(0, & newWidth, & newHeight, & newOrientation)) {
- if (newWidth != mDisplayWidth || newHeight != mDisplayHeight) {
- LOGD("Display size changed from %dx%d to %dx%d, updating device configuration",
- mDisplayWidth, mDisplayHeight, newWidth, newHeight);
+ for (uint32_t j = pointerCount; j-- > 0; ) {
+ int32_t lastY = mLastTouch.pointers[j].y;
+ int32_t deltaY = abs(y - lastY);
- mDisplayWidth = newWidth;
- mDisplayHeight = newHeight;
+#if DEBUG_HACKS
+ LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d",
+ j, lastY, deltaY);
+#endif
- for (size_t i = 0; i < mDevices.size(); i++) {
- configureDeviceForCurrentDisplaySize(mDevices.valueAt(i));
+ if (deltaY < maxDeltaY) {
+ goto SkipSufficientlyClosePoint;
+ }
+ if (deltaY < closestDeltaY) {
+ closestDeltaY = deltaY;
+ closestY = lastY;
}
}
- if (newOrientation != mDisplayOrientation) {
- LOGD("Display orientation changed to %d", mDisplayOrientation);
+ // Must not have found a close enough match.
+#if DEBUG_HACKS
+ LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d",
+ i, y, closestY, closestDeltaY, maxDeltaY);
+#endif
- mDisplayOrientation = newOrientation;
- }
- return true;
- } else {
- resetDisplayProperties();
- return false;
+ mCurrentTouch.pointers[i].y = closestY;
+ return true; // XXX original code only corrects one point
+
+ SkipSufficientlyClosePoint: ;
}
-}
-InputDevice* InputReader::getDevice(int32_t deviceId) {
- ssize_t index = mDevices.indexOfKey(deviceId);
- return index >= 0 ? mDevices.valueAt((size_t) index) : NULL;
+ // No change.
+ return false;
}
-InputDevice* InputReader::getNonIgnoredDevice(int32_t deviceId) {
- InputDevice* device = getDevice(deviceId);
- return device && ! device->ignored ? device : NULL;
-}
+/* Special hack for devices that have bad screen data: drop points where
+ * the coordinate value for one axis has jumped to the other pointer's location.
+ */
+bool TouchInputMapper::applyJumpyTouchFilter() {
+ // This hack requires valid axis parameters.
+ if (! mAxes.y.valid) {
+ return false;
+ }
-void InputReader::addDevice(nsecs_t when, int32_t deviceId) {
- uint32_t classes = mEventHub->getDeviceClasses(deviceId);
- String8 name = mEventHub->getDeviceName(deviceId);
- InputDevice* device = new InputDevice(deviceId, classes, name);
+ uint32_t pointerCount = mCurrentTouch.pointerCount;
+ if (mLastTouch.pointerCount != pointerCount) {
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Different pointer count %d -> %d",
+ mLastTouch.pointerCount, pointerCount);
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ LOGD(" Pointer %d (%d, %d)", i,
+ mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y);
+ }
+#endif
- if (classes != 0) {
- LOGI("Device added: id=0x%x, name=%s, classes=%02x", device->id,
- device->name.string(), device->classes);
+ if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
+ if (mLastTouch.pointerCount == 1 && pointerCount == 2) {
+ // Just drop the first few events going from 1 to 2 pointers.
+ // They're bad often enough that they're not worth considering.
+ mCurrentTouch.pointerCount = 1;
+ mJumpyTouchFilter.jumpyPointsDropped += 1;
- configureDevice(device);
- } else {
- LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", device->id,
- device->name.string());
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Pointer 2 dropped");
+#endif
+ return true;
+ } else if (mLastTouch.pointerCount == 2 && pointerCount == 1) {
+ // The event when we go from 2 -> 1 tends to be messed up too
+ mCurrentTouch.pointerCount = 2;
+ mCurrentTouch.pointers[0] = mLastTouch.pointers[0];
+ mCurrentTouch.pointers[1] = mLastTouch.pointers[1];
+ mJumpyTouchFilter.jumpyPointsDropped += 1;
+
+#if DEBUG_HACKS
+ for (int32_t i = 0; i < 2; i++) {
+ LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i,
+ mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y);
+ }
+#endif
+ return true;
+ }
+ }
+ // Reset jumpy points dropped on other transitions or if limit exceeded.
+ mJumpyTouchFilter.jumpyPointsDropped = 0;
- device->ignored = true;
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Transition - drop limit reset");
+#endif
+ return false;
}
- device->reset();
+ // We have the same number of pointers as last time.
+ // A 'jumpy' point is one where the coordinate value for one axis
+ // has jumped to the other pointer's location. No need to do anything
+ // else if we only have one pointer.
+ if (pointerCount < 2) {
+ return false;
+ }
- mDevices.add(deviceId, device);
+ if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) {
+ int jumpyEpsilon = mAxes.y.getRange() / JUMPY_EPSILON_DIVISOR;
- if (! device->ignored) {
- onConfigurationChanged(when);
- }
-}
+ // We only replace the single worst jumpy point as characterized by pointer distance
+ // in a single axis.
+ int32_t badPointerIndex = -1;
+ int32_t badPointerReplacementIndex = -1;
+ int32_t badPointerDistance = INT_MIN; // distance to be corrected
-void InputReader::removeDevice(nsecs_t when, InputDevice* device) {
- mDevices.removeItem(device->id);
+ for (uint32_t i = pointerCount; i-- > 0; ) {
+ int32_t x = mCurrentTouch.pointers[i].x;
+ int32_t y = mCurrentTouch.pointers[i].y;
- if (! device->ignored) {
- LOGI("Device removed: id=0x%x, name=%s, classes=%02x", device->id,
- device->name.string(), device->classes);
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y);
+#endif
- onConfigurationChanged(when);
- } else {
- LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)", device->id,
- device->name.string());
- }
+ // Check if a touch point is too close to another's coordinates
+ bool dropX = false, dropY = false;
+ for (uint32_t j = 0; j < pointerCount; j++) {
+ if (i == j) {
+ continue;
+ }
- delete device;
-}
+ if (abs(x - mCurrentTouch.pointers[j].x) <= jumpyEpsilon) {
+ dropX = true;
+ break;
+ }
-void InputReader::configureDevice(InputDevice* device) {
- if (device->isMultiTouchScreen()) {
- configureAbsoluteAxisInfo(device, ABS_MT_POSITION_X, "X",
- & device->touchScreen.parameters.xAxis);
- configureAbsoluteAxisInfo(device, ABS_MT_POSITION_Y, "Y",
- & device->touchScreen.parameters.yAxis);
- configureAbsoluteAxisInfo(device, ABS_MT_TOUCH_MAJOR, "Pressure",
- & device->touchScreen.parameters.pressureAxis);
- configureAbsoluteAxisInfo(device, ABS_MT_WIDTH_MAJOR, "Size",
- & device->touchScreen.parameters.sizeAxis);
- configureAbsoluteAxisInfo(device, ABS_MT_ORIENTATION, "Orientation",
- & device->touchScreen.parameters.orientationAxis);
- } else if (device->isSingleTouchScreen()) {
- configureAbsoluteAxisInfo(device, ABS_X, "X",
- & device->touchScreen.parameters.xAxis);
- configureAbsoluteAxisInfo(device, ABS_Y, "Y",
- & device->touchScreen.parameters.yAxis);
- configureAbsoluteAxisInfo(device, ABS_PRESSURE, "Pressure",
- & device->touchScreen.parameters.pressureAxis);
- configureAbsoluteAxisInfo(device, ABS_TOOL_WIDTH, "Size",
- & device->touchScreen.parameters.sizeAxis);
- device->touchScreen.parameters.orientationAxis.valid = false;
- }
-
- if (device->isTouchScreen()) {
- device->touchScreen.parameters.useBadTouchFilter =
- mPolicy->filterTouchEvents();
- device->touchScreen.parameters.useAveragingTouchFilter =
- mPolicy->filterTouchEvents();
- device->touchScreen.parameters.useJumpyTouchFilter =
- mPolicy->filterJumpyTouchEvents();
-
- if (device->touchScreen.parameters.pressureAxis.valid) {
- device->touchScreen.precalculated.pressureOrigin =
- device->touchScreen.parameters.pressureAxis.minValue;
- device->touchScreen.precalculated.pressureScale =
- 1.0f / device->touchScreen.parameters.pressureAxis.range;
- } else {
- device->touchScreen.precalculated.pressureOrigin = 0;
- device->touchScreen.precalculated.pressureScale = 1.0f;
- }
+ if (abs(y - mCurrentTouch.pointers[j].y) <= jumpyEpsilon) {
+ dropY = true;
+ break;
+ }
+ }
+ if (! dropX && ! dropY) {
+ continue; // not jumpy
+ }
- if (device->touchScreen.parameters.sizeAxis.valid) {
- device->touchScreen.precalculated.sizeOrigin =
- device->touchScreen.parameters.sizeAxis.minValue;
- device->touchScreen.precalculated.sizeScale =
- 1.0f / device->touchScreen.parameters.sizeAxis.range;
- } else {
- device->touchScreen.precalculated.sizeOrigin = 0;
- device->touchScreen.precalculated.sizeScale = 1.0f;
- }
+ // Find a replacement candidate by comparing with older points on the
+ // complementary (non-jumpy) axis.
+ int32_t distance = INT_MIN; // distance to be corrected
+ int32_t replacementIndex = -1;
+
+ if (dropX) {
+ // X looks too close. Find an older replacement point with a close Y.
+ int32_t smallestDeltaY = INT_MAX;
+ for (uint32_t j = 0; j < pointerCount; j++) {
+ int32_t deltaY = abs(y - mLastTouch.pointers[j].y);
+ if (deltaY < smallestDeltaY) {
+ smallestDeltaY = deltaY;
+ replacementIndex = j;
+ }
+ }
+ distance = abs(x - mLastTouch.pointers[replacementIndex].x);
+ } else {
+ // Y looks too close. Find an older replacement point with a close X.
+ int32_t smallestDeltaX = INT_MAX;
+ for (uint32_t j = 0; j < pointerCount; j++) {
+ int32_t deltaX = abs(x - mLastTouch.pointers[j].x);
+ if (deltaX < smallestDeltaX) {
+ smallestDeltaX = deltaX;
+ replacementIndex = j;
+ }
+ }
+ distance = abs(y - mLastTouch.pointers[replacementIndex].y);
+ }
- if (device->touchScreen.parameters.orientationAxis.valid
- && device->touchScreen.parameters.orientationAxis.maxValue > 0) {
- device->touchScreen.precalculated.orientationScale =
- M_PI_4 / device->touchScreen.parameters.orientationAxis.maxValue;
- } else {
- device->touchScreen.precalculated.orientationScale = 0.0f;
+ // If replacing this pointer would correct a worse error than the previous ones
+ // considered, then use this replacement instead.
+ if (distance > badPointerDistance) {
+ badPointerIndex = i;
+ badPointerReplacementIndex = replacementIndex;
+ badPointerDistance = distance;
+ }
}
- }
- if (device->isTrackball()) {
- device->trackball.precalculated.xPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
- device->trackball.precalculated.yPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
- device->trackball.precalculated.xScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
- device->trackball.precalculated.yScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+ // Correct the jumpy pointer if one was found.
+ if (badPointerIndex >= 0) {
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)",
+ badPointerIndex,
+ mLastTouch.pointers[badPointerReplacementIndex].x,
+ mLastTouch.pointers[badPointerReplacementIndex].y);
+#endif
+
+ mCurrentTouch.pointers[badPointerIndex].x =
+ mLastTouch.pointers[badPointerReplacementIndex].x;
+ mCurrentTouch.pointers[badPointerIndex].y =
+ mLastTouch.pointers[badPointerReplacementIndex].y;
+ mJumpyTouchFilter.jumpyPointsDropped += 1;
+ return true;
+ }
}
- configureDeviceForCurrentDisplaySize(device);
+ mJumpyTouchFilter.jumpyPointsDropped = 0;
+ return false;
}
-void InputReader::configureDeviceForCurrentDisplaySize(InputDevice* device) {
- if (device->isTouchScreen()) {
- if (device->touchScreen.parameters.xAxis.valid
- && device->touchScreen.parameters.yAxis.valid) {
- device->touchScreen.precalculated.xOrigin =
- device->touchScreen.parameters.xAxis.minValue;
- device->touchScreen.precalculated.yOrigin =
- device->touchScreen.parameters.yAxis.minValue;
+/* Special hack for devices that have bad screen data: aggregate and
+ * compute averages of the coordinate data, to reduce the amount of
+ * jitter seen by applications. */
+void TouchInputMapper::applyAveragingTouchFilter() {
+ for (uint32_t currentIndex = 0; currentIndex < mCurrentTouch.pointerCount; currentIndex++) {
+ uint32_t id = mCurrentTouch.pointers[currentIndex].id;
+ int32_t x = mCurrentTouch.pointers[currentIndex].x;
+ int32_t y = mCurrentTouch.pointers[currentIndex].y;
+ int32_t pressure = mCurrentTouch.pointers[currentIndex].pressure;
+
+ if (mLastTouch.idBits.hasBit(id)) {
+ // Pointer was down before and is still down now.
+ // Compute average over history trace.
+ uint32_t start = mAveragingTouchFilter.historyStart[id];
+ uint32_t end = mAveragingTouchFilter.historyEnd[id];
+
+ int64_t deltaX = x - mAveragingTouchFilter.historyData[end].pointers[id].x;
+ int64_t deltaY = y - mAveragingTouchFilter.historyData[end].pointers[id].y;
+ uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
+
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld",
+ id, distance);
+#endif
- if (mDisplayWidth < 0) {
- LOGD("Skipping part of touch screen configuration since display size is unknown.");
+ if (distance < AVERAGING_DISTANCE_LIMIT) {
+ // Increment end index in preparation for recording new historical data.
+ end += 1;
+ if (end > AVERAGING_HISTORY_SIZE) {
+ end = 0;
+ }
- device->touchScreen.precalculated.xScale = 1.0f;
- device->touchScreen.precalculated.yScale = 1.0f;
- } else {
- LOGI("Device configured: id=0x%x, name=%s (display size was changed)", device->id,
- device->name.string());
+ // If the end index has looped back to the start index then we have filled
+ // the historical trace up to the desired size so we drop the historical
+ // data at the start of the trace.
+ if (end == start) {
+ start += 1;
+ if (start > AVERAGING_HISTORY_SIZE) {
+ start = 0;
+ }
+ }
- device->touchScreen.precalculated.xScale =
- float(mDisplayWidth) / device->touchScreen.parameters.xAxis.range;
- device->touchScreen.precalculated.yScale =
- float(mDisplayHeight) / device->touchScreen.parameters.yAxis.range;
+ // Add the raw data to the historical trace.
+ mAveragingTouchFilter.historyStart[id] = start;
+ mAveragingTouchFilter.historyEnd[id] = end;
+ mAveragingTouchFilter.historyData[end].pointers[id].x = x;
+ mAveragingTouchFilter.historyData[end].pointers[id].y = y;
+ mAveragingTouchFilter.historyData[end].pointers[id].pressure = pressure;
+
+ // Average over all historical positions in the trace by total pressure.
+ int32_t averagedX = 0;
+ int32_t averagedY = 0;
+ int32_t totalPressure = 0;
+ for (;;) {
+ int32_t historicalX = mAveragingTouchFilter.historyData[start].pointers[id].x;
+ int32_t historicalY = mAveragingTouchFilter.historyData[start].pointers[id].y;
+ int32_t historicalPressure = mAveragingTouchFilter.historyData[start]
+ .pointers[id].pressure;
+
+ averagedX += historicalX * historicalPressure;
+ averagedY += historicalY * historicalPressure;
+ totalPressure += historicalPressure;
+
+ if (start == end) {
+ break;
+ }
+
+ start += 1;
+ if (start > AVERAGING_HISTORY_SIZE) {
+ start = 0;
+ }
+ }
+
+ averagedX /= totalPressure;
+ averagedY /= totalPressure;
+
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - "
+ "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure,
+ averagedX, averagedY);
+#endif
- configureVirtualKeys(device);
+ mCurrentTouch.pointers[currentIndex].x = averagedX;
+ mCurrentTouch.pointers[currentIndex].y = averagedY;
+ } else {
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id);
+#endif
}
} else {
- device->touchScreen.precalculated.xOrigin = 0;
- device->touchScreen.precalculated.xScale = 1.0f;
- device->touchScreen.precalculated.yOrigin = 0;
- device->touchScreen.precalculated.yScale = 1.0f;
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id);
+#endif
}
+
+ // Reset pointer history.
+ mAveragingTouchFilter.historyStart[id] = 0;
+ mAveragingTouchFilter.historyEnd[id] = 0;
+ mAveragingTouchFilter.historyData[0].pointers[id].x = x;
+ mAveragingTouchFilter.historyData[0].pointers[id].y = y;
+ mAveragingTouchFilter.historyData[0].pointers[id].pressure = pressure;
}
}
-void InputReader::configureVirtualKeys(InputDevice* device) {
- assert(device->touchScreen.parameters.xAxis.valid
- && device->touchScreen.parameters.yAxis.valid);
-
- device->touchScreen.virtualKeys.clear();
-
- Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions;
- mPolicy->getVirtualKeyDefinitions(device->name, virtualKeyDefinitions);
- if (virtualKeyDefinitions.size() == 0) {
- return;
- }
+int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ { // acquire virtual key lock
+ AutoMutex _l(mVirtualKeyLock);
- device->touchScreen.virtualKeys.setCapacity(virtualKeyDefinitions.size());
+ if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) {
+ return AKEY_STATE_VIRTUAL;
+ }
- int32_t touchScreenLeft = device->touchScreen.parameters.xAxis.minValue;
- int32_t touchScreenTop = device->touchScreen.parameters.yAxis.minValue;
- int32_t touchScreenWidth = device->touchScreen.parameters.xAxis.range;
- int32_t touchScreenHeight = device->touchScreen.parameters.yAxis.range;
+ for (size_t i = 0; i < mVirtualKeys.size(); i++) {
+ const VirtualKey& virtualKey = mVirtualKeys[i];
+ if (virtualKey.keyCode == keyCode) {
+ return AKEY_STATE_UP;
+ }
+ }
+ } // release virtual key lock
- for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
- const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition =
- virtualKeyDefinitions[i];
+ return AKEY_STATE_UNKNOWN;
+}
- device->touchScreen.virtualKeys.add();
- InputDevice::VirtualKey& virtualKey =
- device->touchScreen.virtualKeys.editTop();
+int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ { // acquire virtual key lock
+ AutoMutex _l(mVirtualKeyLock);
- virtualKey.scanCode = virtualKeyDefinition.scanCode;
- int32_t keyCode;
- uint32_t flags;
- if (mEventHub->scancodeToKeycode(device->id, virtualKey.scanCode,
- & keyCode, & flags)) {
- LOGW(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode);
- device->touchScreen.virtualKeys.pop(); // drop the key
- continue;
+ if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) {
+ return AKEY_STATE_VIRTUAL;
}
- virtualKey.keyCode = keyCode;
- virtualKey.flags = flags;
+ for (size_t i = 0; i < mVirtualKeys.size(); i++) {
+ const VirtualKey& virtualKey = mVirtualKeys[i];
+ if (virtualKey.scanCode == scanCode) {
+ return AKEY_STATE_UP;
+ }
+ }
+ } // release virtual key lock
- // convert the key definition's display coordinates into touch coordinates for a hit box
- int32_t halfWidth = virtualKeyDefinition.width / 2;
- int32_t halfHeight = virtualKeyDefinition.height / 2;
+ return AKEY_STATE_UNKNOWN;
+}
- virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth)
- * touchScreenWidth / mDisplayWidth + touchScreenLeft;
- virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth)
- * touchScreenWidth / mDisplayWidth + touchScreenLeft;
- virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight)
- * touchScreenHeight / mDisplayHeight + touchScreenTop;
- virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
- * touchScreenHeight / mDisplayHeight + touchScreenTop;
+bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ { // acquire virtual key lock
+ AutoMutex _l(mVirtualKeyLock);
- LOGI(" VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d",
- virtualKey.scanCode, virtualKey.keyCode,
- virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom);
- }
-}
+ for (size_t i = 0; i < mVirtualKeys.size(); i++) {
+ const VirtualKey& virtualKey = mVirtualKeys[i];
-void InputReader::configureAbsoluteAxisInfo(InputDevice* device,
- int axis, const char* name, InputDevice::AbsoluteAxisInfo* out) {
- if (! mEventHub->getAbsoluteInfo(device->id, axis,
- & out->minValue, & out->maxValue, & out->flat, &out->fuzz)) {
- out->range = out->maxValue - out->minValue;
- if (out->range != 0) {
- LOGI(" %s: min=%d max=%d flat=%d fuzz=%d",
- name, out->minValue, out->maxValue, out->flat, out->fuzz);
- out->valid = true;
- return;
+ for (size_t i = 0; i < numCodes; i++) {
+ if (virtualKey.keyCode == keyCodes[i]) {
+ outFlags[i] = 1;
+ }
+ }
}
- }
+ } // release virtual key lock
- out->valid = false;
- out->minValue = 0;
- out->maxValue = 0;
- out->flat = 0;
- out->fuzz = 0;
- out->range = 0;
- LOGI(" %s: unknown axis values, marking as invalid", name);
+ return true;
}
-void InputReader::configureExcludedDevices() {
- Vector<String8> excludedDeviceNames;
- mPolicy->getExcludedDeviceNames(excludedDeviceNames);
- for (size_t i = 0; i < excludedDeviceNames.size(); i++) {
- mEventHub->addExcludedDevice(excludedDeviceNames[i]);
- }
+// --- SingleTouchInputMapper ---
+
+SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+ TouchInputMapper(device, associatedDisplayId) {
+ initialize();
}
-void InputReader::resetGlobalMetaState() {
- mGlobalMetaState = -1;
+SingleTouchInputMapper::~SingleTouchInputMapper() {
}
-int32_t InputReader::globalMetaState() {
- if (mGlobalMetaState == -1) {
- mGlobalMetaState = 0;
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
- if (device->isKeyboard()) {
- mGlobalMetaState |= device->keyboard.current.metaState;
+void SingleTouchInputMapper::initialize() {
+ mAccumulator.clear();
+
+ mDown = false;
+ mX = 0;
+ mY = 0;
+ mPressure = 0;
+ mSize = 0;
+}
+
+void SingleTouchInputMapper::reset() {
+ TouchInputMapper::reset();
+
+ // Reinitialize.
+ initialize();
+ }
+
+void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_KEY:
+ switch (rawEvent->scanCode) {
+ case BTN_TOUCH:
+ mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH;
+ mAccumulator.btnTouch = rawEvent->value != 0;
+
+ sync(rawEvent->when);
+ mAccumulator.clear();
+ break;
+ }
+ break;
+
+ 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_PRESSURE:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_PRESSURE;
+ mAccumulator.absPressure = rawEvent->value;
+ break;
+ case ABS_TOOL_WIDTH:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_TOOL_WIDTH;
+ mAccumulator.absToolWidth = rawEvent->value;
+ break;
+ }
+ break;
+
+ case EV_SYN:
+ switch (rawEvent->scanCode) {
+ case SYN_REPORT:
+ if (mAccumulator.isDirty()) {
+ sync(rawEvent->when);
+ mAccumulator.clear();
}
+ break;
}
+ break;
}
- return mGlobalMetaState;
}
-void InputReader::updateExportedVirtualKeyState() {
- int32_t keyCode = -1, scanCode = -1;
+void SingleTouchInputMapper::sync(nsecs_t when) {
+ /* Update device state */
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
- if (device->isTouchScreen()) {
- if (device->touchScreen.currentVirtualKey.status
- == InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN) {
- keyCode = device->touchScreen.currentVirtualKey.keyCode;
- scanCode = device->touchScreen.currentVirtualKey.scanCode;
- }
- }
+ uint32_t fields = mAccumulator.fields;
+
+ if (fields & Accumulator::FIELD_BTN_TOUCH) {
+ mDown = mAccumulator.btnTouch;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_X) {
+ mX = mAccumulator.absX;
}
- { // acquire exported state lock
- AutoMutex _l(mExportedStateLock);
+ if (fields & Accumulator::FIELD_ABS_Y) {
+ mY = mAccumulator.absY;
+ }
- mExportedVirtualKeyCode = keyCode;
- mExportedVirtualScanCode = scanCode;
- } // release exported state lock
+ if (fields & Accumulator::FIELD_ABS_PRESSURE) {
+ mPressure = mAccumulator.absPressure;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_TOOL_WIDTH) {
+ mSize = mAccumulator.absToolWidth;
+ }
+
+ mCurrentTouch.clear();
+
+ if (mDown) {
+ mCurrentTouch.pointerCount = 1;
+ mCurrentTouch.pointers[0].id = 0;
+ mCurrentTouch.pointers[0].x = mX;
+ mCurrentTouch.pointers[0].y = mY;
+ mCurrentTouch.pointers[0].pressure = mPressure;
+ mCurrentTouch.pointers[0].size = mSize;
+ mCurrentTouch.pointers[0].touchMajor = mPressure;
+ mCurrentTouch.pointers[0].touchMinor = mPressure;
+ mCurrentTouch.pointers[0].toolMajor = mSize;
+ mCurrentTouch.pointers[0].toolMinor = mSize;
+ mCurrentTouch.pointers[0].orientation = 0;
+ mCurrentTouch.idToIndex[0] = 0;
+ mCurrentTouch.idBits.markBit(0);
+ }
+
+ syncTouch(when, true);
}
-bool InputReader::getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const {
- { // acquire exported state lock
- AutoMutex _l(mExportedStateLock);
+void SingleTouchInputMapper::configureAxes() {
+ TouchInputMapper::configureAxes();
- *outKeyCode = mExportedVirtualKeyCode;
- *outScanCode = mExportedVirtualScanCode;
- return mExportedVirtualKeyCode != -1;
- } // release exported state lock
+ // The axes are aliased to take into account the manner in which they are presented
+ // as part of the TouchData during the sync.
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mAxes.x);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mAxes.y);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mAxes.pressure);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mAxes.size);
+
+ mAxes.touchMajor = mAxes.pressure;
+ mAxes.touchMinor = mAxes.pressure;
+ mAxes.toolMajor = mAxes.size;
+ mAxes.toolMinor = mAxes.size;
}
-void InputReader::updateExportedInputConfiguration() {
- int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH;
- int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS;
- int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV;
- for (size_t i = 0; i < mDevices.size(); i++) {
- InputDevice* device = mDevices.valueAt(i);
- int32_t deviceClasses = device->classes;
+// --- MultiTouchInputMapper ---
+
+MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+ TouchInputMapper(device, associatedDisplayId) {
+ initialize();
+}
+
+MultiTouchInputMapper::~MultiTouchInputMapper() {
+}
+
+void MultiTouchInputMapper::initialize() {
+ mAccumulator.clear();
+}
+
+void MultiTouchInputMapper::reset() {
+ TouchInputMapper::reset();
- if (deviceClasses & INPUT_DEVICE_CLASS_TOUCHSCREEN) {
- touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER;
+ // Reinitialize.
+ initialize();
+}
+
+void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_ABS: {
+ uint32_t pointerIndex = mAccumulator.pointerCount;
+ Accumulator::Pointer* pointer = & mAccumulator.pointers[pointerIndex];
+
+ switch (rawEvent->scanCode) {
+ case ABS_MT_POSITION_X:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_X;
+ pointer->absMTPositionX = rawEvent->value;
+ break;
+ case ABS_MT_POSITION_Y:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_Y;
+ pointer->absMTPositionY = rawEvent->value;
+ break;
+ case ABS_MT_TOUCH_MAJOR:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MAJOR;
+ pointer->absMTTouchMajor = rawEvent->value;
+ break;
+ case ABS_MT_TOUCH_MINOR:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MINOR;
+ pointer->absMTTouchMinor = rawEvent->value;
+ break;
+ case ABS_MT_WIDTH_MAJOR:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
+ pointer->absMTWidthMajor = rawEvent->value;
+ break;
+ case ABS_MT_WIDTH_MINOR:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MINOR;
+ pointer->absMTWidthMinor = rawEvent->value;
+ break;
+ case ABS_MT_ORIENTATION:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_ORIENTATION;
+ pointer->absMTOrientation = rawEvent->value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID;
+ pointer->absMTTrackingId = rawEvent->value;
+ break;
}
- if (deviceClasses & INPUT_DEVICE_CLASS_ALPHAKEY) {
- keyboardConfig = InputConfiguration::KEYBOARD_QWERTY;
+ break;
+ }
+
+ case EV_SYN:
+ switch (rawEvent->scanCode) {
+ case SYN_MT_REPORT: {
+ // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
+ uint32_t pointerIndex = mAccumulator.pointerCount;
+
+ if (mAccumulator.pointers[pointerIndex].fields) {
+ if (pointerIndex == MAX_POINTERS) {
+ LOGW("MultiTouch device driver returned more than maximum of %d pointers.",
+ MAX_POINTERS);
+ } else {
+ pointerIndex += 1;
+ mAccumulator.pointerCount = pointerIndex;
+ }
+ }
+
+ mAccumulator.pointers[pointerIndex].clear();
+ break;
}
- if (deviceClasses & INPUT_DEVICE_CLASS_TRACKBALL) {
- navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL;
- } else if (deviceClasses & INPUT_DEVICE_CLASS_DPAD) {
- navigationConfig = InputConfiguration::NAVIGATION_DPAD;
+
+ case SYN_REPORT:
+ if (mAccumulator.isDirty()) {
+ sync(rawEvent->when);
+ mAccumulator.clear();
+ }
+ break;
}
+ break;
}
+}
- { // acquire exported state lock
- AutoMutex _l(mExportedStateLock);
+void MultiTouchInputMapper::sync(nsecs_t when) {
+ static const uint32_t REQUIRED_FIELDS =
+ Accumulator::FIELD_ABS_MT_POSITION_X
+ | Accumulator::FIELD_ABS_MT_POSITION_Y
+ | Accumulator::FIELD_ABS_MT_TOUCH_MAJOR
+ | Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
- mExportedInputConfiguration.touchScreen = touchScreenConfig;
- mExportedInputConfiguration.keyboard = keyboardConfig;
- mExportedInputConfiguration.navigation = navigationConfig;
- } // release exported state lock
-}
+ /* Update device state */
+
+ uint32_t inCount = mAccumulator.pointerCount;
+ uint32_t outCount = 0;
+ bool havePointerIds = true;
-void InputReader::getCurrentInputConfiguration(InputConfiguration* outConfiguration) const {
- { // acquire exported state lock
- AutoMutex _l(mExportedStateLock);
+ mCurrentTouch.clear();
- *outConfiguration = mExportedInputConfiguration;
- } // release exported state lock
-}
+ for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) {
+ uint32_t fields = mAccumulator.pointers[inIndex].fields;
-int32_t InputReader::getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t scanCode) const {
- { // acquire exported state lock
- AutoMutex _l(mExportedStateLock);
+ if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) {
+#if DEBUG_POINTERS
+ LOGD("Pointers: Missing required multitouch pointer fields: index=%d, fields=%d",
+ inIndex, fields);
+ continue;
+#endif
+ }
- if (mExportedVirtualScanCode == scanCode) {
- return AKEY_STATE_VIRTUAL;
+ if (mAccumulator.pointers[inIndex].absMTTouchMajor <= 0) {
+ // Pointer is not down. Drop it.
+ continue;
}
- } // release exported state lock
- return mEventHub->getScanCodeState(deviceId, deviceClasses, scanCode);
-}
+ mCurrentTouch.pointers[outCount].x = mAccumulator.pointers[inIndex].absMTPositionX;
+ mCurrentTouch.pointers[outCount].y = mAccumulator.pointers[inIndex].absMTPositionY;
-int32_t InputReader::getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses,
- int32_t keyCode) const {
- { // acquire exported state lock
- AutoMutex _l(mExportedStateLock);
+ mCurrentTouch.pointers[outCount].touchMajor =
+ mAccumulator.pointers[inIndex].absMTTouchMajor;
+ mCurrentTouch.pointers[outCount].touchMinor =
+ (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) != 0
+ ? mAccumulator.pointers[inIndex].absMTTouchMinor
+ : mAccumulator.pointers[inIndex].absMTTouchMajor;
- if (mExportedVirtualKeyCode == keyCode) {
- return AKEY_STATE_VIRTUAL;
+ mCurrentTouch.pointers[outCount].toolMajor =
+ mAccumulator.pointers[inIndex].absMTWidthMajor;
+ mCurrentTouch.pointers[outCount].toolMinor =
+ (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) != 0
+ ? mAccumulator.pointers[inIndex].absMTWidthMinor
+ : mAccumulator.pointers[inIndex].absMTWidthMajor;
+
+ mCurrentTouch.pointers[outCount].orientation =
+ (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) != 0
+ ? mAccumulator.pointers[inIndex].absMTOrientation : 0;
+
+ // Derive an approximation of pressure and size.
+ // FIXME assignment of pressure may be incorrect, probably better to let
+ // pressure = touch / width. Later on we pass width to MotionEvent as a size, which
+ // isn't quite right either. Should be using touch for that.
+ mCurrentTouch.pointers[outCount].pressure = mAccumulator.pointers[inIndex].absMTTouchMajor;
+ mCurrentTouch.pointers[outCount].size = mAccumulator.pointers[inIndex].absMTWidthMajor;
+
+ if (havePointerIds) {
+ if (fields & Accumulator::
+ FIELD_ABS_MT_TRACKING_ID) {
+ uint32_t id = uint32_t(mAccumulator.pointers[inIndex].absMTTrackingId);
+
+ if (id > MAX_POINTER_ID) {
+#if DEBUG_POINTERS
+ LOGD("Pointers: Ignoring driver provided pointer id %d because "
+ "it is larger than max supported id %d for optimizations",
+ id, MAX_POINTER_ID);
+#endif
+ havePointerIds = false;
+ }
+ else {
+ mCurrentTouch.pointers[outCount].id = id;
+ mCurrentTouch.idToIndex[id] = outCount;
+ mCurrentTouch.idBits.markBit(id);
+ }
+ } else {
+ havePointerIds = false;
+ }
}
- } // release exported state lock
- return mEventHub->getKeyCodeState(deviceId, deviceClasses, keyCode);
-}
+ outCount += 1;
+ }
-int32_t InputReader::getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses,
- int32_t sw) const {
- return mEventHub->getSwitchState(deviceId, deviceClasses, sw);
-}
+ mCurrentTouch.pointerCount = outCount;
-bool InputReader::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const {
- return mEventHub->hasKeys(numCodes, keyCodes, outFlags);
+ syncTouch(when, havePointerIds);
}
+void MultiTouchInputMapper::configureAxes() {
+ TouchInputMapper::configureAxes();
-// --- InputReaderThread ---
+ // The axes are aliased to take into account the manner in which they are presented
+ // as part of the TouchData during the sync.
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mAxes.x);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mAxes.y);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mAxes.touchMajor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mAxes.touchMinor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mAxes.toolMajor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mAxes.toolMinor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mAxes.orientation);
-InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
- Thread(/*canCallJava*/ true), mReader(reader) {
-}
+ if (! mAxes.touchMinor.valid) {
+ mAxes.touchMinor = mAxes.touchMajor;
+ }
-InputReaderThread::~InputReaderThread() {
-}
+ if (! mAxes.toolMinor.valid) {
+ mAxes.toolMinor = mAxes.toolMajor;
+ }
-bool InputReaderThread::threadLoop() {
- mReader->loopOnce();
- return true;
+ mAxes.pressure = mAxes.touchMajor;
+ mAxes.size = mAxes.toolMajor;
}
+
} // namespace android
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 8bd582353d01..2e20268b3ef4 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -33,6 +33,7 @@ commonSources:= \
SharedBuffer.cpp \
Static.cpp \
StopWatch.cpp \
+ StreamingZipInflater.cpp \
String8.cpp \
String16.cpp \
StringArray.cpp \
@@ -131,4 +132,4 @@ endif
# team really wants is to build the stuff defined by this makefile.
ifeq (,$(ONE_SHOT_MAKEFILE))
include $(call first-makefiles-under,$(LOCAL_PATH))
-endif \ No newline at end of file
+endif
diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp
index 42951237d627..cef7db492e55 100644
--- a/libs/utils/Asset.cpp
+++ b/libs/utils/Asset.cpp
@@ -24,6 +24,7 @@
#include <utils/Asset.h>
#include <utils/Atomic.h>
#include <utils/FileMap.h>
+#include <utils/StreamingZipInflater.h>
#include <utils/ZipUtils.h>
#include <utils/ZipFileRO.h>
#include <utils/Log.h>
@@ -659,7 +660,7 @@ const void* _FileAsset::ensureAlignment(FileMap* map)
*/
_CompressedAsset::_CompressedAsset(void)
: mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
- mMap(NULL), mFd(-1), mBuf(NULL)
+ mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
{
}
@@ -698,6 +699,10 @@ status_t _CompressedAsset::openChunk(int fd, off_t offset,
mFd = fd;
assert(mBuf == NULL);
+ if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
+ mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
+ }
+
return NO_ERROR;
}
@@ -724,6 +729,9 @@ status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod,
mUncompressedLen = uncompressedLen;
assert(mOffset == 0);
+ if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
+ mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
+ }
return NO_ERROR;
}
@@ -739,26 +747,29 @@ ssize_t _CompressedAsset::read(void* buf, size_t count)
assert(mOffset >= 0 && mOffset <= mUncompressedLen);
- // TODO: if mAccessMode == ACCESS_STREAMING, use zlib more cleverly
+ /* If we're relying on a streaming inflater, go through that */
+ if (mZipInflater) {
+ actual = mZipInflater->read(buf, count);
+ } else {
+ if (mBuf == NULL) {
+ if (getBuffer(false) == NULL)
+ return -1;
+ }
+ assert(mBuf != NULL);
- if (mBuf == NULL) {
- if (getBuffer(false) == NULL)
- return -1;
- }
- assert(mBuf != NULL);
+ /* adjust count if we're near EOF */
+ maxLen = mUncompressedLen - mOffset;
+ if (count > maxLen)
+ count = maxLen;
- /* adjust count if we're near EOF */
- maxLen = mUncompressedLen - mOffset;
- if (count > maxLen)
- count = maxLen;
+ if (!count)
+ return 0;
- if (!count)
- return 0;
-
- /* copy from buffer */
- //printf("comp buf read\n");
- memcpy(buf, (char*)mBuf + mOffset, count);
- actual = count;
+ /* copy from buffer */
+ //printf("comp buf read\n");
+ memcpy(buf, (char*)mBuf + mOffset, count);
+ actual = count;
+ }
mOffset += actual;
return actual;
@@ -780,6 +791,9 @@ off_t _CompressedAsset::seek(off_t offset, int whence)
if (newPosn == (off_t) -1)
return newPosn;
+ if (mZipInflater) {
+ mZipInflater->seekAbsolute(newPosn);
+ }
mOffset = newPosn;
return mOffset;
}
@@ -793,10 +807,12 @@ void _CompressedAsset::close(void)
mMap->release();
mMap = NULL;
}
- if (mBuf != NULL) {
- delete[] mBuf;
- mBuf = NULL;
- }
+
+ delete[] mBuf;
+ mBuf = NULL;
+
+ delete mZipInflater;
+ mZipInflater = NULL;
if (mFd > 0) {
::close(mFd);
@@ -817,12 +833,6 @@ const void* _CompressedAsset::getBuffer(bool wordAligned)
if (mBuf != NULL)
return mBuf;
- if (mUncompressedLen > UNCOMPRESS_DATA_MAX) {
- LOGD("Data exceeds UNCOMPRESS_DATA_MAX (%ld vs %d)\n",
- (long) mUncompressedLen, UNCOMPRESS_DATA_MAX);
- goto bail;
- }
-
/*
* Allocate a buffer and read the file into it.
*/
@@ -853,7 +863,13 @@ const void* _CompressedAsset::getBuffer(bool wordAligned)
goto bail;
}
- /* success! */
+ /*
+ * Success - now that we have the full asset in RAM we
+ * no longer need the streaming inflater
+ */
+ delete mZipInflater;
+ mZipInflater = NULL;
+
mBuf = buf;
buf = NULL;
diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp
new file mode 100644
index 000000000000..7ebde78cd85b
--- /dev/null
+++ b/libs/utils/StreamingZipInflater.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "szipinf"
+#include <utils/Log.h>
+
+#include <utils/FileMap.h>
+#include <utils/StreamingZipInflater.h>
+#include <string.h>
+#include <stddef.h>
+#include <assert.h>
+
+static inline size_t min_of(size_t a, size_t b) { return (a < b) ? a : b; }
+
+using namespace android;
+
+/*
+ * Streaming access to compressed asset data in an open fd
+ */
+StreamingZipInflater::StreamingZipInflater(int fd, off_t compDataStart,
+ size_t uncompSize, size_t compSize) {
+ mFd = fd;
+ mDataMap = NULL;
+ mInFileStart = compDataStart;
+ mOutTotalSize = uncompSize;
+ mInTotalSize = compSize;
+
+ mInBufSize = StreamingZipInflater::INPUT_CHUNK_SIZE;
+ mInBuf = new uint8_t[mInBufSize];
+
+ mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE;
+ mOutBuf = new uint8_t[mOutBufSize];
+
+ initInflateState();
+}
+
+/*
+ * Streaming access to compressed data held in an mmapped region of memory
+ */
+StreamingZipInflater::StreamingZipInflater(FileMap* dataMap, size_t uncompSize) {
+ mFd = -1;
+ mDataMap = dataMap;
+ mOutTotalSize = uncompSize;
+ mInTotalSize = dataMap->getDataLength();
+
+ mInBuf = (uint8_t*) dataMap->getDataPtr();
+ mInBufSize = mInTotalSize;
+
+ mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE;
+ mOutBuf = new uint8_t[mOutBufSize];
+
+ initInflateState();
+}
+
+StreamingZipInflater::~StreamingZipInflater() {
+ // tear down the in-flight zip state just in case
+ ::inflateEnd(&mInflateState);
+
+ if (mDataMap == NULL) {
+ delete [] mInBuf;
+ }
+ delete [] mOutBuf;
+}
+
+void StreamingZipInflater::initInflateState() {
+ LOGD("Initializing inflate state");
+
+ memset(&mInflateState, 0, sizeof(mInflateState));
+ mInflateState.zalloc = Z_NULL;
+ mInflateState.zfree = Z_NULL;
+ mInflateState.opaque = Z_NULL;
+ mInflateState.next_in = (Bytef*)mInBuf;
+ mInflateState.next_out = (Bytef*) mOutBuf;
+ mInflateState.avail_out = mOutBufSize;
+ mInflateState.data_type = Z_UNKNOWN;
+
+ mOutLastDecoded = mOutDeliverable = mOutCurPosition = 0;
+ mInNextChunkOffset = 0;
+ mStreamNeedsInit = true;
+
+ if (mDataMap == NULL) {
+ ::lseek(mFd, mInFileStart, SEEK_SET);
+ mInflateState.avail_in = 0; // set when a chunk is read in
+ } else {
+ mInflateState.avail_in = mInBufSize;
+ }
+}
+
+/*
+ * Basic approach:
+ *
+ * 1. If we have undelivered uncompressed data, send it. At this point
+ * either we've satisfied the request, or we've exhausted the available
+ * output data in mOutBuf.
+ *
+ * 2. While we haven't sent enough data to satisfy the request:
+ * 0. if the request is for more data than exists, bail.
+ * a. if there is no input data to decode, read some into the input buffer
+ * and readjust the z_stream input pointers
+ * b. point the output to the start of the output buffer and decode what we can
+ * c. deliver whatever output data we can
+ */
+ssize_t StreamingZipInflater::read(void* outBuf, size_t count) {
+ uint8_t* dest = (uint8_t*) outBuf;
+ size_t bytesRead = 0;
+ size_t toRead = min_of(count, size_t(mOutTotalSize - mOutCurPosition));
+ while (toRead > 0) {
+ // First, write from whatever we already have decoded and ready to go
+ size_t deliverable = min_of(toRead, mOutLastDecoded - mOutDeliverable);
+ if (deliverable > 0) {
+ if (outBuf != NULL) memcpy(dest, mOutBuf + mOutDeliverable, deliverable);
+ mOutDeliverable += deliverable;
+ mOutCurPosition += deliverable;
+ dest += deliverable;
+ bytesRead += deliverable;
+ toRead -= deliverable;
+ }
+
+ // need more data? time to decode some.
+ if (toRead > 0) {
+ // if we don't have any data to decode, read some in. If we're working
+ // from mmapped data this won't happen, because the clipping to total size
+ // will prevent reading off the end of the mapped input chunk.
+ if (mInflateState.avail_in == 0) {
+ int err = readNextChunk();
+ if (err < 0) {
+ LOGE("Unable to access asset data: %d", err);
+ if (!mStreamNeedsInit) {
+ ::inflateEnd(&mInflateState);
+ initInflateState();
+ }
+ return -1;
+ }
+ }
+ // we know we've drained whatever is in the out buffer now, so just
+ // start from scratch there, reading all the input we have at present.
+ mInflateState.next_out = (Bytef*) mOutBuf;
+ mInflateState.avail_out = mOutBufSize;
+
+ /*
+ LOGD("Inflating to outbuf: avail_in=%u avail_out=%u next_in=%p next_out=%p",
+ mInflateState.avail_in, mInflateState.avail_out,
+ mInflateState.next_in, mInflateState.next_out);
+ */
+ int result = Z_OK;
+ if (mStreamNeedsInit) {
+ LOGI("Initializing zlib to inflate");
+ result = inflateInit2(&mInflateState, -MAX_WBITS);
+ mStreamNeedsInit = false;
+ }
+ if (result == Z_OK) result = ::inflate(&mInflateState, Z_SYNC_FLUSH);
+ if (result < 0) {
+ // Whoops, inflation failed
+ LOGE("Error inflating asset: %d", result);
+ ::inflateEnd(&mInflateState);
+ initInflateState();
+ return -1;
+ } else {
+ if (result == Z_STREAM_END) {
+ // we know we have to have reached the target size here and will
+ // not try to read any further, so just wind things up.
+ ::inflateEnd(&mInflateState);
+ }
+
+ // Note how much data we got, and off we go
+ mOutDeliverable = 0;
+ mOutLastDecoded = mOutBufSize - mInflateState.avail_out;
+ }
+ }
+ }
+ return bytesRead;
+}
+
+int StreamingZipInflater::readNextChunk() {
+ assert(mDataMap == NULL);
+
+ if (mInNextChunkOffset < mInTotalSize) {
+ size_t toRead = min_of(mInBufSize, mInTotalSize - mInNextChunkOffset);
+ if (toRead > 0) {
+ ssize_t didRead = ::read(mFd, mInBuf, toRead);
+ //LOGD("Reading input chunk, size %08x didread %08x", toRead, didRead);
+ if (didRead < 0) {
+ // TODO: error
+ LOGE("Error reading asset data");
+ return didRead;
+ } else {
+ mInNextChunkOffset += didRead;
+ mInflateState.next_in = (Bytef*) mInBuf;
+ mInflateState.avail_in = didRead;
+ }
+ }
+ }
+ return 0;
+}
+
+// seeking backwards requires uncompressing fom the beginning, so is very
+// expensive. seeking forwards only requires uncompressing from the current
+// position to the destination.
+off_t StreamingZipInflater::seekAbsolute(off_t absoluteInputPosition) {
+ if (absoluteInputPosition < mOutCurPosition) {
+ // rewind and reprocess the data from the beginning
+ if (!mStreamNeedsInit) {
+ ::inflateEnd(&mInflateState);
+ }
+ initInflateState();
+ read(NULL, absoluteInputPosition);
+ } else if (absoluteInputPosition > mOutCurPosition) {
+ read(NULL, absoluteInputPosition - mOutCurPosition);
+ }
+ // else if the target position *is* our current position, do nothing
+ return absoluteInputPosition;
+}
diff --git a/media/java/android/media/BassBoost.java b/media/java/android/media/BassBoost.java
index ef4ce059d1c7..75c2c88d21e9 100644
--- a/media/java/android/media/BassBoost.java
+++ b/media/java/android/media/BassBoost.java
@@ -19,17 +19,19 @@ package android.media;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.media.AudioEffect;
import android.os.Bundle;
import android.util.Log;
+
import java.nio.ByteOrder;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
+import java.util.StringTokenizer;
-import android.media.AudioEffect;
/**
* Bass boost is an audio effect to boost or amplify low frequencies of the sound. It is comparable
- * to an simple equalizer but limited to one band amplification in the low frequency range.
+ * to a simple equalizer but limited to one band amplification in the low frequency range.
* <p>An application creates a BassBoost object to instantiate and control a bass boost engine
* in the audio framework.
* <p>The methods, parameter types and units exposed by the BassBoost implementation are directly
@@ -210,4 +212,82 @@ public class BassBoost extends AudioEffect {
}
}
}
+
+ /**
+ * The Settings class regroups all bass boost parameters. It is used in
+ * conjuntion with getProperties() and setProperties() methods to backup and restore
+ * all parameters in a single call.
+ */
+ public static class Settings {
+ public short strength;
+
+ public Settings() {
+ }
+
+ /**
+ * Settings class constructor from a key=value; pairs formatted string. The string is
+ * typically returned by Settings.toString() method.
+ * @throws IllegalArgumentException if the string is not correctly formatted.
+ */
+ public Settings(String settings) {
+ StringTokenizer st = new StringTokenizer(settings, "=;");
+ int tokens = st.countTokens();
+ if (st.countTokens() != 3) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ String key = st.nextToken();
+ if (!key.equals("BassBoost")) {
+ throw new IllegalArgumentException(
+ "invalid settings for BassBoost: " + key);
+ }
+ try {
+ key = st.nextToken();
+ if (!key.equals("strength")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ strength = Short.parseShort(st.nextToken());
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("invalid value for key: " + key);
+ }
+ }
+
+ @Override
+ public String toString() {
+ String str = new String (
+ "BassBoost"+
+ ";strength="+Short.toString(strength)
+ );
+ return str;
+ }
+ };
+
+
+ /**
+ * Gets the bass boost properties. This method is useful when a snapshot of current
+ * bass boost settings must be saved by the application.
+ * @return a BassBoost.Settings object containing all current parameters values
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public BassBoost.Settings getProperties()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ Settings settings = new Settings();
+ short[] value = new short[1];
+ checkStatus(getParameter(PARAM_STRENGTH, value));
+ settings.strength = value[0];
+ return settings;
+ }
+
+ /**
+ * Sets the bass boost properties. This method is useful when bass boost settings have to
+ * be applied from a previous backup.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setProperties(BassBoost.Settings settings)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_STRENGTH, settings.strength));
+ }
}
diff --git a/media/java/android/media/EnvironmentalReverb.java b/media/java/android/media/EnvironmentalReverb.java
index 88230fc51f5d..3cc84527c15a 100644
--- a/media/java/android/media/EnvironmentalReverb.java
+++ b/media/java/android/media/EnvironmentalReverb.java
@@ -19,12 +19,13 @@ package android.media;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.media.AudioEffect;
import android.os.Bundle;
import android.util.Log;
+
import java.nio.ByteOrder;
import java.nio.ByteBuffer;
-
-import android.media.AudioEffect;
+import java.util.StringTokenizer;
/**
* A sound generated within a room travels in many directions. The listener first hears the
@@ -107,6 +108,9 @@ public class EnvironmentalReverb extends AudioEffect {
*/
public static final int PARAM_DENSITY = 9;
+ // used by setProperties()/getProperties
+ private static final int PARAM_PROPERTIES = 10;
+
/**
* Registered listener for parameter changes
*/
@@ -142,7 +146,6 @@ public class EnvironmentalReverb extends AudioEffect {
public EnvironmentalReverb(int priority, int audioSession)
throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
super(EFFECT_TYPE_ENV_REVERB, EFFECT_TYPE_NULL, priority, audioSession);
- Log.e(TAG, "contructor");
}
/**
@@ -501,4 +504,169 @@ public class EnvironmentalReverb extends AudioEffect {
}
}
}
+
+ /**
+ * The Settings class regroups all environmental reverb parameters. It is used in
+ * conjuntion with getProperties() and setProperties() methods to backup and restore
+ * all parameters in a single call.
+ */
+ public static class Settings {
+ public short roomLevel;
+ public short roomHFLevel;
+ public int decayTime;
+ public short decayHFRatio;
+ public short reflectionsLevel;
+ public int reflectionsDelay;
+ public short reverbLevel;
+ public int reverbDelay;
+ public short diffusion;
+ public short density;
+
+ public Settings() {
+ }
+
+ /**
+ * Settings class constructor from a key=value; pairs formatted string. The string is
+ * typically returned by Settings.toString() method.
+ * @throws IllegalArgumentException if the string is not correctly formatted.
+ */
+ public Settings(String settings) {
+ StringTokenizer st = new StringTokenizer(settings, "=;");
+ int tokens = st.countTokens();
+ if (st.countTokens() != 21) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ String key = st.nextToken();
+ if (!key.equals("EnvironmentalReverb")) {
+ throw new IllegalArgumentException(
+ "invalid settings for EnvironmentalReverb: " + key);
+ }
+
+ try {
+ key = st.nextToken();
+ if (!key.equals("roomLevel")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ roomLevel = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("roomHFLevel")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ roomHFLevel = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("decayTime")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ decayTime = Integer.parseInt(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("decayHFRatio")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ decayHFRatio = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("reflectionsLevel")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ reflectionsLevel = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("reflectionsDelay")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ reflectionsDelay = Integer.parseInt(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("reverbLevel")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ reverbLevel = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("reverbDelay")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ reverbDelay = Integer.parseInt(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("diffusion")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ diffusion = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("density")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ density = Short.parseShort(st.nextToken());
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("invalid value for key: " + key);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return new String (
+ "EnvironmentalReverb"+
+ ";roomLevel="+Short.toString(roomLevel)+
+ ";roomHFLevel="+Short.toString(roomHFLevel)+
+ ";decayTime="+Integer.toString(decayTime)+
+ ";decayHFRatio="+Short.toString(decayHFRatio)+
+ ";reflectionsLevel="+Short.toString(reflectionsLevel)+
+ ";reflectionsDelay="+Integer.toString(reflectionsDelay)+
+ ";reverbLevel="+Short.toString(reverbLevel)+
+ ";reverbDelay="+Integer.toString(reverbDelay)+
+ ";diffusion="+Short.toString(diffusion)+
+ ";density="+Short.toString(density)
+ );
+ }
+ };
+
+ // Keep this in sync with sizeof(s_reverb_settings) defined in
+ // frameworks/base/include/media/EffectEnvironmentalReverbApi.h
+ static private int PROPERTY_SIZE = 26;
+
+ /**
+ * Gets the environmental reverb properties. This method is useful when a snapshot of current
+ * reverb settings must be saved by the application.
+ * @return an EnvironmentalReverb.Settings object containing all current parameters values
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public EnvironmentalReverb.Settings getProperties()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[PROPERTY_SIZE];
+ checkStatus(getParameter(PARAM_PROPERTIES, param));
+ Settings settings = new Settings();
+ settings.roomLevel = byteArrayToShort(param, 0);
+ settings.roomHFLevel = byteArrayToShort(param, 2);
+ settings.decayTime = byteArrayToInt(param, 4);
+ settings.decayHFRatio = byteArrayToShort(param, 8);
+ settings.reflectionsLevel = byteArrayToShort(param, 10);
+ settings.reflectionsDelay = byteArrayToInt(param, 12);
+ settings.reverbLevel = byteArrayToShort(param, 16);
+ settings.reverbDelay = byteArrayToInt(param, 18);
+ settings.diffusion = byteArrayToShort(param, 22);
+ settings.density = byteArrayToShort(param, 24);
+ return settings;
+ }
+
+ /**
+ * Sets the environmental reverb properties. This method is useful when reverb settings have to
+ * be applied from a previous backup.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setProperties(EnvironmentalReverb.Settings settings)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+
+ byte[] param = concatArrays(shortToByteArray(settings.roomLevel),
+ shortToByteArray(settings.roomHFLevel),
+ intToByteArray(settings.decayTime),
+ shortToByteArray(settings.decayHFRatio),
+ shortToByteArray(settings.reflectionsLevel),
+ intToByteArray(settings.reflectionsDelay),
+ shortToByteArray(settings.reverbLevel),
+ intToByteArray(settings.reverbDelay),
+ shortToByteArray(settings.diffusion),
+ shortToByteArray(settings.density));
+
+ checkStatus(setParameter(PARAM_PROPERTIES, param));
+ }
}
diff --git a/media/java/android/media/Equalizer.java b/media/java/android/media/Equalizer.java
index 082f69416fb4..21c37bb3dec8 100644
--- a/media/java/android/media/Equalizer.java
+++ b/media/java/android/media/Equalizer.java
@@ -19,13 +19,15 @@ package android.media;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.media.AudioEffect;
import android.os.Bundle;
import android.util.Log;
+
import java.nio.ByteOrder;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
+import java.util.StringTokenizer;
-import android.media.AudioEffect;
/**
* An Equalizer is used to alter the frequency response of a particular music source or of the main
@@ -87,12 +89,19 @@ public class Equalizer extends AudioEffect {
* Request preset name. Parameter ID for OnParameterChangeListener
*/
public static final int PARAM_GET_PRESET_NAME = 8;
+ // used by setProperties()/getProperties
+ private static final int PARAM_PROPERTIES = 9;
/**
* maximum size for perset name
*/
public static final int PARAM_STRING_SIZE_MAX = 32;
/**
+ * Number of bands implemented by Equalizer engine
+ */
+ private short mNumBands = 0;
+
+ /**
* Number of presets implemented by Equalizer engine
*/
private int mNumPresets;
@@ -136,6 +145,8 @@ public class Equalizer extends AudioEffect {
UnsupportedOperationException, RuntimeException {
super(EFFECT_TYPE_EQUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
+ getNumberOfBands();
+
mNumPresets = (int)getNumberOfPresets();
if (mNumPresets != 0) {
@@ -150,7 +161,6 @@ public class Equalizer extends AudioEffect {
while (value[length] != 0) length++;
try {
mPresetNames[i] = new String(value, 0, length, "ISO-8859-1");
- Log.e(TAG, "preset #: "+i+" name: "+mPresetNames[i]+" length: "+length);
} catch (java.io.UnsupportedEncodingException e) {
Log.e(TAG, "preset name decode error");
}
@@ -167,11 +177,15 @@ public class Equalizer extends AudioEffect {
*/
public short getNumberOfBands()
throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ if (mNumBands != 0) {
+ return mNumBands;
+ }
int[] param = new int[1];
param[0] = PARAM_NUM_BANDS;
short[] value = new short[1];
checkStatus(getParameter(param, value));
- return value[0];
+ mNumBands = value[0];
+ return mNumBands;
}
/**
@@ -440,4 +454,120 @@ public class Equalizer extends AudioEffect {
}
}
+ /**
+ * The Settings class regroups all equalizer parameters. It is used in
+ * conjuntion with getProperties() and setProperties() methods to backup and restore
+ * all parameters in a single call.
+ */
+ public static class Settings {
+ public short curPreset;
+ public short numBands = 0;
+ public short[] bandLevels = null;
+
+ public Settings() {
+ }
+
+ /**
+ * Settings class constructor from a key=value; pairs formatted string. The string is
+ * typically returned by Settings.toString() method.
+ * @throws IllegalArgumentException if the string is not correctly formatted.
+ */
+ public Settings(String settings) {
+ StringTokenizer st = new StringTokenizer(settings, "=;");
+ int tokens = st.countTokens();
+ if (st.countTokens() < 5) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ String key = st.nextToken();
+ if (!key.equals("Equalizer")) {
+ throw new IllegalArgumentException(
+ "invalid settings for Equalizer: " + key);
+ }
+ try {
+ key = st.nextToken();
+ if (!key.equals("curPreset")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ curPreset = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("numBands")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ numBands = Short.parseShort(st.nextToken());
+ if (st.countTokens() != numBands*2) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ bandLevels = new short[numBands];
+ for (int i = 0; i < numBands; i++) {
+ key = st.nextToken();
+ if (!key.equals("band"+(i+1)+"Level")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ bandLevels[i] = Short.parseShort(st.nextToken());
+ }
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("invalid value for key: " + key);
+ }
+ }
+
+ @Override
+ public String toString() {
+
+ String str = new String (
+ "Equalizer"+
+ ";curPreset="+Short.toString(curPreset)+
+ ";numBands="+Short.toString(numBands)
+ );
+ for (int i = 0; i < numBands; i++) {
+ str = str.concat(";band"+(i+1)+"Level="+Short.toString(bandLevels[i]));
+ }
+ return str;
+ }
+ };
+
+
+ /**
+ * Gets the equalizer properties. This method is useful when a snapshot of current
+ * equalizer settings must be saved by the application.
+ * @return an Equalizer.Settings object containing all current parameters values
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public Equalizer.Settings getProperties()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[4 + mNumBands * 2];
+ checkStatus(getParameter(PARAM_PROPERTIES, param));
+ Settings settings = new Settings();
+ settings.curPreset = byteArrayToShort(param, 0);
+ settings.numBands = byteArrayToShort(param, 2);
+ settings.bandLevels = new short[mNumBands];
+ for (int i = 0; i < mNumBands; i++) {
+ settings.bandLevels[i] = byteArrayToShort(param, 4 + 2*i);
+ }
+ return settings;
+ }
+
+ /**
+ * Sets the equalizer properties. This method is useful when equalizer settings have to
+ * be applied from a previous backup.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setProperties(Equalizer.Settings settings)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ if (settings.numBands != settings.bandLevels.length ||
+ settings.numBands != mNumBands) {
+ throw new IllegalArgumentException("settings invalid band count: " +settings.numBands);
+ }
+
+ byte[] param = concatArrays(shortToByteArray(settings.curPreset),
+ shortToByteArray(mNumBands));
+ for (int i = 0; i < mNumBands; i++) {
+ param = concatArrays(param,
+ shortToByteArray(settings.bandLevels[i]));
+ }
+ checkStatus(setParameter(PARAM_PROPERTIES, param));
+ }
}
diff --git a/media/java/android/media/PresetReverb.java b/media/java/android/media/PresetReverb.java
index 83a01a48e46e..c7d7037c81c3 100644
--- a/media/java/android/media/PresetReverb.java
+++ b/media/java/android/media/PresetReverb.java
@@ -19,12 +19,14 @@ package android.media;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.media.AudioEffect;
import android.os.Bundle;
import android.util.Log;
+
import java.nio.ByteOrder;
import java.nio.ByteBuffer;
+import java.util.StringTokenizer;
-import android.media.AudioEffect;
/**
* A sound generated within a room travels in many directions. The listener first hears the
@@ -116,7 +118,6 @@ public class PresetReverb extends AudioEffect {
public PresetReverb(int priority, int audioSession)
throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
super(EFFECT_TYPE_PRESET_REVERB, EFFECT_TYPE_NULL, priority, audioSession);
- Log.e(TAG, "contructor");
}
/**
@@ -144,10 +145,8 @@ public class PresetReverb extends AudioEffect {
*/
public short getPreset()
throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
- int[] param = new int[1];
- param[0] = PARAM_PRESET;
short[] value = new short[1];
- checkStatus(getParameter(param, value));
+ checkStatus(getParameter(PARAM_PRESET, value));
return value[0];
}
@@ -216,4 +215,82 @@ public class PresetReverb extends AudioEffect {
}
}
}
+
+ /**
+ * The Settings class regroups all preset reverb parameters. It is used in
+ * conjuntion with getProperties() and setProperties() methods to backup and restore
+ * all parameters in a single call.
+ */
+ public static class Settings {
+ public short preset;
+
+ public Settings() {
+ }
+
+ /**
+ * Settings class constructor from a key=value; pairs formatted string. The string is
+ * typically returned by Settings.toString() method.
+ * @throws IllegalArgumentException if the string is not correctly formatted.
+ */
+ public Settings(String settings) {
+ StringTokenizer st = new StringTokenizer(settings, "=;");
+ int tokens = st.countTokens();
+ if (st.countTokens() != 3) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ String key = st.nextToken();
+ if (!key.equals("PresetReverb")) {
+ throw new IllegalArgumentException(
+ "invalid settings for PresetReverb: " + key);
+ }
+ try {
+ key = st.nextToken();
+ if (!key.equals("preset")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ preset = Short.parseShort(st.nextToken());
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("invalid value for key: " + key);
+ }
+ }
+
+ @Override
+ public String toString() {
+ String str = new String (
+ "PresetReverb"+
+ ";preset="+Short.toString(preset)
+ );
+ return str;
+ }
+ };
+
+
+ /**
+ * Gets the preset reverb properties. This method is useful when a snapshot of current
+ * preset reverb settings must be saved by the application.
+ * @return a PresetReverb.Settings object containing all current parameters values
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public PresetReverb.Settings getProperties()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ Settings settings = new Settings();
+ short[] value = new short[1];
+ checkStatus(getParameter(PARAM_PRESET, value));
+ settings.preset = value[0];
+ return settings;
+ }
+
+ /**
+ * Sets the preset reverb properties. This method is useful when preset reverb settings have to
+ * be applied from a previous backup.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setProperties(PresetReverb.Settings settings)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_PRESET, settings.preset));
+ }
}
diff --git a/media/java/android/media/Virtualizer.java b/media/java/android/media/Virtualizer.java
index 9f7129772fae..2c8909e8dc8e 100644
--- a/media/java/android/media/Virtualizer.java
+++ b/media/java/android/media/Virtualizer.java
@@ -19,13 +19,15 @@ package android.media;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.media.AudioEffect;
import android.os.Bundle;
import android.util.Log;
+
import java.nio.ByteOrder;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
+import java.util.StringTokenizer;
-import android.media.AudioEffect;
/**
* An audio virtualizer is a general name for an effect to spatialize audio channels. The exact
@@ -211,4 +213,82 @@ public class Virtualizer extends AudioEffect {
}
}
}
+
+ /**
+ * The Settings class regroups all virtualizer parameters. It is used in
+ * conjuntion with getProperties() and setProperties() methods to backup and restore
+ * all parameters in a single call.
+ */
+ public static class Settings {
+ public short strength;
+
+ public Settings() {
+ }
+
+ /**
+ * Settings class constructor from a key=value; pairs formatted string. The string is
+ * typically returned by Settings.toString() method.
+ * @throws IllegalArgumentException if the string is not correctly formatted.
+ */
+ public Settings(String settings) {
+ StringTokenizer st = new StringTokenizer(settings, "=;");
+ int tokens = st.countTokens();
+ if (st.countTokens() != 3) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ String key = st.nextToken();
+ if (!key.equals("Virtualizer")) {
+ throw new IllegalArgumentException(
+ "invalid settings for Virtualizer: " + key);
+ }
+ try {
+ key = st.nextToken();
+ if (!key.equals("strength")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ strength = Short.parseShort(st.nextToken());
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("invalid value for key: " + key);
+ }
+ }
+
+ @Override
+ public String toString() {
+ String str = new String (
+ "Virtualizer"+
+ ";strength="+Short.toString(strength)
+ );
+ return str;
+ }
+ };
+
+
+ /**
+ * Gets the virtualizer properties. This method is useful when a snapshot of current
+ * virtualizer settings must be saved by the application.
+ * @return a Virtualizer.Settings object containing all current parameters values
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public Virtualizer.Settings getProperties()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ Settings settings = new Settings();
+ short[] value = new short[1];
+ checkStatus(getParameter(PARAM_STRENGTH, value));
+ settings.strength = value[0];
+ return settings;
+ }
+
+ /**
+ * Sets the virtualizer properties. This method is useful when virtualizer settings have to
+ * be applied from a previous backup.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setProperties(Virtualizer.Settings settings)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_STRENGTH, settings.strength));
+ }
}
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index beb3dfc5f3bc..8d9f4fee4107 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -549,7 +549,7 @@ static jint android_media_AudioEffect_native_setParameter(JNIEnv *env,
p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset + vsize);
memcpy(p->data, lpParam, psize);
p->psize = psize;
- memcpy(p->data + voffset, lpValue, psize);
+ memcpy(p->data + voffset, lpValue, vsize);
p->vsize = vsize;
lStatus = lpAudioEffect->setParameter(p);
@@ -698,8 +698,11 @@ static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz,
}
}
- lStatus = translateError(lpAudioEffect->command(cmdCode, cmdSize, pCmdData,
- pReplySize, pReplyData));
+ lStatus = translateError(lpAudioEffect->command((uint32_t)cmdCode,
+ (uint32_t)cmdSize,
+ pCmdData,
+ (uint32_t *)pReplySize,
+ pReplyData));
command_Exit:
diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c
index 0be280c2324e..c19a505d670b 100644
--- a/media/libeffects/factory/EffectsFactory.c
+++ b/media/libeffects/factory/EffectsFactory.c
@@ -73,7 +73,12 @@ int Effect_Process(effect_interface_t self, audio_buffer_t *inBuffer, audio_buff
return ret;
}
-int Effect_Command(effect_interface_t self, int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData)
+int Effect_Command(effect_interface_t self,
+ uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData)
{
int ret = init();
if (ret < 0) {
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c b/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c
index e66513f03ac9..8cf84b7e3c81 100755
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c
@@ -18,8 +18,8 @@
/****************************************************************************************
$Author: nxp007753 $
- $Revision: 1223 $
- $Date: 2010-07-15 14:27:01 +0200 (Thu, 15 Jul 2010) $
+ $Revision: 1315 $
+ $Date: 2010-07-23 11:52:08 +0200 (Fri, 23 Jul 2010) $
*****************************************************************************************/
@@ -124,10 +124,11 @@ void LVDBE_SetFilters(LVDBE_Instance_t *pInstance,
/*
* Setup the high pass filter
*/
- LoadConst_16(0, /* Clear the history, value 0 */
- (LVM_INT16 *)&pInstance->pData->HPFTaps, /* Destination */
- sizeof(pInstance->pData->HPFTaps)/sizeof(LVM_INT16)); /* Number of words */
- BQ_2I_D32F32Cll_TRC_WRA_01_Init(&pInstance->pCoef->HPFInstance, /* Initialise the filter */
+ LoadConst_16(0, /* Clear the history, value 0 */
+ (void *)&pInstance->pData->HPFTaps, /* Destination Cast to void: \
+ no dereferencing in function*/
+ sizeof(pInstance->pData->HPFTaps)/sizeof(LVM_INT16)); /* Number of words */
+ BQ_2I_D32F32Cll_TRC_WRA_01_Init(&pInstance->pCoef->HPFInstance, /* Initialise the filter */
&pInstance->pData->HPFTaps,
(BQ_C32_Coefs_t *)&LVDBE_HPF_Table[Offset]);
@@ -135,10 +136,11 @@ void LVDBE_SetFilters(LVDBE_Instance_t *pInstance,
/*
* Setup the band pass filter
*/
- LoadConst_16(0, /* Clear the history, value 0 */
- (LVM_INT16 *)&pInstance->pData->BPFTaps, /* Destination */
- sizeof(pInstance->pData->BPFTaps)/sizeof(LVM_INT16)); /* Number of words */
- BP_1I_D32F32Cll_TRC_WRA_02_Init(&pInstance->pCoef->BPFInstance, /* Initialise the filter */
+ LoadConst_16(0, /* Clear the history, value 0 */
+ (void *)&pInstance->pData->BPFTaps, /* Destination Cast to void:\
+ no dereferencing in function*/
+ sizeof(pInstance->pData->BPFTaps)/sizeof(LVM_INT16)); /* Number of words */
+ BP_1I_D32F32Cll_TRC_WRA_02_Init(&pInstance->pCoef->BPFInstance, /* Initialise the filter */
&pInstance->pData->BPFTaps,
(BP_C32_Coefs_t *)&LVDBE_BPF_Table[Offset]);
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c
index 41785a32d35c..727340013d89 100755
--- a/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c
+++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c
@@ -19,8 +19,8 @@
/****************************************************************************************
$Author: nxp007753 $
- $Revision: 1082 $
- $Date: 2010-07-05 12:44:39 +0200 (Mon, 05 Jul 2010) $
+ $Revision: 1316 $
+ $Date: 2010-07-23 11:53:24 +0200 (Fri, 23 Jul 2010) $
*****************************************************************************************/
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c
index 4667feb63ab7..922f77d2bae1 100755
--- a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c
+++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c
@@ -18,8 +18,8 @@
/****************************************************************************************
$Author: nxp007753 $
- $Revision: 1255 $
- $Date: 2010-07-16 17:07:29 +0200 (Fri, 16 Jul 2010) $
+ $Revision: 1316 $
+ $Date: 2010-07-23 11:53:24 +0200 (Fri, 23 Jul 2010) $
*****************************************************************************************/
@@ -314,8 +314,9 @@ void LVM_SetTrebleBoost(LVM_Instance_t *pInstance,
/*
* Clear the taps
*/
- LoadConst_16((LVM_INT16)0, /* Value */
- (LVM_INT16 *)&pInstance->pTE_Taps->TrebleBoost_Taps, /* Destination */
+ LoadConst_16((LVM_INT16)0, /* Value */
+ (void *)&pInstance->pTE_Taps->TrebleBoost_Taps, /* Destination.\
+ Cast to void: no dereferencing in function */
(LVM_UINT16)(sizeof(pInstance->pTE_Taps->TrebleBoost_Taps)/sizeof(LVM_INT16))); /* Number of words */
}
}
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c
index 7ac5685ea574..323a2a3a6dce 100755
--- a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c
+++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c
@@ -18,8 +18,8 @@
/************************************************************************************
$Author: nxp007753 $
- $Revision: 1255 $
- $Date: 2010-07-16 17:07:29 +0200 (Fri, 16 Jul 2010) $
+ $Revision: 1316 $
+ $Date: 2010-07-23 11:53:24 +0200 (Fri, 23 Jul 2010) $
*************************************************************************************/
diff --git a/media/libeffects/lvm/lib/Common/src/MixSoft_2St_D32C31_SAT.c b/media/libeffects/lvm/lib/Common/src/MixSoft_2St_D32C31_SAT.c
index b03f0ba0ff4d..83f95ac6cd14 100755
--- a/media/libeffects/lvm/lib/Common/src/MixSoft_2St_D32C31_SAT.c
+++ b/media/libeffects/lvm/lib/Common/src/MixSoft_2St_D32C31_SAT.c
@@ -18,9 +18,9 @@
/************************************************************************/
/* */
/* Project:: */
-/* $Author: beq07716 $*/
-/* $Revision: 1000 $*/
-/* $Date: 2010-06-28 13:08:20 +0200 (Mon, 28 Jun 2010) $*/
+/* $Author: nxp007753 $*/
+/* $Revision: 1316 $*/
+/* $Date: 2010-07-23 11:53:24 +0200 (Fri, 23 Jul 2010) $*/
/* */
/************************************************************************/
@@ -51,7 +51,8 @@ void MixSoft_2St_D32C31_SAT( Mix_2St_Cll_t *pInstance,
if ((pInstance->Current1 != pInstance->Target1) || (pInstance->Current2 != pInstance->Target2))
{
MixSoft_1St_D32C31_WRA( (Mix_1St_Cll_t*) pInstance, src1, dst, n);
- MixInSoft_D32C31_SAT( (Mix_1St_Cll_t*) &pInstance->Alpha2, src2, dst, n);
+ MixInSoft_D32C31_SAT( (void *) &pInstance->Alpha2, /* Cast to void: no dereferencing in function*/
+ src2, dst, n);
}
/******************************************************************************
@@ -61,7 +62,8 @@ void MixSoft_2St_D32C31_SAT( Mix_2St_Cll_t *pInstance,
else
{
if (pInstance->Current1 == 0)
- MixSoft_1St_D32C31_WRA( (Mix_1St_Cll_t*) &pInstance->Alpha2, src2, dst, n);
+ MixSoft_1St_D32C31_WRA( (void *) &pInstance->Alpha2, /* Cast to void: no dereferencing in function*/
+ src2, dst, n);
else if (pInstance->Current2 == 0)
MixSoft_1St_D32C31_WRA( (Mix_1St_Cll_t*) pInstance, src1, dst, n);
else
diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Control.c b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Control.c
index 88f6fb052f52..dac244911d47 100755
--- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Control.c
+++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Control.c
@@ -18,8 +18,8 @@
/**********************************************************************************
$Author: nxp007753 $
- $Revision: 1223 $
- $Date: 2010-07-15 14:27:01 +0200 (Thu, 15 Jul 2010) $
+ $Revision: 1316 $
+ $Date: 2010-07-23 11:53:24 +0200 (Fri, 23 Jul 2010) $
***********************************************************************************/
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_BypassMix.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_BypassMix.c
index 2a83e89fa105..b1d9408c1a05 100755
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_BypassMix.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_BypassMix.c
@@ -17,9 +17,9 @@
/************************************************************************************
- $Author: nxp007753 $
- $Revision: 1246 $
- $Date: 2010-07-16 11:07:10 +0200 (Fri, 16 Jul 2010) $
+ $Author: beq06068 $
+ $Revision: 1307 $
+ $Date: 2010-07-22 17:41:25 +0200 (Thu, 22 Jul 2010) $
*************************************************************************************/
@@ -90,7 +90,7 @@ LVCS_ReturnStatus_en LVCS_BypassMixInit(LVCS_Handle_t hInstance,
*/
if ((pParams->OperatingMode == LVCS_ON) &&
(pInstance->bTimerDone == LVM_TRUE)
- && (LVC_Mixer_GetTarget(&pInstance->MSBypassMixer.MixerStream[1]) != 0x7FFF) /* this indicates an off->on transtion */
+ && (pInstance->MSTarget1 != 0x7FFF) /* this indicates an off->on transtion */
)
{
pInstance->TransitionGain = pParams->EffectLevel;
@@ -260,17 +260,15 @@ LVM_INT32 LVCS_MixerCallback(LVCS_Handle_t hInstance,
LVM_INT16 CallbackParam)
{
LVCS_Instance_t *pInstance = (LVCS_Instance_t *)hInstance;
- LVM_INT32 Target1;
- Target1 = LVC_Mixer_GetTarget(&pInstance->MSBypassMixer.MixerStream[0]);
- (void)pGeneralPurpose;
+ (void)pGeneralPurpose;
/*
* Off transition has completed in Headphone mode
*/
if ((pInstance->OutputDevice == LVCS_HEADPHONE) &&
(pInstance->bInOperatingModeTransition) &&
- (Target1 == 0x0000)&& /* this indicates an on->off transition */
+ (pInstance->MSTarget0 == 0x0000)&& /* this indicates an on->off transition */
(CallbackParam == 0))
{
/* Set operating mode to OFF */
@@ -289,7 +287,7 @@ LVM_INT32 LVCS_MixerCallback(LVCS_Handle_t hInstance,
if ((pInstance->OutputDevice == LVCS_HEADPHONE) &&
- (Target1 == 1) &&
+ (pInstance->MSTarget0 == 1) &&
(pInstance->bTimerDone == LVM_TRUE)){
/* Exit transition state */
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c
index 5dfca2568bd4..fea44bfca4fb 100755
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Control.c
@@ -17,9 +17,9 @@
/************************************************************************************
- $Author: nxp007753 $
- $Revision: 1246 $
- $Date: 2010-07-16 11:07:10 +0200 (Fri, 16 Jul 2010) $
+ $Author: beq06068 $
+ $Revision: 1307 $
+ $Date: 2010-07-22 17:41:25 +0200 (Thu, 22 Jul 2010) $
*************************************************************************************/
@@ -94,9 +94,6 @@ LVCS_ReturnStatus_en LVCS_Control(LVCS_Handle_t hInstance,
if (pParams->SampleRate != pInstance->Params.SampleRate)
{
- LVC_Mixer_VarSlope_SetTimeConstant(&pInstance->MSBypassMixer.MixerStream[0],LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
-
- LVC_Mixer_VarSlope_SetTimeConstant(&pInstance->MSBypassMixer.MixerStream[1],LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
pInstance->TimerParams.SamplingRate = LVCS_SampleRateTable[pParams->SampleRate];
}
@@ -130,6 +127,29 @@ LVCS_ReturnStatus_en LVCS_Control(LVCS_Handle_t hInstance,
pInstance->VolCorrect = pLVCS_VolCorrectTable[Offset];
+ LVC_Mixer_Init(&pInstance->BypassMix.Mixer_Instance.MixerStream[0],0,0);
+
+
+ {
+ LVM_UINT32 Gain;
+ const Gain_t *pOutputGainTable = (Gain_t*)&LVCS_OutputGainTable[0];
+ Gain = (LVM_UINT32)(pOutputGainTable[Offset].Loss * LVM_MAXINT_16);
+ Gain = (LVM_UINT32)pOutputGainTable[Offset].UnprocLoss * (Gain >> 15);
+ Gain=Gain>>15;
+ /*
+ * Apply the gain correction and shift, note the result is in Q3.13 format
+ */
+ Gain = (Gain * pInstance->VolCorrect.GainMin) >>12;
+
+ LVC_Mixer_Init(&pInstance->BypassMix.Mixer_Instance.MixerStream[1],0,Gain);
+ LVC_Mixer_VarSlope_SetTimeConstant(&pInstance->BypassMix.Mixer_Instance.MixerStream[0],
+ LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
+ LVC_Mixer_VarSlope_SetTimeConstant(&pInstance->BypassMix.Mixer_Instance.MixerStream[1],
+ LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
+
+ }
+
+
err=LVCS_SEnhancerInit(hInstance,
pParams);
@@ -199,44 +219,15 @@ LVCS_ReturnStatus_en LVCS_Control(LVCS_Handle_t hInstance,
/* Change transition bypass mixer settings if needed depending on transition type */
if(pParams->OperatingMode != LVCS_OFF){
- LVM_INT32 Current1;
- LVM_INT32 Current2;
-
- Current1 = LVC_Mixer_GetCurrent(&pInstance->MSBypassMixer.MixerStream[0]);
- Current2 = LVC_Mixer_GetCurrent(&pInstance->MSBypassMixer.MixerStream[1]);
-
- if(pInstance->bInOperatingModeTransition != LVM_TRUE)
- {
- Current1 = 0x00000000;
- Current2 = LVM_MAXINT_16;
- }
- pInstance->MSBypassMixer.MixerStream[0].CallbackSet = 1;
- pInstance->MSBypassMixer.MixerStream[1].CallbackSet = 1;
-
- LVC_Mixer_Init(&pInstance->MSBypassMixer.MixerStream[0],LVM_MAXINT_16,Current1);
- LVC_Mixer_Init(&pInstance->MSBypassMixer.MixerStream[1],0,Current2);
+ pInstance->MSTarget0=LVM_MAXINT_16;
+ pInstance->MSTarget1=0;
}
else
{
- LVM_INT32 Current1;
- LVM_INT32 Current2;
-
- Current1 = LVC_Mixer_GetCurrent(&pInstance->MSBypassMixer.MixerStream[0]);
- Current2 = LVC_Mixer_GetCurrent(&pInstance->MSBypassMixer.MixerStream[1]);
-
- if(pInstance->bInOperatingModeTransition != LVM_TRUE)
- {
- Current1 = LVM_MAXINT_16;
- Current2 = 0x00000000;
- }
- pInstance->MSBypassMixer.MixerStream[0].CallbackSet = 1;
- pInstance->MSBypassMixer.MixerStream[1].CallbackSet = 1;
pInstance->Params.OperatingMode = OperatingModeSave;
- LVC_Mixer_Init(&pInstance->MSBypassMixer.MixerStream[0],0x00000000,Current1);
- LVC_Mixer_Init(&pInstance->MSBypassMixer.MixerStream[1],LVM_MAXINT_16,Current2);
+ pInstance->MSTarget1=LVM_MAXINT_16;
+ pInstance->MSTarget0=0;
}
- LVC_Mixer_SetTimeConstant(&pInstance->MSBypassMixer.MixerStream[0],LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
- LVC_Mixer_SetTimeConstant(&pInstance->MSBypassMixer.MixerStream[1],LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
/* Set transition flag */
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.c
index ca615b07c8a4..7ab6571774a1 100755
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Equaliser.c
@@ -17,9 +17,9 @@
/************************************************************************************
- $Author: beq07716 $
- $Revision: 1001 $
- $Date: 2010-06-28 13:23:02 +0200 (Mon, 28 Jun 2010) $
+ $Author: nxp007753 $
+ $Revision: 1315 $
+ $Date: 2010-07-23 11:52:08 +0200 (Fri, 23 Jul 2010) $
*************************************************************************************/
@@ -94,8 +94,9 @@ LVCS_ReturnStatus_en LVCS_EqualiserInit(LVCS_Handle_t hInstance,
Coeffs.B1 = (LVM_INT16)-pEqualiserCoefTable[Offset].B1;
Coeffs.B2 = (LVM_INT16)-pEqualiserCoefTable[Offset].B2;
- LoadConst_16((LVM_INT16)0, /* Value */
- (LVM_INT16 *)&pData->EqualiserBiquadTaps, /* Destination */
+ LoadConst_16((LVM_INT16)0, /* Value */
+ (void *)&pData->EqualiserBiquadTaps, /* Destination Cast to void:\
+ no dereferencing in function*/
(LVM_UINT16)(sizeof(pData->EqualiserBiquadTaps)/sizeof(LVM_INT16))); /* Number of words */
BQ_2I_D16F32Css_TRC_WRA_01_Init(&pCoefficients->EqualiserBiquadInstance,
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.c
index 4aa95d3cb83b..f5f7cd0d5741 100755
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.c
@@ -17,9 +17,9 @@
/************************************************************************************
- $Author: beq07716 $
- $Revision: 1001 $
- $Date: 2010-06-28 13:23:02 +0200 (Mon, 28 Jun 2010) $
+ $Author: beq06068 $
+ $Revision: 1307 $
+ $Date: 2010-07-22 17:41:25 +0200 (Thu, 22 Jul 2010) $
*************************************************************************************/
@@ -159,7 +159,6 @@ LVCS_ReturnStatus_en LVCS_Init(LVCS_Handle_t *phInstance,
LVCS_Capabilities_t *pCapabilities)
{
- LVM_INT16 Offset;
LVCS_Instance_t *pInstance;
LVCS_VolCorrect_t *pLVCS_VolCorrectTable;
@@ -197,30 +196,18 @@ LVCS_ReturnStatus_en LVCS_Init(LVCS_Handle_t *phInstance,
pInstance->Params.EffectLevel = 0;
pInstance->Params.ReverbLevel = (LVM_UINT16)0x8000;
pLVCS_VolCorrectTable = (LVCS_VolCorrect_t*)&LVCS_VolCorrectTable[0];
- Offset = (LVM_INT16)(pInstance->Params.SpeakerType + (pInstance->Params.SourceFormat*(1+LVCS_EX_HEADPHONES)));
- pInstance->VolCorrect = pLVCS_VolCorrectTable[Offset];
+ pInstance->VolCorrect = pLVCS_VolCorrectTable[0];
pInstance->TransitionGain = 0;
+ /* These current and target values are intialized again in LVCS_Control.c */
LVC_Mixer_Init(&pInstance->BypassMix.Mixer_Instance.MixerStream[0],0,0);
+ /* These current and target values are intialized again in LVCS_Control.c */
LVC_Mixer_Init(&pInstance->BypassMix.Mixer_Instance.MixerStream[1],0,0);
/*
* Initialise the bypass variables
*/
- pInstance->MSBypassMixer.MixerStream[0].CallbackParam = 0;
- pInstance->MSBypassMixer.MixerStream[0].pCallbackHandle = LVM_NULL;
- pInstance->MSBypassMixer.MixerStream[0].pCallBack = LVM_NULL;
- pInstance->MSBypassMixer.MixerStream[0].CallbackSet = 0;
- LVC_Mixer_Init(&pInstance->MSBypassMixer.MixerStream[0],0,0);
- LVC_Mixer_SetTimeConstant(&pInstance->MSBypassMixer.MixerStream[0],0,LVM_FS_44100,2);
-
-
- pInstance->MSBypassMixer.MixerStream[1].CallbackParam = 0;
- pInstance->MSBypassMixer.MixerStream[1].pCallbackHandle = LVM_NULL;
- pInstance->MSBypassMixer.MixerStream[1].pCallBack = LVM_NULL;
- pInstance->MSBypassMixer.MixerStream[1].CallbackSet = 0;
- LVC_Mixer_Init(&pInstance->MSBypassMixer.MixerStream[1],0,0);
- LVC_Mixer_SetTimeConstant(&pInstance->MSBypassMixer.MixerStream[1],0,LVM_FS_44100,2);
-
+ pInstance->MSTarget0=0;
+ pInstance->MSTarget1=0;
pInstance->bInOperatingModeTransition = LVM_FALSE;
pInstance->bTimerDone = LVM_FALSE;
pInstance->TimerParams.CallBackParam = 0;
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h
index be30829d8331..a97769032580 100755
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h
@@ -17,9 +17,9 @@
/************************************************************************************
- $Author: beq07716 $
- $Revision: 1001 $
- $Date: 2010-06-28 13:23:02 +0200 (Mon, 28 Jun 2010) $
+ $Author: beq06068 $
+ $Revision: 1307 $
+ $Date: 2010-07-22 17:41:25 +0200 (Thu, 22 Jul 2010) $
*************************************************************************************/
@@ -128,7 +128,8 @@ typedef struct
LVCS_BypassMix_t BypassMix; /* Bypass mixer configuration */
/* Bypass variable */
- LVMixer3_2St_st MSBypassMixer; /* Bypass mixer used in transitions in MS mode */
+ LVM_INT16 MSTarget0; /* Mixer state control variable for smooth transtion */
+ LVM_INT16 MSTarget1; /* Mixer state control variable for smooth transtion */
LVM_INT16 bInOperatingModeTransition; /* Operating mode transition flag */
LVM_INT16 bTimerDone; /* Timer completion flag */
LVM_Timer_Params_t TimerParams; /* Timer parameters */
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.c
index 2efef84b5ebb..861bde69b8f9 100755
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.c
@@ -17,9 +17,9 @@
/************************************************************************************
- $Author: beq07716 $
- $Revision: 1001 $
- $Date: 2010-06-28 13:23:02 +0200 (Mon, 28 Jun 2010) $
+ $Author: nxp007753 $
+ $Revision: 1315 $
+ $Date: 2010-07-23 11:52:08 +0200 (Fri, 23 Jul 2010) $
*************************************************************************************/
@@ -113,7 +113,7 @@ LVCS_ReturnStatus_en LVCS_ReverbGeneratorInit(LVCS_Handle_t hInstance,
Coeffs.B2 = (LVM_INT16)-pReverbCoefTable[Offset].B2;
LoadConst_16(0, /* Value */
- (LVM_INT16 *)&pData->ReverbBiquadTaps, /* Destination */
+ (void *)&pData->ReverbBiquadTaps, /* Destination Cast to void: no dereferencing in function*/
(LVM_UINT16)(sizeof(pData->ReverbBiquadTaps)/sizeof(LVM_INT16))); /* Number of words */
BQ_2I_D16F16Css_TRC_WRA_01_Init(&pCoefficients->ReverbBiquadInstance,
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.c
index 4ff471658d1e..b67d824f2a55 100755
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.c
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_StereoEnhancer.c
@@ -17,9 +17,9 @@
/************************************************************************************
- $Author: beq07716 $
- $Revision: 1001 $
- $Date: 2010-06-28 13:23:02 +0200 (Mon, 28 Jun 2010) $
+ $Author: nxp007753 $
+ $Revision: 1315 $
+ $Date: 2010-07-23 11:52:08 +0200 (Fri, 23 Jul 2010) $
*************************************************************************************/
@@ -90,7 +90,8 @@ LVCS_ReturnStatus_en LVCS_SEnhancerInit(LVCS_Handle_t hInstance,
/* Clear the taps */
LoadConst_16(0, /* Value */
- (LVM_INT16 *)&pData->SEBiquadTapsMid, /* Destination */
+ (void *)&pData->SEBiquadTapsMid, /* Destination Cast to void:\
+ no dereferencing in function*/
(LVM_UINT16)(sizeof(pData->SEBiquadTapsMid)/sizeof(LVM_UINT16))); /* Number of words */
FO_1I_D16F16Css_TRC_WRA_01_Init(&pCoefficient->SEBiquadInstanceMid,
@@ -116,7 +117,8 @@ LVCS_ReturnStatus_en LVCS_SEnhancerInit(LVCS_Handle_t hInstance,
/* Clear the taps */
LoadConst_16(0, /* Value */
- (LVM_INT16 *)&pData->SEBiquadTapsSide, /* Destination */
+ (void *)&pData->SEBiquadTapsSide, /* Destination Cast to void:\
+ no dereferencing in function*/
(LVM_UINT16)(sizeof(pData->SEBiquadTapsSide)/sizeof(LVM_UINT16))); /* Number of words */
diff --git a/media/libeffects/lvm/wrapper/Android.mk b/media/libeffects/lvm/wrapper/Android.mk
index 4ebc44399a02..7855dcd4fceb 100644
--- a/media/libeffects/lvm/wrapper/Android.mk
+++ b/media/libeffects/lvm/wrapper/Android.mk
@@ -1,30 +1,36 @@
-LOCAL_PATH:= $(call my-dir)
-
-# music bundle wrapper
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_ARM_MODE := arm
-
-LOCAL_SRC_FILES:= \
- Bundle/EffectBundle.cpp
-
-LOCAL_MODULE:= libbundlewrapper
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
-
-LOCAL_PRELINK_MODULE := false
-
-LOCAL_STATIC_LIBRARIES += libmusicbundle
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libdl
-
-LOCAL_C_INCLUDES += \
- $(LOCAL_PATH)/Bundle \
- $(LOCAL_PATH)/../lib/Common/lib/ \
- $(LOCAL_PATH)/../lib/Bundle/lib/
-
-
-include $(BUILD_SHARED_LIBRARY)
+LOCAL_PATH:= $(call my-dir)
+
+# music bundle wrapper
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_ARM_MODE := arm
+
+LOCAL_SRC_FILES:= \
+ Bundle/EffectBundle.cpp
+
+LOCAL_MODULE:= libbundlewrapper
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_STATIC_LIBRARIES += libmusicbundle
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+
+ifeq ($(TARGET_SIMULATOR),true)
+LOCAL_LDLIBS += -ldl
+else
+LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/Bundle \
+ $(LOCAL_PATH)/../lib/Common/lib/ \
+ $(LOCAL_PATH)/../lib/Bundle/lib/
+
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 6043dd5ae7f5..4c3ebcae1a3b 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -1625,7 +1625,7 @@ int BassBoost_getParameter(EffectContext *pContext,
//LOGV("\tBassBoost_getParameter start");
switch (param){
- case BASSBOOST_PARAM_STRENGTH_SUP:
+ case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
case BASSBOOST_PARAM_STRENGTH:
if (*pValueSize != sizeof(int16_t)){
LOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize2 %d", *pValueSize);
@@ -1640,10 +1640,10 @@ int BassBoost_getParameter(EffectContext *pContext,
}
switch (param){
- case BASSBOOST_PARAM_STRENGTH_SUP:
+ case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
*(uint32_t *)pValue = 1;
- //LOGV("\tBassBoost_getParameter() BASSBOOST_PARAM_STRENGTH_SUP Value is %d",
+ //LOGV("\tBassBoost_getParameter() BASSBOOST_PARAM_STRENGTH_SUPPORTED Value is %d",
// *(uint32_t *)pValue);
break;
@@ -1735,7 +1735,7 @@ int Virtualizer_getParameter(EffectContext *pContext,
//LOGV("\tVirtualizer_getParameter start");
switch (param){
- case VIRTUALIZER_PARAM_STRENGTH_SUP:
+ case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
case VIRTUALIZER_PARAM_STRENGTH:
if (*pValueSize != sizeof(int16_t)){
LOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize2 %d",*pValueSize);
@@ -1750,10 +1750,10 @@ int Virtualizer_getParameter(EffectContext *pContext,
}
switch (param){
- case VIRTUALIZER_PARAM_STRENGTH_SUP:
+ case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
*(uint32_t *)pValue = 1;
- //LOGV("\tVirtualizer_getParameter() VIRTUALIZER_PARAM_STRENGTH_SUP Value is %d",
+ //LOGV("\tVirtualizer_getParameter() VIRTUALIZER_PARAM_STRENGTH_SUPPORTED Value is %d",
// *(uint32_t *)pValue);
break;
@@ -1876,6 +1876,14 @@ int Equalizer_getParameter(EffectContext *pContext,
case EQ_PARAM_GET_PRESET_NAME:
break;
+ case EQ_PARAM_PROPERTIES:
+ if (*pValueSize < (2 + FIVEBAND_NUMBANDS) * sizeof(uint16_t)) {
+ LOGV("\tLVM_ERROR : Equalizer_getParameter() invalid pValueSize 1 %d", *pValueSize);
+ return -EINVAL;
+ }
+ *pValueSize = (2 + FIVEBAND_NUMBANDS) * sizeof(uint16_t);
+ break;
+
default:
LOGV("\tLVM_ERROR : Equalizer_getParameter unknown param %d", param);
return -EINVAL;
@@ -1959,6 +1967,16 @@ int Equalizer_getParameter(EffectContext *pContext,
// param2, gEqualizerPresets[param2].name, *pValueSize);
break;
+ case EQ_PARAM_PROPERTIES: {
+ uint16_t *p = (uint16_t *)pValue;
+ LOGV("\tEqualizer_getParameter() EQ_PARAM_PROPERTIES");
+ p[0] = EqualizerGetPreset(pContext);
+ p[1] = FIVEBAND_NUMBANDS;
+ for (int i = 0; i < FIVEBAND_NUMBANDS; i++) {
+ p[2 + i] = EqualizerGetBandLevel(pContext, i);
+ }
+ } break;
+
default:
LOGV("\tLVM_ERROR : Equalizer_getParameter() invalid param %d", param);
status = -EINVAL;
@@ -2330,14 +2348,13 @@ extern "C" int Effect_process(effect_interface_t self,
/* Effect Control Interface Implementation: Command */
extern "C" int Effect_command(effect_interface_t self,
- int cmdCode,
- int cmdSize,
+ uint32_t cmdCode,
+ uint32_t cmdSize,
void *pCmdData,
- int *replySize,
+ uint32_t *replySize,
void *pReplyData){
EffectContext * pContext = (EffectContext *) self;
int retsize;
- int status = 0;
//LOGV("\t\nEffect_command start");
@@ -2371,54 +2388,29 @@ extern "C" int Effect_command(effect_interface_t self,
switch (cmdCode){
case EFFECT_CMD_INIT:
+ if (pReplyData == NULL || *replySize != sizeof(int)){
+ LOGV("\tLVM_ERROR, EFFECT_CMD_INIT: ERROR for effect type %d",
+ pContext->EffectType);
+ return -EINVAL;
+ }
+ *(int *) pReplyData = 0;
//LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_INIT start");
if(pContext->EffectType == LVM_BASS_BOOST){
//LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_INIT for LVM_BASS_BOOST");
-
- if (pReplyData == NULL || *replySize != sizeof(int)){
- LOGV("\tLVM_ERROR : BassBoost_command cmdCode Case: "
- "EFFECT_CMD_INIT: ERROR");
- return -EINVAL;
- }
-
android::BassSetStrength(pContext, 0);
}
if(pContext->EffectType == LVM_VIRTUALIZER){
//LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_INIT for LVM_VIRTUALIZER");
-
- if (pReplyData == NULL || *replySize != sizeof(int)){
- LOGV("\tLVM_ERROR : Virtualizer_command cmdCode Case: "
- "EFFECT_CMD_INIT: ERROR");
- return -EINVAL;
- }
-
android::VirtualizerSetStrength(pContext, 0);
}
if(pContext->EffectType == LVM_EQUALIZER){
//LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_INIT for LVM_EQUALIZER");
-
- if (pReplyData == NULL || *replySize != sizeof(int)){
- LOGV("\tLVM_ERROR : Equalizer_command cmdCode Case: "
- "EFFECT_CMD_INIT: ERROR");
- return -EINVAL;
- }
-
android::EqualizerSetPreset(pContext, 0);
}
if(pContext->EffectType == LVM_VOLUME){
//LOGV("\tEffect_command cmdCode Case: "
// "EFFECT_CMD_INIT start");
-
- if (pReplyData == NULL || *replySize != sizeof(int)){
- LOGV("\tLVM_ERROR : Volume_command cmdCode Case: "
- "EFFECT_CMD_INIT: ERROR");
- return -EINVAL;
- }
-
- status = android::VolumeSetVolumeLevel(pContext, 0);
- if(status == -EINVAL){
- return -EINVAL;
- }
+ *(int *) pReplyData = android::VolumeSetVolumeLevel(pContext, 0);
}
break;
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
index 029f843ad757..d009bf926737 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
@@ -17,7 +17,9 @@
#ifndef ANDROID_EFFECTBUNDLE_H_
#define ANDROID_EFFECTBUNDLE_H_
-#include <media/EffectApi.h>
+#include <media/EffectEqualizerApi.h>
+#include <media/EffectBassBoostApi.h>
+#include <media/EffectVirtualizerApi.h>
#include <LVM.h>
#if __cplusplus
@@ -29,22 +31,11 @@ extern "C" {
#define MAX_CALL_SIZE 256
//#define LVM_PCM
-//TODO: this should be included from each effect API include
-static const effect_uuid_t SL_IID_BASSBOOST_ = { 0x0634f220, 0xddd4, 0x11db, 0xa0fc,
- { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
-const effect_uuid_t * const SL_IID_BASSBOOST = &SL_IID_BASSBOOST_;
-
-static const effect_uuid_t SL_IID_EQUALIZER_ = { 0x0bed4300, 0xddd6, 0x11db, 0x8f34,
- { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
-const effect_uuid_t * const SL_IID_EQUALIZER = &SL_IID_EQUALIZER_;
-
-static const effect_uuid_t SL_IID_VIRTUALIZER_ = { 0x37cc2c00, 0xdddd, 0x11db, 0x8577,
- { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
-const effect_uuid_t * const SL_IID_VIRTUALIZER = &SL_IID_VIRTUALIZER_;
-
+#ifndef OPENSL_ES_H_
static const effect_uuid_t SL_IID_VOLUME_ = { 0x09e8ede0, 0xddde, 0x11db, 0xb4f6,
{ 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
const effect_uuid_t * const SL_IID_VOLUME = &SL_IID_VOLUME_;
+#endif //OPENSL_ES_H_
typedef enum
{
@@ -112,34 +103,6 @@ struct EffectContext{
BundledEffectContext *pBundledContext;
};
-//TODO: this should be included from each effect API include
-/* enumerated parameter settings for BassBoost effect */
-typedef enum
-{
- BASSBOOST_PARAM_STRENGTH_SUP, // type SLboolean = typedef SLuint32
- BASSBOOST_PARAM_STRENGTH // type SLpermille = typedef SLuint16
-} t_bassboost_params;
-
-/* enumerated parameter settings for Virtualizer effect */
-typedef enum
-{
- VIRTUALIZER_PARAM_STRENGTH_SUP, // type SLboolean = typedef SLuint32
- VIRTUALIZER_PARAM_STRENGTH // type SLpermille = typedef SLuint16
-} t_virtualizer_params;
-
-/* enumerated parameter settings for Equalizer effect */
-typedef enum
-{
- EQ_PARAM_NUM_BANDS, // Gets the number of frequency bands that the equalizer supports.
- EQ_PARAM_LEVEL_RANGE, // Returns the minimum and maximum band levels supported.
- EQ_PARAM_BAND_LEVEL, // Gets/Sets the gain set for the given equalizer band.
- EQ_PARAM_CENTER_FREQ, // Gets the center frequency of the given band.
- EQ_PARAM_BAND_FREQ_RANGE, // Gets the frequency range of the given frequency band.
- EQ_PARAM_GET_BAND, // Gets the band that has the most effect on the given frequency.
- EQ_PARAM_CUR_PRESET, // Gets/Sets the current preset.
- EQ_PARAM_GET_NUM_OF_PRESETS, // Gets the total number of presets the equalizer supports.
- EQ_PARAM_GET_PRESET_NAME // Gets the preset name based on the index.
-} t_equalizer_params;
/* enumerated parameter settings for Volume effect */
typedef enum
diff --git a/media/libeffects/testlibs/EffectEqualizer.cpp b/media/libeffects/testlibs/EffectEqualizer.cpp
index 0eb2bdf4ea75..a71f236aafd8 100644
--- a/media/libeffects/testlibs/EffectEqualizer.cpp
+++ b/media/libeffects/testlibs/EffectEqualizer.cpp
@@ -551,8 +551,8 @@ extern "C" int Equalizer_process(effect_interface_t self, audio_buffer_t *inBuff
return 0;
} // end Equalizer_process
-extern "C" int Equalizer_command(effect_interface_t self, int cmdCode, int cmdSize,
- void *pCmdData, int *replySize, void *pReplyData) {
+extern "C" int Equalizer_command(effect_interface_t self, uint32_t cmdCode, uint32_t cmdSize,
+ void *pCmdData, uint32_t *replySize, void *pReplyData) {
android::EqualizerContext * pContext = (android::EqualizerContext *) self;
int retsize;
diff --git a/media/libeffects/testlibs/EffectReverb.c b/media/libeffects/testlibs/EffectReverb.c
index 2ce755897fee..3eb8b2cf8634 100644
--- a/media/libeffects/testlibs/EffectReverb.c
+++ b/media/libeffects/testlibs/EffectReverb.c
@@ -270,8 +270,8 @@ static int Reverb_Process(effect_interface_t self, audio_buffer_t *inBuffer, aud
}
-static int Reverb_Command(effect_interface_t self, int cmdCode, int cmdSize,
- void *pCmdData, int *replySize, void *pReplyData) {
+static int Reverb_Command(effect_interface_t self, uint32_t cmdCode, uint32_t cmdSize,
+ void *pCmdData, uint32_t *replySize, void *pReplyData) {
reverb_module_t *pRvbModule = (reverb_module_t *) self;
reverb_object_t *pReverb;
int retsize;
@@ -688,7 +688,7 @@ int Reverb_getParameter(reverb_object_t *pReverb, int32_t param, size_t *pSize,
void *pValue) {
int32_t *pValue32;
int16_t *pValue16;
- t_reverb_properties *pProperties;
+ t_reverb_settings *pProperties;
int32_t i;
int32_t temp;
int32_t temp2;
@@ -727,7 +727,7 @@ int Reverb_getParameter(reverb_object_t *pReverb, int32_t param, size_t *pSize,
break;
case REVERB_PARAM_PROPERTIES:
- size = sizeof(t_reverb_properties);
+ size = sizeof(t_reverb_settings);
break;
default:
@@ -740,7 +740,7 @@ int Reverb_getParameter(reverb_object_t *pReverb, int32_t param, size_t *pSize,
pValue32 = (int32_t *) pValue;
pValue16 = (int16_t *) pValue;
- pProperties = (t_reverb_properties *) pValue;
+ pProperties = (t_reverb_settings *) pValue;
switch (param) {
case REVERB_PARAM_BYPASS:
@@ -971,7 +971,7 @@ int Reverb_setParameter(reverb_object_t *pReverb, int32_t param, size_t size,
void *pValue) {
int32_t value32;
int16_t value16;
- t_reverb_properties *pProperties;
+ t_reverb_settings *pProperties;
int32_t i;
int32_t temp;
int32_t temp2;
@@ -1019,7 +1019,7 @@ int Reverb_setParameter(reverb_object_t *pReverb, int32_t param, size_t size,
break;
case REVERB_PARAM_PROPERTIES:
- paramSize = sizeof(t_reverb_properties);
+ paramSize = sizeof(t_reverb_settings);
break;
default:
@@ -1035,7 +1035,7 @@ int Reverb_setParameter(reverb_object_t *pReverb, int32_t param, size_t size,
} else if (paramSize == sizeof(int32_t)) {
value32 = *(int32_t *) pValue;
} else {
- pProperties = (t_reverb_properties *) pValue;
+ pProperties = (t_reverb_settings *) pValue;
}
pPreset = &pReverb->m_sPreset.m_sPreset[pReverb->m_nNextRoom];
diff --git a/media/libeffects/testlibs/EffectReverb.h b/media/libeffects/testlibs/EffectReverb.h
index ee8e3905ef0c..dbcd1922e9f3 100644
--- a/media/libeffects/testlibs/EffectReverb.h
+++ b/media/libeffects/testlibs/EffectReverb.h
@@ -301,12 +301,23 @@ typedef struct reverb_module_s {
*------------------------------------
*/
int EffectQueryNumberEffects(uint32_t *pNumEffects);
-int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor);
-int EffectCreate(effect_uuid_t *effectUID, int32_t sessionId, int32_t ioId, effect_interface_t *pInterface);
+int EffectQueryEffect(uint32_t index,
+ effect_descriptor_t *pDescriptor);
+int EffectCreate(effect_uuid_t *effectUID,
+ int32_t sessionId,
+ int32_t ioId,
+ effect_interface_t *pInterface);
int EffectRelease(effect_interface_t interface);
-static int Reverb_Process(effect_interface_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer);
-static int Reverb_Command(effect_interface_t self, int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData);
+static int Reverb_Process(effect_interface_t self,
+ audio_buffer_t *inBuffer,
+ audio_buffer_t *outBuffer);
+static int Reverb_Command(effect_interface_t self,
+ uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData);
/*------------------------------------
diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk
index 82cd92550b95..48b45ff52dde 100644
--- a/media/libeffects/visualizer/Android.mk
+++ b/media/libeffects/visualizer/Android.mk
@@ -15,7 +15,7 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
LOCAL_MODULE:= libvisualizer
ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
-LOCAL_LDLIBS += -ldlS
+LOCAL_LDLIBS += -ldl
endif
ifneq ($(TARGET_SIMULATOR),true)
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index ec135576b236..8ab57c932345 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -272,8 +272,8 @@ extern "C" int Visualizer_process(
return 0;
} // end Visualizer_process
-extern "C" int Visualizer_command(effect_interface_t self, int cmdCode, int cmdSize,
- void *pCmdData, int *replySize, void *pReplyData) {
+extern "C" int Visualizer_command(effect_interface_t self, uint32_t cmdCode, uint32_t cmdSize,
+ void *pCmdData, uint32_t *replySize, void *pReplyData) {
android::VisualizerContext * pContext = (android::VisualizerContext *)self;
int retsize;
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index df0f73bc87b2..3cdf48a4d51b 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -221,7 +221,11 @@ status_t AudioEffect::setEnabled(bool enabled)
return INVALID_OPERATION;
}
-status_t AudioEffect::command(int32_t cmdCode, int32_t cmdSize, void *cmdData, int32_t *replySize, void *replyData)
+status_t AudioEffect::command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *cmdData,
+ uint32_t *replySize,
+ void *replyData)
{
if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) {
return INVALID_OPERATION;
@@ -241,8 +245,8 @@ status_t AudioEffect::setParameter(effect_param_t *param)
return BAD_VALUE;
}
- int size = sizeof(int);
- int psize = ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;
+ uint32_t size = sizeof(int);
+ uint32_t psize = ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;
LOGV("setParameter: param: %d, param2: %d", *(int *)param->data, (param->psize == 8) ? *((int *)param->data + 1): -1);
@@ -285,7 +289,7 @@ status_t AudioEffect::setParameterCommit()
if (mCblk->clientIndex == 0) {
return INVALID_OPERATION;
}
- int size = 0;
+ uint32_t size = 0;
return mIEffect->command(EFFECT_CMD_SET_PARAM_COMMIT, 0, NULL, &size, NULL);
}
@@ -301,7 +305,7 @@ status_t AudioEffect::getParameter(effect_param_t *param)
LOGV("getParameter: param: %d, param2: %d", *(int *)param->data, (param->psize == 8) ? *((int *)param->data + 1): -1);
- int psize = sizeof(effect_param_t) + ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;
+ uint32_t psize = sizeof(effect_param_t) + ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;
return mIEffect->command(EFFECT_CMD_GET_PARAM, sizeof(effect_param_t) + param->psize, param, &psize, param);
}
@@ -350,7 +354,11 @@ void AudioEffect::enableStatusChanged(bool enabled)
}
}
-void AudioEffect::commandExecuted(int cmdCode, int cmdSize, void *cmdData, int replySize, void *replyData)
+void AudioEffect::commandExecuted(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *cmdData,
+ uint32_t replySize,
+ void *replyData)
{
if (cmdData == NULL || replyData == NULL) {
return;
diff --git a/media/libmedia/IEffect.cpp b/media/libmedia/IEffect.cpp
index 8e3ac7110d83..a945b978cf85 100644
--- a/media/libmedia/IEffect.cpp
+++ b/media/libmedia/IEffect.cpp
@@ -59,7 +59,11 @@ public:
return reply.readInt32();
}
- status_t command(int cmdCode, int cmdSize, void *pCmdData, int *pReplySize, void *pReplyData)
+ status_t command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *pReplySize,
+ void *pReplyData)
{
LOGV("command");
Parcel data, reply;
@@ -136,15 +140,15 @@ status_t BnEffect::onTransact(
case COMMAND: {
LOGV("COMMAND");
CHECK_INTERFACE(IEffect, data, reply);
- int cmdCode = data.readInt32();
- int cmdSize = data.readInt32();
+ uint32_t cmdCode = data.readInt32();
+ uint32_t cmdSize = data.readInt32();
char *cmd = NULL;
if (cmdSize) {
cmd = (char *)malloc(cmdSize);
data.read(cmd, cmdSize);
}
- int replySize = data.readInt32();
- int replySz = replySize;
+ uint32_t replySize = data.readInt32();
+ uint32_t replySz = replySize;
char *resp = NULL;
if (replySize) {
resp = (char *)malloc(replySize);
diff --git a/media/libmedia/IEffectClient.cpp b/media/libmedia/IEffectClient.cpp
index e7659ae2b099..1fa9cbea7eba 100644
--- a/media/libmedia/IEffectClient.cpp
+++ b/media/libmedia/IEffectClient.cpp
@@ -56,7 +56,11 @@ public:
remote()->transact(ENABLE_STATUS_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
}
- void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData)
+ void commandExecuted(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t replySize,
+ void *pReplyData)
{
LOGV("commandExecuted");
Parcel data, reply;
@@ -108,14 +112,14 @@ status_t BnEffectClient::onTransact(
case COMMAND_EXECUTED: {
LOGV("COMMAND_EXECUTED");
CHECK_INTERFACE(IEffectClient, data, reply);
- int cmdCode = data.readInt32();
- int cmdSize = data.readInt32();
+ uint32_t cmdCode = data.readInt32();
+ uint32_t cmdSize = data.readInt32();
char *cmd = NULL;
if (cmdSize) {
cmd = (char *)malloc(cmdSize);
data.read(cmd, cmdSize);
}
- int replySize = data.readInt32();
+ uint32_t replySize = data.readInt32();
char *resp = NULL;
if (replySize) {
resp = (char *)malloc(replySize);
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index 47e96e523372..32cdb491e387 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -184,7 +184,7 @@ status_t Visualizer::getWaveForm(uint8_t *waveform)
status_t status = NO_ERROR;
if (mEnabled) {
- int32_t replySize = mCaptureSize;
+ uint32_t replySize = mCaptureSize;
status_t status = command(VISU_CMD_CAPTURE, 0, NULL, &replySize, waveform);
if (replySize == 0) {
status = NOT_ENOUGH_DATA;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index b56f99734708..f26676da110e 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -981,8 +981,10 @@ status_t StagefrightRecorder::startMPEG4Recording() {
totalBitRate += mVideoBitRate;
}
- reinterpret_cast<MPEG4Writer *>(writer.get())->
- setInterleaveDuration(mInterleaveDurationUs);
+ if (mInterleaveDurationUs > 0) {
+ reinterpret_cast<MPEG4Writer *>(writer.get())->
+ setInterleaveDuration(mInterleaveDurationUs);
+ }
if (mMaxFileDurationUs != 0) {
writer->setMaxFileDuration(mMaxFileDurationUs);
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index b7bde6bdb097..c27cfc81739d 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -23,6 +23,7 @@
#include <media/stagefright/AudioPlayer.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>
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index c4a25bc5c3db..baf9f4f11864 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -66,11 +66,11 @@ private:
pthread_t mThread;
- struct SampleInfo {
- size_t size;
- int64_t timestampUs;
- };
- List<SampleInfo> mSampleInfos;
+ // mNumSamples is used to track how many samples in mSampleSizes List.
+ // This is to reduce the cost associated with mSampleSizes.size() call,
+ // since it is O(n). Ideally, the fix should be in List class.
+ size_t mNumSamples;
+ List<size_t> mSampleSizes;
bool mSamplesHaveSameSize;
List<MediaBuffer *> mChunkSamples;
@@ -145,7 +145,7 @@ MPEG4Writer::MPEG4Writer(const char *filename)
mOffset(0),
mMdatOffset(0),
mEstimatedMoovBoxSize(0),
- mInterleaveDurationUs(500000) {
+ mInterleaveDurationUs(1000000) {
CHECK(mFile != NULL);
}
@@ -157,7 +157,7 @@ MPEG4Writer::MPEG4Writer(int fd)
mOffset(0),
mMdatOffset(0),
mEstimatedMoovBoxSize(0),
- mInterleaveDurationUs(500000) {
+ mInterleaveDurationUs(1000000) {
CHECK(mFile != NULL);
}
@@ -916,6 +916,15 @@ status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
return OK;
}
+static bool collectStatisticalData() {
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("media.stagefright.record-stats", value, NULL)
+ && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+ return true;
+ }
+ return false;
+}
+
void MPEG4Writer::Track::threadEntry() {
sp<MetaData> meta = mSource->getFormat();
const char *mime;
@@ -935,7 +944,9 @@ void MPEG4Writer::Track::threadEntry() {
uint32_t previousSampleSize = 0; // Size of the previous sample
int64_t previousPausedDurationUs = 0;
sp<MetaData> meta_data;
+ bool collectStats = collectStatisticalData();
+ mNumSamples = 0;
status_t err = OK;
MediaBuffer *buffer;
while (!mDone && (err = mSource->read(&buffer)) == OK) {
@@ -1081,8 +1092,8 @@ void MPEG4Writer::Track::threadEntry() {
if (is_avc) StripStartcode(copy);
- SampleInfo info;
- info.size = is_avc
+ size_t sampleSize;
+ sampleSize = is_avc
#if USE_NALLEN_FOUR
? copy->range_length() + 4
#else
@@ -1091,7 +1102,7 @@ void MPEG4Writer::Track::threadEntry() {
: copy->range_length();
// Max file size or duration handling
- mEstimatedTrackSizeBytes += info.size;
+ mEstimatedTrackSizeBytes += sampleSize;
if (mOwner->exceedsFileSizeLimit()) {
mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
break;
@@ -1109,7 +1120,7 @@ void MPEG4Writer::Track::threadEntry() {
CHECK(meta_data->findInt64(kKeyTime, &timestampUs));
////////////////////////////////////////////////////////////////////////////////
- if (mSampleInfos.empty()) {
+ if (mSampleSizes.empty()) {
mStartTimestampUs = timestampUs;
mOwner->setStartTimestampUs(mStartTimestampUs);
}
@@ -1126,10 +1137,10 @@ void MPEG4Writer::Track::threadEntry() {
mMaxTimeStampUs = timestampUs;
}
- info.timestampUs = timestampUs;
- mSampleInfos.push_back(info);
- if (mSampleInfos.size() > 2) {
- if (lastDurationUs != info.timestampUs - lastTimestampUs) {
+ mSampleSizes.push_back(sampleSize);
+ ++mNumSamples;
+ if (mNumSamples > 2) {
+ if (lastDurationUs != timestampUs - lastTimestampUs) {
SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
mSttsTableEntries.push_back(sttsEntry);
sampleCount = 1;
@@ -1138,16 +1149,16 @@ void MPEG4Writer::Track::threadEntry() {
}
}
if (mSamplesHaveSameSize) {
- if (mSampleInfos.size() >= 2 && previousSampleSize != info.size) {
+ if (mNumSamples >= 2 && previousSampleSize != sampleSize) {
mSamplesHaveSameSize = false;
}
- previousSampleSize = info.size;
+ previousSampleSize = sampleSize;
}
- lastDurationUs = info.timestampUs - lastTimestampUs;
- lastTimestampUs = info.timestampUs;
+ lastDurationUs = timestampUs - lastTimestampUs;
+ lastTimestampUs = timestampUs;
if (isSync != 0) {
- mStssTableEntries.push_back(mSampleInfos.size());
+ mStssTableEntries.push_back(mNumSamples);
}
if (mTrackingProgressStatus) {
@@ -1178,7 +1189,9 @@ void MPEG4Writer::Track::threadEntry() {
} else {
if (timestampUs - chunkTimestampUs > interleaveDurationUs) {
++nChunks;
- mChunkDurations.push_back(timestampUs - chunkTimestampUs);
+ if (collectStats) {
+ mChunkDurations.push_back(timestampUs - chunkTimestampUs);
+ }
if (nChunks == 1 || // First chunk
(--(mStscTableEntries.end()))->samplesPerChunk !=
mChunkSamples.size()) {
@@ -1194,14 +1207,14 @@ void MPEG4Writer::Track::threadEntry() {
}
- if (mSampleInfos.empty()) {
+ if (mSampleSizes.empty()) {
err = UNKNOWN_ERROR;
}
mOwner->trackProgressStatus(this, -1, err);
// Last chunk
if (mOwner->numTracks() == 1) {
- StscTableEntry stscEntry(1, mSampleInfos.size(), 1);
+ StscTableEntry stscEntry(1, mNumSamples, 1);
mStscTableEntries.push_back(stscEntry);
} else if (!mChunkSamples.empty()) {
++nChunks;
@@ -1213,7 +1226,7 @@ void MPEG4Writer::Track::threadEntry() {
// We don't really know how long the last frame lasts, since
// there is no frame time after it, just repeat the previous
// frame's duration.
- if (mSampleInfos.size() == 1) {
+ if (mNumSamples == 1) {
lastDurationUs = 0; // A single sample's duration
} else {
++sampleCount; // Count for the last sample
@@ -1222,7 +1235,7 @@ void MPEG4Writer::Track::threadEntry() {
mSttsTableEntries.push_back(sttsEntry);
mReachedEOS = true;
LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s",
- count, nZeroLengthFrames, mSampleInfos.size(), is_audio? "audio": "video");
+ count, nZeroLengthFrames, mNumSamples, is_audio? "audio": "video");
logStatisticalData(is_audio);
}
@@ -1284,8 +1297,8 @@ void MPEG4Writer::trackProgressStatus(
void MPEG4Writer::Track::findMinAvgMaxSampleDurationMs(
int32_t *min, int32_t *avg, int32_t *max) {
- CHECK(!mSampleInfos.empty());
- int32_t avgSampleDurationMs = mMaxTimeStampUs / 1000 / mSampleInfos.size();
+ CHECK(!mSampleSizes.empty());
+ int32_t avgSampleDurationMs = mMaxTimeStampUs / 1000 / mNumSamples;
int32_t minSampleDurationMs = 0x7FFFFFFF;
int32_t maxSampleDurationMs = 0;
for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
@@ -1327,22 +1340,17 @@ void MPEG4Writer::Track::findMinMaxChunkDurations(int64_t *min, int64_t *max) {
}
void MPEG4Writer::Track::logStatisticalData(bool isAudio) {
- if (mMaxTimeStampUs <= 0 || mSampleInfos.empty()) {
+ if (mMaxTimeStampUs <= 0 || mSampleSizes.empty()) {
LOGI("nothing is recorded");
return;
}
- bool collectStats = false;
- char value[PROPERTY_VALUE_MAX];
- if (property_get("media.stagefright.record-stats", value, NULL)
- && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
- collectStats = true;
- }
+ bool collectStats = collectStatisticalData();
if (collectStats) {
LOGI("%s track - duration %lld us, total %d frames",
isAudio? "audio": "video", mMaxTimeStampUs,
- mSampleInfos.size());
+ mNumSamples);
int32_t min, avg, max;
findMinAvgMaxSampleDurationMs(&min, &avg, &max);
LOGI("min/avg/max sample duration (ms): %d/%d/%d", min, avg, max);
@@ -1355,9 +1363,9 @@ void MPEG4Writer::Track::logStatisticalData(bool isAudio) {
}
int64_t totalBytes = 0;
- for (List<SampleInfo>::iterator it = mSampleInfos.begin();
- it != mSampleInfos.end(); ++it) {
- totalBytes += it->size;
+ for (List<size_t>::iterator it = mSampleSizes.begin();
+ it != mSampleSizes.end(); ++it) {
+ totalBytes += (*it);
}
float bitRate = (totalBytes * 8000000.0) / mMaxTimeStampUs;
LOGI("avg bit rate (bps): %.2f", bitRate);
@@ -1731,16 +1739,16 @@ void MPEG4Writer::Track::writeTrackHeader(
mOwner->beginBox("stsz");
mOwner->writeInt32(0); // version=0, flags=0
if (mSamplesHaveSameSize) {
- List<SampleInfo>::iterator it = mSampleInfos.begin();
- mOwner->writeInt32(it->size); // default sample size
+ List<size_t>::iterator it = mSampleSizes.begin();
+ mOwner->writeInt32(*it); // default sample size
} else {
mOwner->writeInt32(0);
}
- mOwner->writeInt32(mSampleInfos.size());
+ mOwner->writeInt32(mNumSamples);
if (!mSamplesHaveSameSize) {
- for (List<SampleInfo>::iterator it = mSampleInfos.begin();
- it != mSampleInfos.end(); ++it) {
- mOwner->writeInt32((*it).size);
+ for (List<size_t>::iterator it = mSampleSizes.begin();
+ it != mSampleSizes.end(); ++it) {
+ mOwner->writeInt32(*it);
}
}
mOwner->endBox(); // stsz
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 59bf71178f7c..a82282d35a03 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -21,14 +21,21 @@
#include <ui/Input.h>
#include <ui/InputTransport.h>
#include <utils/PollLoop.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
#include <android_runtime/android_app_NativeActivity.h>
#include <poll.h>
+#include <errno.h>
using android::InputEvent;
using android::KeyEvent;
using android::MotionEvent;
+using android::InputDeviceInfo;
+using android::InputDeviceProxy;
+using android::sp;
+using android::Vector;
int32_t AInputEvent_getType(const AInputEvent* event) {
return static_cast<const InputEvent*>(event)->getType();
@@ -263,3 +270,74 @@ int32_t AInputQueue_preDispatchEvent(AInputQueue* queue, AInputEvent* event) {
void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled) {
queue->finishEvent(event, handled != 0);
}
+
+
+int32_t AInputDevice_getDeviceIds(int32_t* idBuf, size_t nMax, size_t* nActual) {
+ Vector<int32_t> ids;
+ InputDeviceProxy::getDeviceIds(ids);
+
+ if (nActual) {
+ *nActual = ids.size();
+ }
+
+ if (idBuf && ids.size() < nMax) {
+ memcpy(idBuf, ids.array(), ids.size() * sizeof(int32_t));
+ return 0;
+ }
+
+ return -ENOMEM;
+}
+
+AInputDevice* AInputDevice_acquire(int32_t deviceId) {
+ sp<InputDeviceProxy> proxy(InputDeviceProxy::getDevice(deviceId));
+ if (proxy == NULL) {
+ return NULL;
+ }
+ proxy->incStrong((void*)AInputDevice_acquire);
+ return static_cast<AInputDevice*>(proxy.get());
+}
+
+void AInputDevice_release(AInputDevice* device) {
+ if (device) {
+ InputDeviceProxy* proxy = static_cast<InputDeviceProxy*>(device);
+ proxy->decStrong((void*)AInputDevice_acquire);
+ }
+}
+
+const char* AInputDevice_getName(AInputDevice* device) {
+ InputDeviceProxy* proxy = static_cast<InputDeviceProxy*>(device);
+ return proxy->getInfo()->getName().string();
+}
+
+uint32_t AInputDevice_getSources(AInputDevice* device) {
+ InputDeviceProxy* proxy = static_cast<InputDeviceProxy*>(device);
+ return proxy->getInfo()->getSources();
+}
+
+int32_t AInputDevice_getKeyboardType(AInputDevice* device) {
+ InputDeviceProxy* proxy = static_cast<InputDeviceProxy*>(device);
+ return proxy->getInfo()->getKeyboardType();
+}
+
+int32_t AInputDevice_getMotionRange(AInputDevice* device, int32_t rangeType,
+ float* outMin, float* outMax, float* outFlat, float* outFuzz) {
+ InputDeviceProxy* proxy = static_cast<InputDeviceProxy*>(device);
+ const InputDeviceInfo::MotionRange* range = proxy->getInfo()->getMotionRange(rangeType);
+ if (range) {
+ if (outMin) {
+ *outMin = range->min;
+ }
+ if (outMax) {
+ *outMax = range->max;
+ }
+ if (outFlat) {
+ *outFlat = range->flat;
+ }
+ if (outFuzz) {
+ *outFuzz = range->fuzz;
+ }
+ return 0;
+ } else {
+ return -ENOTSUP;
+ }
+}
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index e1fc4e7bc9ac..db534e09dab8 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -149,3 +149,7 @@ float ASensor_getResolution(ASensor const* sensor)
return static_cast<Sensor const*>(sensor)->getResolution();
}
+int ASensor_getMinDelay(ASensor const* sensor)
+{
+ return static_cast<Sensor const*>(sensor)->getMinDelay();
+}
diff --git a/native/include/android/input.h b/native/include/android/input.h
index 0b8c7e49d246..243c33c3a714 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -40,6 +40,7 @@
* NOTE: These functions MUST be implemented by /system/lib/libui.so
*/
+#include <stdint.h>
#include <sys/types.h>
#include <android/keycodes.h>
#include <android/looper.h>
@@ -268,7 +269,6 @@ enum {
/*
* Input sources.
*
- * The appropriate interpretation for an input event depends on its source.
* Refer to the documentation on android.view.InputDevice for more details about input sources
* and their correct interpretation.
*/
@@ -297,6 +297,37 @@ enum {
};
/*
+ * Keyboard types.
+ *
+ * Refer to the documentation on android.view.InputDevice for more details.
+ */
+enum {
+ AINPUT_KEYBOARD_TYPE_NONE = 0,
+ AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC = 1,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC = 2,
+};
+
+/*
+ * Constants used to retrieve information about the range of motion for a particular
+ * coordinate of a motion event.
+ *
+ * Refer to the documentation on android.view.InputDevice for more details about input sources
+ * and their correct interpretation.
+ */
+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,
+};
+
+
+/*
* Input event accessors.
*
* Note that most functions can only be used on input events that are of a given type.
@@ -475,7 +506,7 @@ float AMotionEvent_getToolMinor(const AInputEvent* motion_event, size_t pointer_
* 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/4 radians (finger pointing fully left) to PI/4 radians
+ * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
* (finger pointing fully right). */
float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointer_index);
@@ -575,7 +606,7 @@ float AMotionEvent_getHistoricalToolMinor(const AInputEvent* motion_event, size_
* 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/4 radians (finger pointing fully left) to PI/4 radians
+ * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
* (finger pointing fully right). */
float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, size_t pointer_index,
size_t history_index);
@@ -631,6 +662,64 @@ int32_t AInputQueue_preDispatchEvent(AInputQueue* queue, AInputEvent* event);
*/
void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled);
+/*
+ * Input devices.
+ *
+ * These functions provide a mechanism for querying the set of available input devices
+ * and their characteristics and capabilities.
+ */
+struct AInputDevice;
+typedef struct AInputDevice AInputDevice;
+
+/*
+ * Populates the supplied array with the ids of all input devices in the system.
+ * Sets nActual to the actual number of devices.
+ * Returns zero if enumeration was successful.
+ * Returns non-zero if the actual number of devices is greater than nMax, in which case the
+ * client should call the method again with a larger id buffer.
+ */
+int32_t AInputDevice_getDeviceIds(int32_t* idBuf, size_t nMax, size_t* nActual);
+
+/*
+ * Acquires a device by id.
+ * Returns NULL if the device was not found.
+ *
+ * Note: The returned object must be freed using AInputDevice_release when no longer needed.
+ */
+AInputDevice* AInputDevice_acquire(int32_t deviceId);
+
+/*
+ * Releases a device previously acquired by AInputDevice_acquire.
+ * If device is NULL, this function does nothing.
+ */
+void AInputDevice_release(AInputDevice* device);
+
+/*
+ * Gets the name of an input device.
+ *
+ * Note: The caller should copy the name into a private buffer since the returned pointer
+ * will become invalid when the device object is released.
+ */
+const char* AInputDevice_getName(AInputDevice* device);
+
+/*
+ * Gets the combination of input sources provided by the input device.
+ */
+uint32_t AInputDevice_getSources(AInputDevice* device);
+
+/*
+ * Gets the keyboard type.
+ */
+int32_t AInputDevice_getKeyboardType(AInputDevice* device);
+
+/* Gets the minimum value, maximum value, flat position and error tolerance for a
+ * particular motion coodinate.
+ * Returns zero if the device supports the specified motion range. */
+int32_t AInputDevice_getMotionRange(AInputDevice* device, int32_t rangeType,
+ float* outMin, float* outMax, float* outFlat, float* outFuzz);
+
+//TODO hasKey, keymap stuff, etc...
+
#ifdef __cplusplus
}
#endif
diff --git a/native/include/android/sensor.h b/native/include/android/sensor.h
index 00d95d8e64fc..b4ce0242682d 100644
--- a/native/include/android/sensor.h
+++ b/native/include/android/sensor.h
@@ -121,6 +121,7 @@ typedef struct ASensorEvent {
float temperature;
float distance;
float light;
+ float pressure;
};
int32_t reserved1[4];
} ASensorEvent;
@@ -188,7 +189,8 @@ int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor const* sen
/*
* Sets the delivery rate of events in microseconds for the given sensor.
* Note that this is a hint only, generally event will arrive at a higher
- * rate.
+ * rate. It is an error to set a rate inferior to the value returned by
+ * ASensor_getMinDelay().
* Returns a negative error code on failure.
*/
int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor const* sensor, int32_t usec);
@@ -239,6 +241,13 @@ int ASensor_getType(ASensor const* sensor);
*/
float ASensor_getResolution(ASensor const* sensor);
+/*
+ * Returns the minimum delay allowed between events in microseconds.
+ * A value of zero means that this sensor doesn't report events at a
+ * constant rate, but rather only when a new data is available.
+ */
+int ASensor_getMinDelay(ASensor const* sensor);
+
#ifdef __cplusplus
};
diff --git a/packages/DefaultContainerService/res/values/strings.xml b/packages/DefaultContainerService/res/values/strings.xml
index 2897f34d5845..37f5b61fd59a 100644
--- a/packages/DefaultContainerService/res/values/strings.xml
+++ b/packages/DefaultContainerService/res/values/strings.xml
@@ -19,5 +19,5 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- service name -->
- <string name="service_name">Media Container Service</string>
+ <string name="service_name">Package Access Helper</string>
</resources>
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index b88e69d88580..252b42ab097f 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -307,6 +307,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
{
Mutex::Autolock _l(mLock);
PlaybackThread *thread = checkPlaybackThread_l(output);
+ PlaybackThread *effectThread = NULL;
if (thread == NULL) {
LOGE("unknown output thread");
lStatus = BAD_VALUE;
@@ -324,12 +325,19 @@ sp<IAudioTrack> AudioFlinger::createTrack(
LOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
- // prevent same audio session on different output threads
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- if (mPlaybackThreads.keyAt(i) != output &&
- mPlaybackThreads.valueAt(i)->hasAudioSession(*sessionId)) {
- lStatus = BAD_VALUE;
- goto Exit;
+ sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
+ if (mPlaybackThreads.keyAt(i) != output) {
+ // prevent same audio session on different output threads
+ uint32_t sessions = t->hasAudioSession(*sessionId);
+ if (sessions & PlaybackThread::TRACK_SESSION) {
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ // check if an effect with same session ID is waiting for a track to be created
+ if (sessions & PlaybackThread::EFFECT_SESSION) {
+ effectThread = t.get();
+ }
}
}
lSessionId = *sessionId;
@@ -344,6 +352,14 @@ sp<IAudioTrack> AudioFlinger::createTrack(
track = thread->createTrack_l(client, streamType, sampleRate, format,
channelCount, frameCount, sharedBuffer, lSessionId, &lStatus);
+
+ // move effect chain to this output thread if an effect on same session was waiting
+ // for a track to be created
+ if (lStatus == NO_ERROR && effectThread != NULL) {
+ Mutex::Autolock _dl(thread->mLock);
+ Mutex::Autolock _sl(effectThread->mLock);
+ moveEffectChain_l(lSessionId, effectThread, thread, true);
+ }
}
if (lStatus == NO_ERROR) {
trackHandle = new TrackHandle(track);
@@ -1377,7 +1393,7 @@ void AudioFlinger::PlaybackThread::readOutputParameters()
// create a copy of mEffectChains as calling moveEffectChain_l() can reorder some effect chains
Vector< sp<EffectChain> > effectChains = mEffectChains;
for (size_t i = 0; i < effectChains.size(); i ++) {
- mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(), this, this);
+ mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(), this, this, false);
}
}
@@ -1394,22 +1410,24 @@ status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, ui
return mOutput->getRenderPosition(dspFrames);
}
-bool AudioFlinger::PlaybackThread::hasAudioSession(int sessionId)
+uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId)
{
Mutex::Autolock _l(mLock);
+ uint32_t result = 0;
if (getEffectChain_l(sessionId) != 0) {
- return true;
+ result = EFFECT_SESSION;
}
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> track = mTracks[i];
if (sessionId == track->sessionId() &&
!(track->mCblk->flags & CBLK_INVALID_MSK)) {
- return true;
+ result |= TRACK_SESSION;
+ break;
}
}
- return false;
+ return result;
}
uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(int sessionId)
@@ -1997,7 +2015,7 @@ uint32_t AudioFlinger::MixerThread::activeSleepTimeUs()
uint32_t AudioFlinger::MixerThread::idleSleepTimeUs()
{
- return (uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000;
+ return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
}
// ----------------------------------------------------------------------------
@@ -2458,7 +2476,7 @@ uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs()
{
uint32_t time;
if (AudioSystem::isLinearPCM(mFormat)) {
- time = (uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000;
+ time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
} else {
time = 10000;
}
@@ -4704,11 +4722,17 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
} else {
// look for the thread where the specified audio session is present
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId)) {
+ if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) {
output = mPlaybackThreads.keyAt(i);
break;
}
}
+ // If no output thread contains the requested session ID, default to
+ // first output. The effect chain will be moved to the correct output
+ // thread when a track with the same session ID is created
+ if (output == 0 && mPlaybackThreads.size()) {
+ output = mPlaybackThreads.keyAt(0);
+ }
}
}
PlaybackThread *thread = checkPlaybackThread_l(output);
@@ -4764,7 +4788,7 @@ status_t AudioFlinger::moveEffects(int session, int srcOutput, int dstOutput)
Mutex::Autolock _dl(dstThread->mLock);
Mutex::Autolock _sl(srcThread->mLock);
- moveEffectChain_l(session, srcThread, dstThread);
+ moveEffectChain_l(session, srcThread, dstThread, false);
return NO_ERROR;
}
@@ -4772,7 +4796,8 @@ status_t AudioFlinger::moveEffects(int session, int srcOutput, int dstOutput)
// moveEffectChain_l mustbe called with both srcThread and dstThread mLocks held
status_t AudioFlinger::moveEffectChain_l(int session,
AudioFlinger::PlaybackThread *srcThread,
- AudioFlinger::PlaybackThread *dstThread)
+ AudioFlinger::PlaybackThread *dstThread,
+ bool reRegister)
{
LOGV("moveEffectChain_l() session %d from thread %p to thread %p",
session, srcThread, dstThread);
@@ -4784,7 +4809,7 @@ status_t AudioFlinger::moveEffectChain_l(int session,
return INVALID_OPERATION;
}
- // remove chain first. This is usefull only if reconfiguring effect chain on same output thread,
+ // remove chain first. This is useful only if reconfiguring effect chain on same output thread,
// so that a new chain is created with correct parameters when first effect is added. This is
// otherwise unecessary as removeEffect_l() will remove the chain when last effect is
// removed.
@@ -4792,10 +4817,32 @@ status_t AudioFlinger::moveEffectChain_l(int session,
// transfer all effects one by one so that new effect chain is created on new thread with
// correct buffer sizes and audio parameters and effect engines reconfigured accordingly
+ int dstOutput = dstThread->id();
+ sp<EffectChain> dstChain;
+ uint32_t strategy;
sp<EffectModule> effect = chain->getEffectFromId_l(0);
while (effect != 0) {
srcThread->removeEffect_l(effect);
dstThread->addEffect_l(effect);
+ // if the move request is not received from audio policy manager, the effect must be
+ // re-registered with the new strategy and output
+ if (dstChain == 0) {
+ dstChain = effect->chain().promote();
+ if (dstChain == 0) {
+ LOGW("moveEffectChain_l() cannot get chain from effect %p", effect.get());
+ srcThread->addEffect_l(effect);
+ return NO_INIT;
+ }
+ strategy = dstChain->strategy();
+ }
+ if (reRegister) {
+ AudioSystem::unregisterEffect(effect->id());
+ AudioSystem::registerEffect(&effect->desc(),
+ dstOutput,
+ strategy,
+ session,
+ effect->id());
+ }
effect = chain->getEffectFromId_l(0);
}
@@ -5408,8 +5455,13 @@ status_t AudioFlinger::EffectModule::configure()
this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
status_t cmdStatus;
- int size = sizeof(int);
- status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_CONFIGURE, sizeof(effect_config_t), &mConfig, &size, &cmdStatus);
+ uint32_t size = sizeof(int);
+ status_t status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_CONFIGURE,
+ sizeof(effect_config_t),
+ &mConfig,
+ &size,
+ &cmdStatus);
if (status == 0) {
status = cmdStatus;
}
@@ -5427,8 +5479,13 @@ status_t AudioFlinger::EffectModule::init()
return NO_INIT;
}
status_t cmdStatus;
- int size = sizeof(status_t);
- status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_INIT, 0, NULL, &size, &cmdStatus);
+ uint32_t size = sizeof(status_t);
+ status_t status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_INIT,
+ 0,
+ NULL,
+ &size,
+ &cmdStatus);
if (status == 0) {
status = cmdStatus;
}
@@ -5441,8 +5498,13 @@ status_t AudioFlinger::EffectModule::start_l()
return NO_INIT;
}
status_t cmdStatus;
- int size = sizeof(status_t);
- status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_ENABLE, 0, NULL, &size, &cmdStatus);
+ uint32_t size = sizeof(status_t);
+ status_t status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_ENABLE,
+ 0,
+ NULL,
+ &size,
+ &cmdStatus);
if (status == 0) {
status = cmdStatus;
}
@@ -5455,15 +5517,24 @@ status_t AudioFlinger::EffectModule::stop_l()
return NO_INIT;
}
status_t cmdStatus;
- int size = sizeof(status_t);
- status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_DISABLE, 0, NULL, &size, &cmdStatus);
+ uint32_t size = sizeof(status_t);
+ status_t status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_DISABLE,
+ 0,
+ NULL,
+ &size,
+ &cmdStatus);
if (status == 0) {
status = cmdStatus;
}
return status;
}
-status_t AudioFlinger::EffectModule::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData)
+status_t AudioFlinger::EffectModule::command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData)
{
Mutex::Autolock _l(mLock);
// LOGV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface);
@@ -5471,9 +5542,14 @@ status_t AudioFlinger::EffectModule::command(int cmdCode, int cmdSize, void *pCm
if (mEffectInterface == NULL) {
return NO_INIT;
}
- status_t status = (*mEffectInterface)->command(mEffectInterface, cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+ status_t status = (*mEffectInterface)->command(mEffectInterface,
+ cmdCode,
+ cmdSize,
+ pCmdData,
+ replySize,
+ pReplyData);
if (cmdCode != EFFECT_CMD_GET_PARAM && status == NO_ERROR) {
- int size = (replySize == NULL) ? 0 : *replySize;
+ uint32_t size = (replySize == NULL) ? 0 : *replySize;
for (size_t i = 1; i < mHandles.size(); i++) {
sp<EffectHandle> h = mHandles[i].promote();
if (h != 0) {
@@ -5549,13 +5625,18 @@ status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right,
status_t cmdStatus;
uint32_t volume[2];
uint32_t *pVolume = NULL;
- int size = sizeof(volume);
+ uint32_t size = sizeof(volume);
volume[0] = *left;
volume[1] = *right;
if (controller) {
pVolume = volume;
}
- status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_VOLUME, size, volume, &size, pVolume);
+ status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_SET_VOLUME,
+ size,
+ volume,
+ &size,
+ pVolume);
if (controller && status == NO_ERROR && size == sizeof(volume)) {
*left = volume[0];
*right = volume[1];
@@ -5575,8 +5656,13 @@ status_t AudioFlinger::EffectModule::setDevice(uint32_t device)
return BAD_VALUE;
}
status_t cmdStatus;
- int size = sizeof(status_t);
- status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_DEVICE, sizeof(uint32_t), &device, &size, &cmdStatus);
+ uint32_t size = sizeof(status_t);
+ status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_SET_DEVICE,
+ sizeof(uint32_t),
+ &device,
+ &size,
+ &cmdStatus);
if (status == NO_ERROR) {
status = cmdStatus;
}
@@ -5595,8 +5681,13 @@ status_t AudioFlinger::EffectModule::setMode(uint32_t mode)
return BAD_VALUE;
}
status_t cmdStatus;
- int size = sizeof(status_t);
- status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_AUDIO_MODE, sizeof(int), &effectMode, &size, &cmdStatus);
+ uint32_t size = sizeof(status_t);
+ status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_SET_AUDIO_MODE,
+ sizeof(int),
+ &effectMode,
+ &size,
+ &cmdStatus);
if (status == NO_ERROR) {
status = cmdStatus;
}
@@ -5805,9 +5896,14 @@ void AudioFlinger::EffectHandle::disconnect()
}
}
-status_t AudioFlinger::EffectHandle::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData)
+status_t AudioFlinger::EffectHandle::command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData)
{
-// LOGV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p", cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get());
+// LOGV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p",
+// cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get());
// only get parameter command is permitted for applications not controlling the effect
if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) {
@@ -5829,7 +5925,7 @@ status_t AudioFlinger::EffectHandle::command(int cmdCode, int cmdSize, void *pCm
status_t status = NO_ERROR;
while (mCblk->serverIndex < mCblk->clientIndex) {
int reply;
- int rsize = sizeof(int);
+ uint32_t rsize = sizeof(int);
int *p = (int *)(mBuffer + mCblk->serverIndex);
int size = *p++;
if (((uint8_t *)p + size) > mBuffer + mCblk->clientIndex) {
@@ -5842,8 +5938,14 @@ status_t AudioFlinger::EffectHandle::command(int cmdCode, int cmdSize, void *pCm
mCblk->serverIndex += size;
continue;
}
- int psize = sizeof(effect_param_t) + ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;
- status_t ret = mEffect->command(EFFECT_CMD_SET_PARAM, psize, p, &rsize, &reply);
+ uint32_t psize = sizeof(effect_param_t) +
+ ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) +
+ param->vsize;
+ status_t ret = mEffect->command(EFFECT_CMD_SET_PARAM,
+ psize,
+ p,
+ &rsize,
+ &reply);
if (ret == NO_ERROR) {
if (reply != NO_ERROR) {
status = reply;
@@ -5879,7 +5981,11 @@ void AudioFlinger::EffectHandle::setControl(bool hasControl, bool signal)
}
}
-void AudioFlinger::EffectHandle::commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData)
+void AudioFlinger::EffectHandle::commandExecuted(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t replySize,
+ void *pReplyData)
{
if (mEffectClient != 0) {
mEffectClient->commandExecuted(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index a8c9a92da919..5520551992f4 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -613,7 +613,15 @@ private:
void disconnectEffect(const sp< EffectModule>& effect,
const wp<EffectHandle>& handle);
- bool hasAudioSession(int sessionId);
+ // return values for hasAudioSession (bit field)
+ enum effect_state {
+ EFFECT_SESSION = 0x1, // the audio session corresponds to at least one
+ // effect
+ TRACK_SESSION = 0x2 // the audio session corresponds to at least one
+ // track
+ };
+
+ uint32_t hasAudioSession(int sessionId);
sp<EffectChain> getEffectChain(int sessionId);
sp<EffectChain> getEffectChain_l(int sessionId);
status_t addEffectChain_l(const sp<EffectChain>& chain);
@@ -776,7 +784,8 @@ private:
int nextUniqueId();
status_t moveEffectChain_l(int session,
AudioFlinger::PlaybackThread *srcThread,
- AudioFlinger::PlaybackThread *dstThread);
+ AudioFlinger::PlaybackThread *dstThread,
+ bool reRegister);
friend class AudioBuffer;
@@ -933,7 +942,11 @@ private:
int id() { return mId; }
void process();
void updateState();
- status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData);
+ status_t command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData);
void reset_l();
status_t configure();
@@ -1023,7 +1036,11 @@ private:
// IEffect
virtual status_t enable();
virtual status_t disable();
- virtual status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData);
+ virtual status_t command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData);
virtual void disconnect();
virtual sp<IMemory> getCblk() const;
virtual status_t onTransact(uint32_t code, const Parcel& data,
@@ -1032,7 +1049,11 @@ private:
// Give or take control of effect module
void setControl(bool hasControl, bool signal);
- void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData);
+ void commandExecuted(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t replySize,
+ void *pReplyData);
void setEnabled(bool enabled);
// Getters
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index 0de11c65c856..14b7d3ec3891 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -690,8 +690,6 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
// was lost. Tombstones are expunged by age (see above).
if (mAllFiles.blocks > mCachedQuotaBlocks) {
- Slog.i(TAG, "Usage (" + mAllFiles.blocks + ") > Quota (" + mCachedQuotaBlocks + ")");
-
// Find a fair share amount of space to limit each tag
int unsqueezed = mAllFiles.blocks, squeezed = 0;
TreeSet<FileList> tags = new TreeSet<FileList>(mFilesByTag.values());
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index b4f46ab55a97..91951233fca1 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -29,6 +29,7 @@ import android.os.PowerManager;
import android.util.Slog;
import android.util.Xml;
import android.view.InputChannel;
+import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
@@ -66,39 +67,50 @@ public class InputManager {
private static native void nativeSetDisplaySize(int displayId, int width, int height);
private static native void nativeSetDisplayOrientation(int displayId, int rotation);
- private static native int nativeGetScanCodeState(int deviceId, int deviceClasses,
+ private static native int nativeGetScanCodeState(int deviceId, int sourceMask,
int scanCode);
- private static native int nativeGetKeyCodeState(int deviceId, int deviceClasses,
+ private static native int nativeGetKeyCodeState(int deviceId, int sourceMask,
int keyCode);
- private static native int nativeGetSwitchState(int deviceId, int deviceClasses,
+ private static native int nativeGetSwitchState(int deviceId, int sourceMask,
int sw);
- private static native boolean nativeHasKeys(int[] keyCodes, boolean[] keyExists);
+ private static native boolean nativeHasKeys(int deviceId, int sourceMask,
+ int[] keyCodes, boolean[] keyExists);
private static native void nativeRegisterInputChannel(InputChannel inputChannel);
private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
- private static native int nativeInjectKeyEvent(KeyEvent event,
- int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
- private static native int nativeInjectMotionEvent(MotionEvent event,
- int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
+ private static native int nativeInjectInputEvent(InputEvent event,
+ int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
private static native void nativeSetInputWindows(InputWindow[] windows);
private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
private static native void nativeSetFocusedApplication(InputApplication application);
private static native void nativePreemptInputDispatch();
private static native String nativeDump();
- // Device class as defined by EventHub.
- private static final int CLASS_KEYBOARD = 0x00000001;
- private static final int CLASS_ALPHAKEY = 0x00000002;
- private static final int CLASS_TOUCHSCREEN = 0x00000004;
- private static final int CLASS_TRACKBALL = 0x00000008;
- private static final int CLASS_TOUCHSCREEN_MT = 0x00000010;
- private static final int CLASS_DPAD = 0x00000020;
-
// Input event injection constants defined in InputDispatcher.h.
static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1;
static final int INPUT_EVENT_INJECTION_FAILED = 2;
static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3;
+ // Input event injection synchronization modes defined in InputDispatcher.h
+ static final int INPUT_EVENT_INJECTION_SYNC_NONE = 0;
+ static final int INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1;
+ static final int INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH = 2;
+
+ // Key states (may be returned by queries about the current state of a
+ // particular key code, scan code or switch).
+
+ /** The key state is unknown or the requested key itself is not supported. */
+ public static final int KEY_STATE_UNKNOWN = -1;
+
+ /** The key is up. /*/
+ public static final int KEY_STATE_UP = 0;
+
+ /** The key is down. */
+ public static final int KEY_STATE_DOWN = 1;
+
+ /** The key is down but is a virtual key press that is being emulated by the system. */
+ public static final int KEY_STATE_VIRTUAL = 2;
+
public InputManager(Context context, WindowManagerService windowManagerService) {
this.mContext = context;
this.mWindowManagerService = windowManagerService;
@@ -150,55 +162,67 @@ public class InputManager {
config.navigation = mNavigationConfig;
}
- public int getScancodeState(int code) {
- return nativeGetScanCodeState(0, -1, code);
- }
-
- public int getScancodeState(int deviceId, int code) {
- return nativeGetScanCodeState(deviceId, -1, code);
- }
-
- public int getTrackballScancodeState(int code) {
- return nativeGetScanCodeState(-1, CLASS_TRACKBALL, code);
- }
-
- public int getDPadScancodeState(int code) {
- return nativeGetScanCodeState(-1, CLASS_DPAD, code);
- }
-
- public int getKeycodeState(int code) {
- return nativeGetKeyCodeState(0, -1, code);
- }
-
- public int getKeycodeState(int deviceId, int code) {
- return nativeGetKeyCodeState(deviceId, -1, code);
- }
-
- public int getTrackballKeycodeState(int code) {
- return nativeGetKeyCodeState(-1, CLASS_TRACKBALL, code);
+ /**
+ * Gets the current state of a key or button by key code.
+ * @param deviceId The input device id, or -1 to consult all devices.
+ * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+ * consider all input sources. An input device is consulted if at least one of its
+ * non-class input source bits matches the specified source mask.
+ * @param keyCode The key code to check.
+ * @return The key state.
+ */
+ public int getKeyCodeState(int deviceId, int sourceMask, int keyCode) {
+ return nativeGetKeyCodeState(deviceId, sourceMask, keyCode);
}
- public int getDPadKeycodeState(int code) {
- return nativeGetKeyCodeState(-1, CLASS_DPAD, code);
- }
-
- public int getSwitchState(int sw) {
- return nativeGetSwitchState(-1, -1, sw);
+ /**
+ * Gets the current state of a key or button by scan code.
+ * @param deviceId The input device id, or -1 to consult all devices.
+ * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+ * consider all input sources. An input device is consulted if at least one of its
+ * non-class input source bits matches the specified source mask.
+ * @param scanCode The scan code to check.
+ * @return The key state.
+ */
+ public int getScanCodeState(int deviceId, int sourceMask, int scanCode) {
+ return nativeGetScanCodeState(deviceId, sourceMask, scanCode);
}
- public int getSwitchState(int deviceId, int sw) {
- return nativeGetSwitchState(deviceId, -1, sw);
+ /**
+ * Gets the current state of a switch by switch code.
+ * @param deviceId The input device id, or -1 to consult all devices.
+ * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+ * consider all input sources. An input device is consulted if at least one of its
+ * non-class input source bits matches the specified source mask.
+ * @param switchCode The switch code to check.
+ * @return The switch state.
+ */
+ public int getSwitchState(int deviceId, int sourceMask, int switchCode) {
+ return nativeGetSwitchState(deviceId, sourceMask, switchCode);
}
- public boolean hasKeys(int[] keyCodes, boolean[] keyExists) {
+ /**
+ * Determines whether the specified key codes are supported by a particular device.
+ * @param deviceId The input device id, or -1 to consult all devices.
+ * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+ * consider all input sources. An input device is consulted if at least one of its
+ * non-class input source bits matches the specified source mask.
+ * @param keyCodes The array of key codes to check.
+ * @param keyExists An array at least as large as keyCodes whose entries will be set
+ * to true or false based on the presence or absence of support for the corresponding
+ * key codes.
+ * @return True if the lookup was successful, false otherwise.
+ */
+ public boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists) {
if (keyCodes == null) {
throw new IllegalArgumentException("keyCodes must not be null.");
}
- if (keyExists == null) {
- throw new IllegalArgumentException("keyExists must not be null.");
+ if (keyExists == null || keyExists.length < keyCodes.length) {
+ throw new IllegalArgumentException("keyExists must not be null and must be at "
+ + "least as large as keyCodes.");
}
- return nativeHasKeys(keyCodes, keyExists);
+ return nativeHasKeys(deviceId, sourceMask, keyCodes, keyExists);
}
public void registerInputChannel(InputChannel inputChannel) {
@@ -218,19 +242,30 @@ public class InputManager {
}
/**
- * Injects a key event into the event system on behalf of an application.
- * This method may block even if sync is false because it must wait for previous events
- * to be dispatched before it can determine whether input event injection will be
- * permitted based on the current input focus.
+ * Injects an input event into the event system on behalf of an application.
+ * The synchronization mode determines whether the method blocks while waiting for
+ * input injection to proceed.
+ *
+ * {@link #INPUT_EVENT_INJECTION_SYNC_NONE} never blocks. Injection is asynchronous and
+ * is assumed always to be successful.
+ *
+ * {@link #INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT} waits for previous events to be
+ * dispatched so that the input dispatcher can determine whether input event injection will
+ * be permitted based on the current input focus. Does not wait for the input event to
+ * finish processing.
+ *
+ * {@link #INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH} waits for the input event to
+ * be completely processed.
+ *
* @param event The event to inject.
* @param injectorPid The pid of the injecting application.
* @param injectorUid The uid of the injecting application.
- * @param sync If true, waits for the event to be completed before returning.
+ * @param syncMode The synchronization mode.
* @param timeoutMillis The injection timeout in milliseconds.
* @return One of the INPUT_EVENT_INJECTION_XXX constants.
*/
- public int injectKeyEvent(KeyEvent event, int injectorPid, int injectorUid,
- boolean sync, int timeoutMillis) {
+ public int injectInputEvent(InputEvent event, int injectorPid, int injectorUid,
+ int syncMode, int timeoutMillis) {
if (event == null) {
throw new IllegalArgumentException("event must not be null");
}
@@ -240,38 +275,8 @@ public class InputManager {
if (timeoutMillis <= 0) {
throw new IllegalArgumentException("timeoutMillis must be positive");
}
-
- return nativeInjectKeyEvent(event, injectorPid, injectorUid,
- sync, timeoutMillis);
- }
-
- /**
- * Injects a motion event into the event system on behalf of an application.
- * This method may block even if sync is false because it must wait for previous events
- * to be dispatched before it can determine whether input event injection will be
- * permitted based on the current input focus.
- * @param event The event to inject.
- * @param sync If true, waits for the event to be completed before returning.
- * @param injectorPid The pid of the injecting application.
- * @param injectorUid The uid of the injecting application.
- * @param sync If true, waits for the event to be completed before returning.
- * @param timeoutMillis The injection timeout in milliseconds.
- * @return One of the INPUT_EVENT_INJECTION_XXX constants.
- */
- public int injectMotionEvent(MotionEvent event, int injectorPid, int injectorUid,
- boolean sync, int timeoutMillis) {
- if (event == null) {
- throw new IllegalArgumentException("event must not be null");
- }
- if (injectorPid < 0 || injectorUid < 0) {
- throw new IllegalArgumentException("injectorPid and injectorUid must not be negative.");
- }
- if (timeoutMillis <= 0) {
- throw new IllegalArgumentException("timeoutMillis must be positive");
- }
-
- return nativeInjectMotionEvent(event, injectorPid, injectorUid,
- sync, timeoutMillis);
+
+ return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
}
public void setInputWindows(InputWindow[] windows) {
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 11d0b7accb73..b29c6e683222 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -187,7 +187,9 @@ class PackageManagerService extends IPackageManager.Stub {
static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
"com.android.defcontainer",
"com.android.defcontainer.DefaultContainerService");
-
+
+ static final String mTempContainerPrefix = "smdl2tmp";
+
final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
Process.THREAD_PRIORITY_BACKGROUND);
final PackageHandler mHandler;
@@ -1495,6 +1497,7 @@ class PackageManagerService extends IPackageManager.Stub {
ps.pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
ps.pkg.applicationInfo.sourceDir = ps.codePathString;
ps.pkg.applicationInfo.dataDir = getDataPathForPackage(ps.pkg).getPath();
+ ps.pkg.mSetEnabled = ps.enabled;
}
return generatePackageInfo(ps.pkg, flags);
}
@@ -6279,11 +6282,10 @@ class PackageManagerService extends IPackageManager.Stub {
File dataDir = new File(pkg.applicationInfo.dataDir);
dataDir.delete();
}
+ schedulePackageCleaning(packageName);
}
synchronized (mPackages) {
if (deletedPs != null) {
- schedulePackageCleaning(packageName);
-
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
if (outInfo != null) {
outInfo.removedUid = mSettings.removePackageLP(packageName);
@@ -6875,6 +6877,7 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
pkgSetting.enabled = newState;
+ pkgSetting.pkg.mSetEnabled = newState;
} else {
// We're dealing with a component level state change
switch (newState) {
@@ -8261,6 +8264,7 @@ class PackageManagerService extends IPackageManager.Stub {
private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg) {
p.pkg = pkg;
+ pkg.mSetEnabled = p.enabled;
String codePath = pkg.applicationInfo.sourceDir;
String resourcePath = pkg.applicationInfo.publicSourceDir;
// Update code path if needed
@@ -9486,6 +9490,9 @@ class PackageManagerService extends IPackageManager.Stub {
}
boolean isEnabledLP(ComponentInfo componentInfo, int flags) {
+ if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
+ return true;
+ }
final PackageSetting packageSettings = mPackages.get(componentInfo.packageName);
if (Config.LOGV) {
Log.v(TAG, "isEnabledLock - packageName = " + componentInfo.packageName
@@ -9501,14 +9508,20 @@ class PackageManagerService extends IPackageManager.Stub {
Debug.waitForDebugger();
Log.i(TAG, "We will crash!");
}
+ return false;
}
- return ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0)
- || ((componentInfo.enabled
- && ((packageSettings.enabled == COMPONENT_ENABLED_STATE_ENABLED)
- || (componentInfo.applicationInfo.enabled
- && packageSettings.enabled != COMPONENT_ENABLED_STATE_DISABLED))
- && !packageSettings.disabledComponents.contains(componentInfo.name))
- || packageSettings.enabledComponents.contains(componentInfo.name));
+ if (packageSettings.enabled == COMPONENT_ENABLED_STATE_DISABLED
+ || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
+ && packageSettings.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
+ return false;
+ }
+ if (packageSettings.enabledComponents.contains(componentInfo.name)) {
+ return true;
+ }
+ if (packageSettings.disabledComponents.contains(componentInfo.name)) {
+ return false;
+ }
+ return componentInfo.enabled;
}
}
@@ -9537,48 +9550,28 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- static String getTempContainerId() {
- String prefix = "smdl2tmp";
- int tmpIdx = 1;
- String list[] = PackageHelper.getSecureContainerList();
- if (list != null) {
- int idx = 0;
- int idList[] = new int[MAX_CONTAINERS];
- boolean neverFound = true;
- for (String name : list) {
- // Ignore null entries
- if (name == null) {
- continue;
- }
- int sidx = name.indexOf(prefix);
- if (sidx == -1) {
- // Not a temp file. just ignore
- continue;
- }
- String subStr = name.substring(sidx + prefix.length());
- idList[idx] = -1;
- if (subStr != null) {
- try {
- int cid = Integer.parseInt(subStr);
- idList[idx++] = cid;
- neverFound = false;
- } catch (NumberFormatException e) {
- }
- }
- }
- if (!neverFound) {
- // Sort idList
- Arrays.sort(idList);
- for (int j = 1; j <= idList.length; j++) {
- if (idList[j-1] != j) {
- tmpIdx = j;
- break;
- }
- }
- }
- }
- return prefix + tmpIdx;
- }
+ /* package */ static String getTempContainerId() {
+ int tmpIdx = 1;
+ String list[] = PackageHelper.getSecureContainerList();
+ if (list != null) {
+ for (final String name : list) {
+ // Ignore null and non-temporary container entries
+ if (name == null || !name.startsWith(mTempContainerPrefix)) {
+ continue;
+ }
+
+ String subStr = name.substring(mTempContainerPrefix.length());
+ try {
+ int cid = Integer.parseInt(subStr);
+ if (cid >= tmpIdx) {
+ tmpIdx = cid + 1;
+ }
+ } catch (NumberFormatException e) {
+ }
+ }
+ }
+ return mTempContainerPrefix + tmpIdx;
+ }
/*
* Update media status on PackageManager.
@@ -9793,10 +9786,15 @@ class PackageManagerService extends IPackageManager.Stub {
if (doGc) {
Runtime.getRuntime().gc();
}
- // List stale containers.
+ // List stale containers and destroy stale temporary containers.
if (removeCids != null) {
for (String cid : removeCids) {
- Log.w(TAG, "Container " + cid + " is stale");
+ if (cid.startsWith(mTempContainerPrefix)) {
+ Log.i(TAG, "Destroying stale temporary container " + cid);
+ PackageHelper.destroySdDir(cid);
+ } else {
+ Log.w(TAG, "Container " + cid + " is stale");
+ }
}
}
}
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index e9d5efce6bac..2fb481cbc133 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -1030,36 +1030,71 @@ class PowerManagerService extends IPowerManager.Stub
}
}
- private void setTimeoutLocked(long now, int nextState)
- {
+ private void setTimeoutLocked(long now, int nextState) {
+ setTimeoutLocked(now, -1, nextState);
+ }
+
+ // If they gave a timeoutOverride it is the number of seconds
+ // to screen-off. Figure out where in the countdown cycle we
+ // should jump to.
+ private void setTimeoutLocked(long now, long timeoutOverride, int nextState) {
if (mBootCompleted) {
- mHandler.removeCallbacks(mTimeoutTask);
- mTimeoutTask.nextState = nextState;
- long when = now;
- switch (nextState)
- {
- case SCREEN_BRIGHT:
- when += mKeylightDelay;
- break;
- case SCREEN_DIM:
- if (mDimDelay >= 0) {
- when += mDimDelay;
- break;
- } else {
- Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim");
+ synchronized (mLocks) {
+ mHandler.removeCallbacks(mTimeoutTask);
+ mTimeoutTask.nextState = nextState;
+ long when = 0;
+ if (timeoutOverride <= 0) {
+ switch (nextState)
+ {
+ case SCREEN_BRIGHT:
+ when = now + mKeylightDelay;
+ break;
+ case SCREEN_DIM:
+ if (mDimDelay >= 0) {
+ when = now + mDimDelay;
+ break;
+ } else {
+ Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim");
+ }
+ case SCREEN_OFF:
+ synchronized (mLocks) {
+ when = now + mScreenOffDelay;
+ }
+ break;
+ default:
+ when = now;
+ break;
}
- case SCREEN_OFF:
- synchronized (mLocks) {
- when += mScreenOffDelay;
+ } else {
+ override: {
+ if (timeoutOverride <= mScreenOffDelay) {
+ when = now + timeoutOverride;
+ nextState = SCREEN_OFF;
+ break override;
+ }
+ timeoutOverride -= mScreenOffDelay;
+
+ if (mDimDelay >= 0) {
+ if (timeoutOverride <= mDimDelay) {
+ when = now + timeoutOverride;
+ nextState = SCREEN_DIM;
+ break override;
+ }
+ timeoutOverride -= mDimDelay;
+ }
+
+ when = now + timeoutOverride;
+ nextState = SCREEN_BRIGHT;
}
- break;
- }
- if (mSpew) {
- Slog.d(TAG, "setTimeoutLocked now=" + now + " nextState=" + nextState
- + " when=" + when);
+ }
+ if (mSpew) {
+ Slog.d(TAG, "setTimeoutLocked now=" + now
+ + " timeoutOverride=" + timeoutOverride
+ + " nextState=" + nextState + " when=" + when);
+ }
+ mHandler.postAtTime(mTimeoutTask, when);
+ mNextTimeout = when; // for debugging
}
- mHandler.postAtTime(mTimeoutTask, when);
- mNextTimeout = when; // for debugging
}
}
@@ -1974,18 +2009,33 @@ class PowerManagerService extends IPowerManager.Stub
public void userActivityWithForce(long time, boolean noChangeLights, boolean force) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- userActivity(time, noChangeLights, OTHER_EVENT, force);
+ userActivity(time, -1, noChangeLights, OTHER_EVENT, force);
}
public void userActivity(long time, boolean noChangeLights) {
- userActivity(time, noChangeLights, OTHER_EVENT, false);
+ userActivity(time, -1, noChangeLights, OTHER_EVENT, false);
}
public void userActivity(long time, boolean noChangeLights, int eventType) {
- userActivity(time, noChangeLights, eventType, false);
+ userActivity(time, -1, noChangeLights, eventType, false);
}
public void userActivity(long time, boolean noChangeLights, int eventType, boolean force) {
+ userActivity(time, -1, noChangeLights, eventType, force);
+ }
+
+ /*
+ * Reset the user activity timeout to now + timeout. This overrides whatever else is going
+ * on with user activity. Don't use this function.
+ */
+ public void clearUserActivityTimeout(long now, long timeout) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+ Slog.i(TAG, "clearUserActivity for " + timeout + "ms from now");
+ userActivity(now, timeout, false, OTHER_EVENT, false);
+ }
+
+ private void userActivity(long time, long timeoutOverride, boolean noChangeLights,
+ int eventType, boolean force) {
//mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0)
@@ -2057,7 +2107,7 @@ class PowerManagerService extends IPowerManager.Stub
mWakeLockState = mLocks.reactivateScreenLocksLocked();
setPowerState(mUserState | mWakeLockState, noChangeLights,
WindowManagerPolicy.OFF_BECAUSE_OF_USER);
- setTimeoutLocked(time, SCREEN_BRIGHT);
+ setTimeoutLocked(time, timeoutOverride, SCREEN_BRIGHT);
}
}
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 2e28afb1b203..5615232a74a8 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -103,6 +103,7 @@ import android.view.IWindowManager;
import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InputDevice;
+import android.view.InputEvent;
import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -4307,7 +4308,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getSwitchState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mInputManager.getSwitchState(sw);
+ return mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, sw);
}
public int getSwitchStateForDevice(int devid, int sw) {
@@ -4315,7 +4316,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getSwitchStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mInputManager.getSwitchState(devid, sw);
+ return mInputManager.getSwitchState(devid, InputDevice.SOURCE_ANY, sw);
}
public int getScancodeState(int sw) {
@@ -4323,7 +4324,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mInputManager.getScancodeState(sw);
+ return mInputManager.getScanCodeState(-1, InputDevice.SOURCE_ANY, sw);
}
public int getScancodeStateForDevice(int devid, int sw) {
@@ -4331,7 +4332,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getScancodeStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mInputManager.getScancodeState(devid, sw);
+ return mInputManager.getScanCodeState(devid, InputDevice.SOURCE_ANY, sw);
}
public int getTrackballScancodeState(int sw) {
@@ -4339,7 +4340,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getTrackballScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mInputManager.getTrackballScancodeState(sw);
+ return mInputManager.getScanCodeState(-1, InputDevice.SOURCE_TRACKBALL, sw);
}
public int getDPadScancodeState(int sw) {
@@ -4347,7 +4348,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getDPadScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mInputManager.getDPadScancodeState(sw);
+ return mInputManager.getScanCodeState(-1, InputDevice.SOURCE_DPAD, sw);
}
public int getKeycodeState(int sw) {
@@ -4355,7 +4356,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mInputManager.getKeycodeState(sw);
+ return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, sw);
}
public int getKeycodeStateForDevice(int devid, int sw) {
@@ -4363,7 +4364,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getKeycodeStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mInputManager.getKeycodeState(devid, sw);
+ return mInputManager.getKeyCodeState(devid, InputDevice.SOURCE_ANY, sw);
}
public int getTrackballKeycodeState(int sw) {
@@ -4371,7 +4372,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getTrackballKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mInputManager.getTrackballKeycodeState(sw);
+ return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_TRACKBALL, sw);
}
public int getDPadKeycodeState(int sw) {
@@ -4379,11 +4380,11 @@ public class WindowManagerService extends IWindowManager.Stub
"getDPadKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mInputManager.getDPadKeycodeState(sw);
+ return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD, sw);
}
public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
- return mInputManager.hasKeys(keycodes, keyExists);
+ return mInputManager.hasKeys(-1, InputDevice.SOURCE_ANY, keycodes, keyExists);
}
public void enableScreenAfterBoot() {
@@ -5382,6 +5383,8 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Injects a keystroke event into the UI.
+ * Even when sync is false, this method may block while waiting for current
+ * input events to be dispatched.
*
* @param ev A motion event describing the keystroke action. (Be sure to use
* {@link SystemClock#uptimeMillis()} as the timebase.)
@@ -5414,8 +5417,10 @@ public class WindowManagerService extends IWindowManager.Stub
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
- final int result = mInputManager.injectKeyEvent(newEvent,
- pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
+ final int result = mInputManager.injectInputEvent(newEvent, pid, uid,
+ sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH
+ : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ INJECTION_TIMEOUT_MILLIS);
Binder.restoreCallingIdentity(ident);
return reportInjectionResult(result);
@@ -5423,6 +5428,8 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Inject a pointer (touch) event into the UI.
+ * Even when sync is false, this method may block while waiting for current
+ * input events to be dispatched.
*
* @param ev A motion event describing the pointer (touch) action. (As noted in
* {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
@@ -5440,8 +5447,10 @@ public class WindowManagerService extends IWindowManager.Stub
newEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
}
- final int result = mInputManager.injectMotionEvent(newEvent,
- pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
+ final int result = mInputManager.injectInputEvent(newEvent, pid, uid,
+ sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH
+ : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ INJECTION_TIMEOUT_MILLIS);
Binder.restoreCallingIdentity(ident);
return reportInjectionResult(result);
@@ -5449,6 +5458,8 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Inject a trackball (navigation device) event into the UI.
+ * Even when sync is false, this method may block while waiting for current
+ * input events to be dispatched.
*
* @param ev A motion event describing the trackball action. (As noted in
* {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
@@ -5466,8 +5477,31 @@ public class WindowManagerService extends IWindowManager.Stub
newEvent.setSource(InputDevice.SOURCE_TRACKBALL);
}
- final int result = mInputManager.injectMotionEvent(newEvent,
- pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
+ final int result = mInputManager.injectInputEvent(newEvent, pid, uid,
+ sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH
+ : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ INJECTION_TIMEOUT_MILLIS);
+
+ Binder.restoreCallingIdentity(ident);
+ return reportInjectionResult(result);
+ }
+
+ /**
+ * Inject an input event into the UI without waiting for dispatch to commence.
+ * This variant is useful for fire-and-forget input event injection. It does not
+ * block any longer than it takes to enqueue the input event.
+ *
+ * @param ev An input event. (Be sure to set the input source correctly.)
+ * @return Returns true if event was dispatched, false if it was dropped for any reason
+ */
+ public boolean injectInputEventNoWait(InputEvent ev) {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+
+ final int result = mInputManager.injectInputEvent(ev, pid, uid,
+ InputManager.INPUT_EVENT_INJECTION_SYNC_NONE,
+ INJECTION_TIMEOUT_MILLIS);
Binder.restoreCallingIdentity(ident);
return reportInjectionResult(result);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e333a82ec312..bcbda3e0940f 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6170,7 +6170,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
boolean bufferWasEmpty;
-
+ boolean needsFlush;
final StringBuilder sb = isSystemApp ? mStrictModeBuffer : new StringBuilder(1024);
synchronized (sb) {
bufferWasEmpty = sb.length() == 0;
@@ -6185,18 +6185,32 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
sb.append(crashInfo.stackTrace);
}
sb.append("\n");
+
+ // Only buffer up to ~64k. Various logging bits truncate
+ // things at 128k.
+ needsFlush = (sb.length() > 64 * 1024);
}
- // Non-system apps are isolated with a different tag & policy.
- // They're also not batched. Batching is useful during system
- // boot with strict system-wide logging policies and lots of
- // things firing, but not common with regular apps, which
- // won't ship with StrictMode dropboxing enabled.
- if (!isSystemApp) {
+ // Flush immediately if the buffer's grown too large, or this
+ // is a non-system app. Non-system apps are isolated with a
+ // different tag & policy and not batched.
+ //
+ // Batching is useful during internal testing with
+ // StrictMode settings turned up high. Without batching,
+ // thousands of separate files could be created on boot.
+ if (!isSystemApp || needsFlush) {
new Thread("Error dump: " + dropboxTag) {
@Override
public void run() {
- dbox.addText(dropboxTag, sb.toString());
+ String report;
+ synchronized (sb) {
+ report = sb.toString();
+ sb.delete(0, sb.length());
+ sb.trimToSize();
+ }
+ if (report.length() != 0) {
+ dbox.addText(dropboxTag, report);
+ }
}
}.start();
return;
@@ -6204,8 +6218,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// System app batching:
if (!bufferWasEmpty) {
- // An existing dropbox-writing thread is outstanding and
- // will handle it.
+ // An existing dropbox-writing thread is outstanding, so
+ // we don't need to start it up. The existing thread will
+ // catch the buffer appends we just did.
return;
}
@@ -6752,7 +6767,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
return;
} else if ("service".equals(cmd)) {
- dumpService(fd, pw, args, opti, true);
+ dumpService(fd, pw, args, opti, dumpAll);
return;
} else if ("services".equals(cmd) || "s".equals(cmd)) {
synchronized (this) {
@@ -7058,20 +7073,28 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
componentNameString = args[opti];
opti++;
ComponentName componentName = ComponentName.unflattenFromString(componentNameString);
- r = componentName != null ? mServices.get(componentName) : null;
+ synchronized (this) {
+ r = componentName != null ? mServices.get(componentName) : null;
+ }
newArgs = new String[args.length - opti];
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
}
if (r != null) {
- dumpService(fd, pw, r, newArgs);
+ dumpService(fd, pw, r, newArgs, dumpAll);
} else {
- for (ServiceRecord r1 : mServices.values()) {
- if (componentNameString == null
- || r1.name.flattenToString().contains(componentNameString)) {
- dumpService(fd, pw, r1, newArgs);
+ ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+ synchronized (this) {
+ for (ServiceRecord r1 : mServices.values()) {
+ if (componentNameString == null
+ || r1.name.flattenToString().contains(componentNameString)) {
+ services.add(r1);
+ }
}
}
+ for (int i=0; i<services.size(); i++) {
+ dumpService(fd, pw, services.get(i), newArgs, dumpAll);
+ }
}
}
@@ -7079,8 +7102,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* Invokes IApplicationThread.dumpService() on the thread of the specified service if
* there is a thread associated with the service.
*/
- private void dumpService(FileDescriptor fd, PrintWriter pw, ServiceRecord r, String[] args) {
+ private void dumpService(FileDescriptor fd, PrintWriter pw, ServiceRecord r, String[] args,
+ boolean dumpAll) {
pw.println(" Service " + r.name.flattenToString());
+ if (dumpAll) {
+ synchronized (this) {
+ pw.print(" * "); pw.println(r);
+ r.dump(pw, " ");
+ }
+ pw.println("");
+ }
if (r.app != null && r.app.thread != null) {
try {
// flush anything that is already in the PrintWriter since the thread is going
@@ -7088,6 +7119,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
pw.flush();
r.app.thread.dumpService(fd, r, args);
pw.print("\n");
+ pw.flush();
} catch (RemoteException e) {
pw.println("got a RemoteException while dumping the service");
}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index b29f8756fa0e..eb0a8a9aea36 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -26,13 +26,13 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.hardware.Usb;
import android.net.ConnectivityManager;
import android.net.InterfaceConfiguration;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.NetworkInfo;
import android.net.NetworkUtils;
-import android.os.BatteryManager;
import android.os.Binder;
import android.os.Environment;
import android.os.HandlerThread;
@@ -135,7 +135,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mStateReceiver = new StateReceiver();
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Usb.ACTION_USB_STATE);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
mContext.registerReceiver(mStateReceiver, filter);
@@ -424,10 +424,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private class StateReceiver extends BroadcastReceiver {
public void onReceive(Context content, Intent intent) {
String action = intent.getAction();
- if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- mUsbConnected = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
- == BatteryManager.BATTERY_PLUGGED_USB);
- Tethering.this.updateUsbStatus();
+ if (action.equals(Usb.ACTION_USB_STATE)) {
+ mUsbConnected = intent.getExtras().getBoolean(Usb.USB_CONNECTED);
+ updateUsbStatus();
} else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
mUsbMassStorageOff = false;
updateUsbStatus();
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 4b4c784001c4..f8c9a6132848 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -892,7 +892,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) {
if (singleShot && hasCapability(GPS_CAPABILITY_MSA)) {
mPositionMode = GPS_POSITION_MODE_MS_ASSISTED;
- } else if (hasCapability(GPS_CAPABILITY_MSA)) {
+ } else if (hasCapability(GPS_CAPABILITY_MSB)) {
mPositionMode = GPS_POSITION_MODE_MS_BASED;
}
}
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 0992b33f877d..0982b32719a4 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -180,6 +180,14 @@ static struct {
jfieldID token;
} gInputApplicationClassInfo;
+static struct {
+ jclass clazz;
+} gKeyEventClassInfo;
+
+static struct {
+ jclass clazz;
+} gMotionEventClassInfo;
+
// ----------------------------------------------------------------------------
static inline nsecs_t now() {
@@ -219,11 +227,10 @@ public:
int32_t* width, int32_t* height, int32_t* orientation);
virtual void virtualKeyDownFeedback();
virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
- bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags);
- virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
- bool rolled);
- virtual int32_t interceptTouch(nsecs_t when);
- virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue);
+ bool down, int32_t keyCode, int32_t scanCode, uint32_t& policyFlags);
+ virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
+ uint32_t& policyFlags);
+ virtual int32_t interceptGeneric(nsecs_t when, uint32_t& policyFlags);
virtual bool filterTouchEvents();
virtual bool filterJumpyTouchEvents();
virtual void getVirtualKeyDefinitions(const String8& deviceName,
@@ -343,6 +350,7 @@ private:
InputApplication* mFocusedApplication;
InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication
+ void dumpDeviceInfo(String8& dump);
void dumpDispatchStateLd(String8& dump);
void logDispatchStateLd();
@@ -409,12 +417,16 @@ NativeInputManager::~NativeInputManager() {
String8 NativeInputManager::dump() {
String8 dump;
- dump.append("Native Input Dispatcher State:\n");
-
{ // acquire lock
AutoMutex _l(mDisplayLock);
+ dump.append("Native Input Dispatcher State:\n");
dumpDispatchStateLd(dump);
+ dump.append("\n");
} // release lock
+
+ dump.append("Input Devices:\n");
+ dumpDeviceInfo(dump);
+
return dump;
}
@@ -566,9 +578,15 @@ bool NativeInputManager::getDisplayInfo(int32_t displayId,
AutoMutex _l(mDisplayLock);
if (mDisplayWidth > 0) {
- *width = mDisplayWidth;
- *height = mDisplayHeight;
- *orientation = mDisplayOrientation;
+ if (width) {
+ *width = mDisplayWidth;
+ }
+ if (height) {
+ *height = mDisplayHeight;
+ }
+ if (orientation) {
+ *orientation = mDisplayOrientation;
+ }
result = true;
}
}
@@ -595,7 +613,7 @@ void NativeInputManager::virtualKeyDownFeedback() {
}
int32_t NativeInputManager::interceptKey(nsecs_t when,
- int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) {
+ int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) {
#if DEBUG_INPUT_READER_POLICY
LOGD("interceptKey - when=%lld, deviceId=%d, down=%d, keyCode=%d, scanCode=%d, "
"policyFlags=0x%x",
@@ -626,12 +644,12 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
if (! isScreenOn) {
// Key presses and releases wake the device.
- actions |= InputReaderPolicyInterface::ACTION_WOKE_HERE;
+ policyFlags |= POLICY_FLAG_WOKE_HERE;
}
if (! isScreenBright) {
// Key presses and releases brighten the screen if dimmed.
- actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+ policyFlags |= POLICY_FLAG_BRIGHT_HERE;
}
if (wmActions & WM_ACTION_GO_TO_SLEEP) {
@@ -658,42 +676,20 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
return actions;
}
-int32_t NativeInputManager::interceptTouch(nsecs_t when) {
-#if DEBUG_INPUT_READER_POLICY
- LOGD("interceptTouch - when=%lld", when);
-#endif
-
- int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
- if (isScreenOn()) {
- // Only dispatch touch events when the device is awake.
- // Do not wake the device.
- actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
-
- if (! isScreenBright()) {
- // Brighten the screen if dimmed.
- actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
- }
- }
-
- return actions;
-}
-
-int32_t NativeInputManager::interceptTrackball(nsecs_t when,
- bool buttonChanged, bool buttonDown, bool rolled) {
+int32_t NativeInputManager::interceptGeneric(nsecs_t when, uint32_t& policyFlags) {
#if DEBUG_INPUT_READER_POLICY
- LOGD("interceptTrackball - when=%lld, buttonChanged=%d, buttonDown=%d, rolled=%d",
- when, buttonChanged, buttonDown, rolled);
+ LOGD("interceptGeneric - when=%lld, policyFlags=0x%x", when, policyFlags);
#endif
int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
if (isScreenOn()) {
- // Only dispatch trackball events when the device is awake.
+ // Only dispatch events when the device is awake.
// Do not wake the device.
actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
if (! isScreenBright()) {
// Brighten the screen if dimmed.
- actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+ policyFlags |= POLICY_FLAG_BRIGHT_HERE;
}
}
@@ -701,10 +697,10 @@ int32_t NativeInputManager::interceptTrackball(nsecs_t when,
}
int32_t NativeInputManager::interceptSwitch(nsecs_t when, int32_t switchCode,
- int32_t switchValue) {
+ int32_t switchValue, uint32_t& policyFlags) {
#if DEBUG_INPUT_READER_POLICY
- LOGD("interceptSwitch - when=%lld, switchCode=%d, switchValue=%d",
- when, switchCode, switchValue);
+ LOGD("interceptSwitch - when=%lld, switchCode=%d, switchValue=%d, policyFlags=0x%x",
+ when, switchCode, switchValue, policyFlags);
#endif
JNIEnv* env = jniEnv();
@@ -1718,6 +1714,56 @@ void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType)
android_server_PowerManagerService_userActivity(eventTime, eventType);
}
+static void dumpMotionRange(String8& dump,
+ const char* name, const InputDeviceInfo::MotionRange* range) {
+ if (range) {
+ dump.appendFormat(" %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n",
+ name, range->min, range->max, range->flat, range->fuzz);
+ }
+}
+
+#define DUMP_MOTION_RANGE(range) \
+ dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range));
+
+void NativeInputManager::dumpDeviceInfo(String8& dump) {
+ Vector<int32_t> deviceIds;
+ mInputManager->getInputDeviceIds(deviceIds);
+
+ InputDeviceInfo deviceInfo;
+ for (size_t i = 0; i < deviceIds.size(); i++) {
+ int32_t deviceId = deviceIds[i];
+
+ status_t result = mInputManager->getInputDeviceInfo(deviceId, & deviceInfo);
+ if (result == NAME_NOT_FOUND) {
+ continue;
+ } else if (result != OK) {
+ dump.appendFormat(" ** Unexpected error %d getting information about input devices.\n",
+ result);
+ continue;
+ }
+
+ dump.appendFormat(" Device %d: '%s'\n",
+ deviceInfo.getId(), deviceInfo.getName().string());
+ dump.appendFormat(" sources = 0x%08x\n",
+ deviceInfo.getSources());
+ dump.appendFormat(" keyboardType = %d\n",
+ deviceInfo.getKeyboardType());
+
+ dump.append(" motion ranges:\n");
+ DUMP_MOTION_RANGE(X);
+ DUMP_MOTION_RANGE(Y);
+ DUMP_MOTION_RANGE(PRESSURE);
+ DUMP_MOTION_RANGE(SIZE);
+ DUMP_MOTION_RANGE(TOUCH_MAJOR);
+ DUMP_MOTION_RANGE(TOUCH_MINOR);
+ DUMP_MOTION_RANGE(TOOL_MAJOR);
+ DUMP_MOTION_RANGE(TOOL_MINOR);
+ DUMP_MOTION_RANGE(ORIENTATION);
+ }
+}
+
+#undef DUMP_MOTION_RANGE
+
void NativeInputManager::logDispatchStateLd() {
String8 dump;
dumpDispatchStateLd(dump);
@@ -1899,36 +1945,37 @@ static void android_server_InputManager_nativeSetDisplayOrientation(JNIEnv* env,
}
static jint android_server_InputManager_nativeGetScanCodeState(JNIEnv* env, jclass clazz,
- jint deviceId, jint deviceClasses, jint scanCode) {
+ jint deviceId, jint sourceMask, jint scanCode) {
if (checkInputManagerUnitialized(env)) {
return AKEY_STATE_UNKNOWN;
}
return gNativeInputManager->getInputManager()->getScanCodeState(
- deviceId, deviceClasses, scanCode);
+ deviceId, uint32_t(sourceMask), scanCode);
}
static jint android_server_InputManager_nativeGetKeyCodeState(JNIEnv* env, jclass clazz,
- jint deviceId, jint deviceClasses, jint keyCode) {
+ jint deviceId, jint sourceMask, jint keyCode) {
if (checkInputManagerUnitialized(env)) {
return AKEY_STATE_UNKNOWN;
}
return gNativeInputManager->getInputManager()->getKeyCodeState(
- deviceId, deviceClasses, keyCode);
+ deviceId, uint32_t(sourceMask), keyCode);
}
static jint android_server_InputManager_nativeGetSwitchState(JNIEnv* env, jclass clazz,
- jint deviceId, jint deviceClasses, jint sw) {
+ jint deviceId, jint sourceMask, jint sw) {
if (checkInputManagerUnitialized(env)) {
return AKEY_STATE_UNKNOWN;
}
- return gNativeInputManager->getInputManager()->getSwitchState(deviceId, deviceClasses, sw);
+ return gNativeInputManager->getInputManager()->getSwitchState(
+ deviceId, uint32_t(sourceMask), sw);
}
static jboolean android_server_InputManager_nativeHasKeys(JNIEnv* env, jclass clazz,
- jintArray keyCodes, jbooleanArray outFlags) {
+ jint deviceId, jint sourceMask, jintArray keyCodes, jbooleanArray outFlags) {
if (checkInputManagerUnitialized(env)) {
return JNI_FALSE;
}
@@ -1937,8 +1984,9 @@ static jboolean android_server_InputManager_nativeHasKeys(JNIEnv* env, jclass cl
uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL);
jsize numCodes = env->GetArrayLength(keyCodes);
jboolean result;
- if (numCodes == env->GetArrayLength(outFlags)) {
- result = gNativeInputManager->getInputManager()->hasKeys(numCodes, codes, flags);
+ if (numCodes == env->GetArrayLength(keyCodes)) {
+ result = gNativeInputManager->getInputManager()->hasKeys(
+ deviceId, uint32_t(sourceMask), numCodes, codes, flags);
} else {
result = JNI_FALSE;
}
@@ -2011,32 +2059,29 @@ static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env
}
}
-static jint android_server_InputManager_nativeInjectKeyEvent(JNIEnv* env, jclass clazz,
- jobject keyEventObj, jint injectorPid, jint injectorUid,
- jboolean sync, jint timeoutMillis) {
+static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jclass clazz,
+ jobject inputEventObj, jint injectorPid, jint injectorUid,
+ jint syncMode, jint timeoutMillis) {
if (checkInputManagerUnitialized(env)) {
return INPUT_EVENT_INJECTION_FAILED;
}
- KeyEvent keyEvent;
- android_view_KeyEvent_toNative(env, keyEventObj, & keyEvent);
+ if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) {
+ KeyEvent keyEvent;
+ android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent);
- return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent,
- injectorPid, injectorUid, sync, timeoutMillis);
-}
+ return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent,
+ injectorPid, injectorUid, syncMode, timeoutMillis);
+ } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
+ MotionEvent motionEvent;
+ android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent);
-static jint android_server_InputManager_nativeInjectMotionEvent(JNIEnv* env, jclass clazz,
- jobject motionEventObj, jint injectorPid, jint injectorUid,
- jboolean sync, jint timeoutMillis) {
- if (checkInputManagerUnitialized(env)) {
+ return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent,
+ injectorPid, injectorUid, syncMode, timeoutMillis);
+ } else {
+ jniThrowRuntimeException(env, "Invalid input event type.");
return INPUT_EVENT_INJECTION_FAILED;
}
-
- MotionEvent motionEvent;
- android_view_MotionEvent_toNative(env, motionEventObj, & motionEvent);
-
- return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent,
- injectorPid, injectorUid, sync, timeoutMillis);
}
static void android_server_InputManager_nativeSetInputWindows(JNIEnv* env, jclass clazz,
@@ -2102,16 +2147,14 @@ static JNINativeMethod gInputManagerMethods[] = {
(void*) android_server_InputManager_nativeGetKeyCodeState },
{ "nativeGetSwitchState", "(III)I",
(void*) android_server_InputManager_nativeGetSwitchState },
- { "nativeHasKeys", "([I[Z)Z",
+ { "nativeHasKeys", "(II[I[Z)Z",
(void*) android_server_InputManager_nativeHasKeys },
{ "nativeRegisterInputChannel", "(Landroid/view/InputChannel;)V",
(void*) android_server_InputManager_nativeRegisterInputChannel },
{ "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
(void*) android_server_InputManager_nativeUnregisterInputChannel },
- { "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIZI)I",
- (void*) android_server_InputManager_nativeInjectKeyEvent },
- { "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIZI)I",
- (void*) android_server_InputManager_nativeInjectMotionEvent },
+ { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIII)I",
+ (void*) android_server_InputManager_nativeInjectInputEvent },
{ "nativeSetInputWindows", "([Lcom/android/server/InputWindow;)V",
(void*) android_server_InputManager_nativeSetInputWindows },
{ "nativeSetFocusedApplication", "(Lcom/android/server/InputApplication;)V",
@@ -2278,6 +2321,14 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_FIELD_ID(gInputApplicationClassInfo.token, gInputApplicationClassInfo.clazz,
"token", "Ljava/lang/Object;");
+ // KeyEvent
+
+ FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
+
+ // MotionEVent
+
+ FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent");
+
return 0;
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 758da4e87653..629d993ca007 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -294,19 +294,9 @@ sp<GraphicBuffer> Layer::requestBuffer(int index,
* This is called from the client's Surface::dequeue(). This can happen
* at any time, especially while we're in the middle of using the
* buffer 'index' as our front buffer.
- *
- * Make sure the buffer we're resizing is not the front buffer and has been
- * dequeued. Once this condition is asserted, we are guaranteed that this
- * buffer cannot become the front buffer under our feet, since we're called
- * from Surface::dequeue()
*/
- status_t err = lcblk->assertReallocate(index);
- LOGE_IF(err, "assertReallocate(%d) failed (%s)", index, strerror(-err));
- if (err != NO_ERROR) {
- // the surface may have died
- return buffer;
- }
+ status_t err = NO_ERROR;
uint32_t w, h, f;
{ // scope for the lock
Mutex::Autolock _l(mLock);
@@ -319,23 +309,17 @@ sp<GraphicBuffer> Layer::requestBuffer(int index,
w = reqWidth ? reqWidth : mWidth;
h = reqHeight ? reqHeight : mHeight;
f = reqFormat ? reqFormat : mFormat;
- buffer = mBufferManager.detachBuffer(index);
if (fixedSizeChanged || formatChanged) {
lcblk->reallocateAllExcept(index);
}
}
+ // here we have to reallocate a new buffer because the buffer could be
+ // used as the front buffer, or by a client in our process
+ // (eg: status bar), and we can't release the handle under its feet.
const uint32_t effectiveUsage = getEffectiveUsage(usage);
- if (buffer!=0 && buffer->getStrongCount() == 1) {
- err = buffer->reallocate(w, h, f, effectiveUsage);
- } else {
- // here we have to reallocate a new buffer because we could have a
- // client in our process with a reference to it (eg: status bar),
- // and we can't release the handle under its feet.
- buffer.clear();
- buffer = new GraphicBuffer(w, h, f, effectiveUsage);
- err = buffer->initCheck();
- }
+ buffer = new GraphicBuffer(w, h, f, effectiveUsage);
+ err = buffer->initCheck();
if (err || buffer->handle == 0) {
LOGE_IF(err || buffer->handle == 0,
diff --git a/services/surfaceflinger/tests/surface/Android.mk b/services/surfaceflinger/tests/surface/Android.mk
new file mode 100644
index 000000000000..ce0e8072f744
--- /dev/null
+++ b/services/surfaceflinger/tests/surface/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ surface.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libbinder \
+ libui \
+ libsurfaceflinger_client
+
+LOCAL_MODULE:= test-surface
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/surface/surface.cpp b/services/surfaceflinger/tests/surface/surface.cpp
new file mode 100644
index 000000000000..b4de4b459c38
--- /dev/null
+++ b/services/surfaceflinger/tests/surface/surface.cpp
@@ -0,0 +1,54 @@
+#include <cutils/memory.h>
+
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+#include <surfaceflinger/Surface.h>
+#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
+#include <ui/Overlay.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+ // set up the thread-pool
+ sp<ProcessState> proc(ProcessState::self());
+ ProcessState::self()->startThreadPool();
+
+ // create a client to surfaceflinger
+ sp<SurfaceComposerClient> client = new SurfaceComposerClient();
+
+ // create pushbuffer surface
+ sp<SurfaceControl> surfaceControl = client->createSurface(
+ getpid(), 0, 160, 240, PIXEL_FORMAT_RGB_565);
+ client->openTransaction();
+ surfaceControl->setLayer(100000);
+ client->closeTransaction();
+
+ // pretend it went cross-process
+ Parcel parcel;
+ SurfaceControl::writeSurfaceToParcel(surfaceControl, &parcel);
+ parcel.setDataPosition(0);
+ sp<Surface> surface = Surface::readFromParcel(parcel);
+ ANativeWindow* window = surface.get();
+
+ printf("window=%p\n", window);
+
+ int err = native_window_set_buffer_count(window, 8);
+ android_native_buffer_t* buffer;
+
+ for (int i=0 ; i<8 ; i++) {
+ window->dequeueBuffer(window, &buffer);
+ printf("buffer %d: %p\n", i, buffer);
+ }
+
+ printf("test complete. CTRL+C to finish.\n");
+
+ IPCThreadState::self()->joinThreadPool();
+ return 0;
+}
diff --git a/tests/LargeAssetTest/Android.mk b/tests/LargeAssetTest/Android.mk
new file mode 100644
index 000000000000..cb7f01be8b40
--- /dev/null
+++ b/tests/LargeAssetTest/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := LargeAssetTest
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/tests/LargeAssetTest/AndroidManifest.xml b/tests/LargeAssetTest/AndroidManifest.xml
new file mode 100644
index 000000000000..c86118e492e5
--- /dev/null
+++ b/tests/LargeAssetTest/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.largeassettest">
+
+ <application>
+ <activity android:name="LargeAssetTest" android:label="Large Asset Test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/tests/LargeAssetTest/assets/million-ints b/tests/LargeAssetTest/assets/million-ints
new file mode 100644
index 000000000000..0607de1b7667
--- /dev/null
+++ b/tests/LargeAssetTest/assets/million-ints
Binary files differ
diff --git a/tests/LargeAssetTest/res/layout/lat.xml b/tests/LargeAssetTest/res/layout/lat.xml
new file mode 100644
index 000000000000..eda7b82e91fa
--- /dev/null
+++ b/tests/LargeAssetTest/res/layout/lat.xml
@@ -0,0 +1,52 @@
+<?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:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ >
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="25dp"
+ android:textColor="#ffffffff"
+ android:text="@string/prompt"
+ />
+
+ <TextView android:id="@+id/result"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginTop="25dp"
+ android:textSize="24sp"
+ android:textColor="#ffffffff"
+ />
+
+ <Button android:id="@+id/validate"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button_text" />
+
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ />
+</LinearLayout>
+
+
diff --git a/tests/LargeAssetTest/res/values/strings.xml b/tests/LargeAssetTest/res/values/strings.xml
new file mode 100644
index 000000000000..54478fad4f6c
--- /dev/null
+++ b/tests/LargeAssetTest/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?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="prompt">Click the button below to read and validate the large binary asset.</string>
+ <string name="button_text">Validate the asset</string>
+
+</resources>
+
diff --git a/tests/LargeAssetTest/src/com/android/largeassettest/LargeAssetTest.java b/tests/LargeAssetTest/src/com/android/largeassettest/LargeAssetTest.java
new file mode 100644
index 000000000000..e3a9cf40b066
--- /dev/null
+++ b/tests/LargeAssetTest/src/com/android/largeassettest/LargeAssetTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+package com.android.largeassettest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * Skeleton to test large-asset handling. The asset in question is one million
+ * four-byte integers, in ascending numeric order.
+ */
+public class LargeAssetTest extends Activity {
+ Button mValidateButton;
+ TextView mResultText;
+ Validator mValidateThread;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.lat);
+
+ mResultText = (TextView) findViewById(R.id.result);
+ mValidateButton = (Button) findViewById(R.id.validate);
+
+ mValidateButton.setOnClickListener(mClickListener);
+ }
+
+ View.OnClickListener mClickListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ mValidateButton.setEnabled(false);
+ mValidateThread = new Validator();
+ mValidateThread.execute(LargeAssetTest.this.getAssets());
+ }
+ };
+
+ /**
+ * Validation happens in a separate thread
+ */
+ class Validator extends AsyncTask<AssetManager, Integer, Boolean> {
+ static final String TAG = "Validator";
+
+ @Override
+ protected Boolean doInBackground(AssetManager... params) {
+ AssetManager am = params[0];
+ try {
+ InputStream is = am.open("million-ints", AssetManager.ACCESS_STREAMING);
+ byte[] buf = new byte[4];
+
+ for (int i = 0; i < 1000000; i++) {
+ int num = is.read(buf, 0, 4);
+ if (num != 4) {
+ Log.e(TAG, "Wanted 4 bytes but read " + num);
+ return false;
+ }
+ // the byte array is stored in the asset in little-endian order
+ int value = (buf[3] << 24) + ((buf[2] & 0xFF) << 16)
+ + ((buf[1] & 0xFF) << 8) + (buf[0] & 0xFF);
+ if (value != i) {
+ Log.e(TAG, "Mismatch: index " + i + " : value " + value);
+ return false;
+ }
+ }
+
+ is.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Couldn't open asset", e);
+ return false;
+ }
+ Log.i(TAG, "Finished, reporting valid");
+ return true;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ CharSequence text = (result) ? "Valid!" : "NOT VALID";
+ mResultText.setText(text);
+ mValidateButton.setEnabled(true);
+ }
+ }
+}