summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk2
-rw-r--r--api/current.xml915
-rw-r--r--core/java/android/animation/AnimatableListenerAdapter.java56
-rwxr-xr-xcore/java/android/animation/Animator.java169
-rw-r--r--core/java/android/animation/Keyframe.java19
-rw-r--r--core/java/android/animation/KeyframeSet.java2
-rw-r--r--core/java/android/animation/PropertyAnimator.java143
-rw-r--r--core/java/android/animation/Sequencer.java23
-rw-r--r--core/java/android/app/Activity.java6
-rw-r--r--core/java/android/app/ContextImpl.java16
-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.java12
-rw-r--r--core/java/android/database/sqlite/SQLiteOpenHelper.java39
-rw-r--r--core/java/android/hardware/Usb.java18
-rw-r--r--core/java/android/net/DownloadManager.java2
-rw-r--r--core/java/android/os/StrictMode.java2
-rw-r--r--core/java/android/preference/PreferenceFragment.java309
-rw-r--r--core/java/android/view/ActionMode.java24
-rwxr-xr-xcore/java/android/view/InputDevice.java32
-rw-r--r--core/java/android/view/MenuInflater.java4
-rw-r--r--core/java/android/view/MotionEvent.java34
-rw-r--r--core/java/android/view/View.java11
-rw-r--r--core/java/android/view/Window.java12
-rw-r--r--core/java/android/view/animation/AnimationUtils.java124
-rw-r--r--core/java/android/webkit/WebSettings.java8
-rw-r--r--core/java/android/widget/BaseExpandableListAdapter.java4
-rw-r--r--core/java/android/widget/HeterogeneousExpandableList.java27
-rw-r--r--core/java/android/widget/Scroller.java6
-rw-r--r--core/java/android/widget/TextView.java769
-rw-r--r--core/java/com/android/internal/app/ActionBarImpl.java32
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java2
-rw-r--r--core/java/com/android/internal/view/StandaloneActionMode.java133
-rw-r--r--core/java/com/android/internal/widget/ActionBarContextView.java4
-rw-r--r--core/jni/android_util_Binder.cpp14
-rw-r--r--core/res/AndroidManifest.xml2
-rw-r--r--core/res/res/layout-xlarge/screen_action_bar.xml (renamed from core/res/res/layout/screen_xlarge_action_bar.xml)6
-rw-r--r--core/res/res/layout/screen.xml7
-rw-r--r--core/res/res/layout/screen_custom_title.xml7
-rw-r--r--core/res/res/layout/screen_progress.xml7
-rw-r--r--core/res/res/layout/screen_title.xml6
-rw-r--r--core/res/res/layout/screen_title_icons.xml6
-rw-r--r--core/res/res/values-xlarge/strings.xml25
-rwxr-xr-xcore/res/res/values/attrs.xml79
-rw-r--r--core/res/res/values/ids.xml1
-rw-r--r--core/res/res/values/public.xml16
-rw-r--r--core/res/res/values/strings.xml28
-rw-r--r--core/res/res/values/themes.xml4
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java76
-rw-r--r--core/tests/hosttests/Android.mk2
-rw-r--r--docs/html/guide/practices/ui_guidelines/activity_task_design.jd4
-rw-r--r--include/binder/Parcel.h12
-rw-r--r--include/media/AudioEffect.h18
-rw-r--r--include/media/EffectApi.h6
-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/InputManager.h49
-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/hwui/Android.mk1
-rw-r--r--libs/hwui/OpenGLRenderer.cpp57
-rw-r--r--libs/hwui/OpenGLRenderer.h6
-rw-r--r--libs/hwui/Program.cpp11
-rw-r--r--libs/hwui/Program.h28
-rw-r--r--libs/hwui/ProgramCache.cpp332
-rw-r--r--libs/hwui/ProgramCache.h155
-rw-r--r--libs/hwui/Vertex.h8
-rw-r--r--libs/hwui/shaders/drawColor.vert11
-rw-r--r--libs/hwui/shaders/drawLinearGradient.vert2
-rw-r--r--libs/rs/java/Fountain/src/com/android/fountain/fountain.rs5
-rw-r--r--libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs8
-rw-r--r--libs/rs/rsContext.cpp10
-rw-r--r--libs/rs/rsScript.h3
-rw-r--r--libs/rs/rsScriptC_Lib.cpp178
-rw-r--r--libs/rs/scriptc/rs_math.rsh53
-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/InputManager.cpp33
-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--location/java/android/location/Country.aidl19
-rwxr-xr-xlocation/java/android/location/Country.java161
-rw-r--r--location/java/android/location/CountryDetector.java152
-rw-r--r--location/java/android/location/CountryListener.java30
-rw-r--r--location/java/android/location/ICountryDetector.aidl44
-rw-r--r--location/java/android/location/ICountryListener.aidl26
-rw-r--r--location/tests/locationtests/src/android/location/CountryTester.java33
-rw-r--r--media/jni/audioeffect/android_media_AudioEffect.cpp7
-rw-r--r--media/libeffects/factory/EffectsFactory.c7
-rw-r--r--media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp6
-rw-r--r--media/libeffects/testlibs/EffectEqualizer.cpp4
-rw-r--r--media/libeffects/testlibs/EffectReverb.c4
-rw-r--r--media/libeffects/testlibs/EffectReverb.h19
-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/MPEG4Writer.cpp96
-rw-r--r--media/tests/CameraBrowser/AndroidManifest.xml8
-rw-r--r--media/tests/CameraBrowser/src/com/android/camerabrowser/UsbReceiver.java47
-rw-r--r--native/android/input.cpp78
-rw-r--r--native/include/android/input.h95
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindow.java77
-rw-r--r--services/audioflinger/AudioFlinger.cpp105
-rw-r--r--services/audioflinger/AudioFlinger.h18
-rw-r--r--services/java/com/android/server/CountryDetectorService.java204
-rw-r--r--services/java/com/android/server/InputManager.java121
-rw-r--r--services/java/com/android/server/PackageManagerService.java77
-rw-r--r--services/java/com/android/server/PowerManagerService.java4
-rw-r--r--services/java/com/android/server/SystemServer.java11
-rw-r--r--services/java/com/android/server/UsbObserver.java36
-rw-r--r--services/java/com/android/server/WindowManagerService.java22
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java35
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java11
-rwxr-xr-xservices/java/com/android/server/location/ComprehensiveCountryDetector.java359
-rw-r--r--services/java/com/android/server/location/CountryDetectorBase.java72
-rwxr-xr-xservices/java/com/android/server/location/LocationBasedCountryDetector.java235
-rw-r--r--services/jni/Android.mk3
-rw-r--r--services/jni/com_android_server_InputManager.cpp144
-rw-r--r--services/jni/com_android_server_UsbObserver.cpp174
-rw-r--r--services/jni/onload.cpp2
-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--services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java109
-rw-r--r--services/tests/servicestests/src/com/android/server/location/ComprehensiveCountryDetectorTest.java299
-rwxr-xr-xservices/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java324
-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
-rw-r--r--wifi/java/android/net/wifi/WifiStateTracker.java16
147 files changed, 10222 insertions, 3556 deletions
diff --git a/Android.mk b/Android.mk
index 3afbfd7a0460..696b1176cf8d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -163,6 +163,8 @@ LOCAL_SRC_FILES += \
core/java/com/android/internal/view/IInputMethodManager.aidl \
core/java/com/android/internal/view/IInputMethodSession.aidl \
core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
+ location/java/android/location/ICountryDetector.aidl \
+ location/java/android/location/ICountryListener.aidl \
location/java/android/location/IGeocodeProvider.aidl \
location/java/android/location/IGpsStatusListener.aidl \
location/java/android/location/IGpsStatusProvider.aidl \
diff --git a/api/current.xml b/api/current.xml
index b2ad7d24ba71..280537dfdb4e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -2088,55 +2088,55 @@
visibility="public"
>
</field>
-<field name="actionBarCloseContextDrawable"
+<field name="actionButtonPadding"
type="int"
transient="false"
volatile="false"
- value="16843550"
+ value="16843547"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="actionBarContextBackground"
+<field name="actionButtonStyle"
type="int"
transient="false"
volatile="false"
- value="16843549"
+ value="16843545"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="actionButtonPadding"
+<field name="actionDropDownStyle"
type="int"
transient="false"
volatile="false"
- value="16843547"
+ value="16843544"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="actionButtonStyle"
+<field name="actionModeBackground"
type="int"
transient="false"
volatile="false"
- value="16843545"
+ value="16843549"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="actionDropDownStyle"
+<field name="actionModeCloseDrawable"
type="int"
transient="false"
volatile="false"
- value="16843544"
+ value="16843550"
static="true"
final="true"
deprecated="not deprecated"
@@ -7181,6 +7181,17 @@
visibility="public"
>
</field>
+<field name="ordering"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843556"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="orderingFromXml"
type="int"
transient="false"
@@ -7742,6 +7753,17 @@
visibility="public"
>
</field>
+<field name="propertyName"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843555"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="protectionLevel"
type="int"
transient="false"
@@ -10195,6 +10217,39 @@
visibility="public"
>
</field>
+<field name="valueFrom"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843552"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="valueTo"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843553"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="valueType"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843554"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="variablePadding"
type="int"
transient="false"
@@ -10525,6 +10580,17 @@
visibility="public"
>
</field>
+<field name="windowActionModeOverlay"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843551"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="windowAnimationStyle"
type="int"
transient="false"
@@ -14801,6 +14867,17 @@
visibility="public"
>
</field>
+<field name="selectTextMode"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16908354"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="selectedIcon"
type="int"
transient="false"
@@ -15920,6 +15997,17 @@
visibility="public"
>
</field>
+<field name="selectTextMode"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17039408"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="unknownName"
type="int"
transient="false"
@@ -21849,6 +21937,19 @@
<parameter name="args" type="android.os.Bundle">
</parameter>
</method>
+<method name="startActionMode"
+ return="android.view.ActionMode"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.view.ActionMode.Callback">
+</parameter>
+</method>
<method name="startActivityForResult"
return="void"
abstract="false"
@@ -21913,19 +22014,6 @@
<parameter name="requestCode" type="int">
</parameter>
</method>
-<method name="startContextMode"
- return="android.view.ActionMode"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="callback" type="android.view.ActionMode.Callback">
-</parameter>
-</method>
<method name="startIntentSenderForResult"
return="void"
abstract="false"
@@ -22143,6 +22231,17 @@
visibility="protected"
>
</field>
+<field name="POP_BACK_STACK_INCLUSIVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="RESULT_CANCELED"
type="int"
transient="false"
@@ -41168,6 +41267,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"
@@ -96740,6 +96850,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"
@@ -131986,6 +132657,112 @@
</parameter>
</constructor>
</class>
+<class name="PreferenceFragment"
+ extends="android.app.Fragment"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="PreferenceFragment"
+ type="android.preference.PreferenceFragment"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="addPreferencesFromIntent"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="addPreferencesFromResource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="preferencesResId" type="int">
+</parameter>
+</method>
+<method name="findPreference"
+ return="android.preference.Preference"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="getPreferenceManager"
+ return="android.preference.PreferenceManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPreferenceScreen"
+ return="android.preference.PreferenceScreen"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onPreferenceTreeClick"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="preferenceScreen" type="android.preference.PreferenceScreen">
+</parameter>
+<parameter name="preference" type="android.preference.Preference">
+</parameter>
+</method>
+<method name="setPreferenceScreen"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="preferenceScreen" type="android.preference.PreferenceScreen">
+</parameter>
+</method>
+</class>
<class name="PreferenceGroup"
extends="android.preference.Preference"
abstract="true"
@@ -176349,6 +177126,19 @@
<parameter name="subtitle" type="java.lang.CharSequence">
</parameter>
</method>
+<method name="setSubtitle"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="resId" type="int">
+</parameter>
+</method>
<method name="setTitle"
return="void"
abstract="true"
@@ -176362,6 +177152,19 @@
<parameter name="title" type="java.lang.CharSequence">
</parameter>
</method>
+<method name="setTitle"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="resId" type="int">
+</parameter>
+</method>
</class>
<interface name="ActionMode.Callback"
abstract="true"
@@ -177684,6 +178487,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"
@@ -177732,6 +178546,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"
@@ -177831,6 +178678,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"
@@ -193748,6 +194606,17 @@
type="int"
transient="false"
volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FEATURE_ACTION_MODE_OVERLAY"
+ type="int"
+ transient="false"
+ volatile="false"
value="9"
static="true"
final="true"
diff --git a/core/java/android/animation/AnimatableListenerAdapter.java b/core/java/android/animation/AnimatableListenerAdapter.java
new file mode 100644
index 000000000000..25a842bcf12b
--- /dev/null
+++ b/core/java/android/animation/AnimatableListenerAdapter.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.animation;
+
+import android.animation.Animatable.AnimatableListener;
+
+/**
+ * This adapter class provides empty implementations of the methods from {@link AnimatableListener}.
+ * Any custom listener that cares only about a subset of the methods of this listener can
+ * simply subclass this adapter class instead of implementing the interface directly.
+ */
+public abstract class AnimatableListenerAdapter implements AnimatableListener {
+
+ /**
+ * @{inheritdoc}
+ */
+ @Override
+ public void onAnimationCancel(Animatable animation) {
+ }
+
+ /**
+ * @{inheritdoc}
+ */
+ @Override
+ public void onAnimationEnd(Animatable animation) {
+ }
+
+ /**
+ * @{inheritdoc}
+ */
+ @Override
+ public void onAnimationRepeat(Animatable animation) {
+ }
+
+ /**
+ * @{inheritdoc}
+ */
+ @Override
+ public void onAnimationStart(Animatable animation) {
+ }
+
+}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 24c3c88afdea..d8c6fff4c51f 100755
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -16,8 +16,11 @@
package android.animation;
+import android.content.Context;
+import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Message;
+import android.util.AttributeSet;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -64,6 +67,15 @@ public class Animator extends Animatable {
private static final int ENDED = 3; // end() called - need to end it
/**
+ * Enum values used in XML attributes to indicate the value for mValueType
+ */
+ private static final int VALUE_TYPE_FLOAT = 0;
+ private static final int VALUE_TYPE_INT = 1;
+ private static final int VALUE_TYPE_DOUBLE = 2;
+ private static final int VALUE_TYPE_COLOR = 3;
+ private static final int VALUE_TYPE_CUSTOM = 4;
+
+ /**
* Internal variables
*/
@@ -219,6 +231,67 @@ public class Animator extends Animatable {
*/
public static final int INFINITE = -1;
+ /**
+ * Creates a new animation whose parameters come from the specified context and
+ * attributes set.
+ *
+ * @param context the application environment
+ * @param attrs the set of attributes holding the animation parameters
+ */
+ public Animator(Context context, AttributeSet attrs) {
+ TypedArray a =
+ context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animator);
+
+ mDuration = (long) a.getInt(com.android.internal.R.styleable.Animator_duration, 0);
+
+ mStartDelay = (long) a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0);
+
+ final int resID =
+ a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
+ if (resID > 0) {
+ setInterpolator(AnimationUtils.loadInterpolator(context, resID));
+ }
+ int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType,
+ VALUE_TYPE_FLOAT);
+
+ switch (valueType) {
+ case VALUE_TYPE_FLOAT:
+ mValueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
+ mValueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
+ mValueType = float.class;
+ break;
+ case VALUE_TYPE_INT:
+ mValueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0);
+ mValueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0);
+ mValueType = int.class;
+ break;
+ case VALUE_TYPE_DOUBLE:
+ mValueFrom = (double)
+ a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
+ mValueTo = (double)
+ a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
+ mValueType = double.class;
+ break;
+ case VALUE_TYPE_COLOR:
+ mValueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0);
+ mValueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0);
+ mEvaluator = new RGBEvaluator();
+ mValueType = int.class;
+ break;
+ case VALUE_TYPE_CUSTOM:
+ // TODO: How to get an 'Object' value?
+ mValueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
+ mValueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
+ mValueType = Object.class;
+ break;
+ }
+
+ mRepeatCount = a.getInt(com.android.internal.R.styleable.Animator_repeatCount, mRepeatCount);
+ mRepeatMode = a.getInt(com.android.internal.R.styleable.Animator_repeatMode, RESTART);
+
+ a.recycle();
+ }
+
private Animator(long duration, Object valueFrom, Object valueTo, Class valueType) {
mDuration = duration;
mValueFrom = valueFrom;
@@ -241,25 +314,6 @@ public class Animator extends Animatable {
}
/**
- * This function is called immediately before processing the first animation
- * frame of an animation. If there is a nonzero <code>startDelay</code>, the
- * function is called after that delay ends.
- * It takes care of the final initialization steps for the
- * animation.
- *
- * <p>Overrides of this method should call the superclass method to ensure
- * that internal mechanisms for the animation are set up correctly.</p>
- */
- void initAnimation() {
- if (mEvaluator == null) {
- mEvaluator = (mValueType == int.class) ? sIntEvaluator :
- (mValueType == double.class) ? sDoubleEvaluator : sFloatEvaluator;
- }
- mPlayingBackwards = false;
- mCurrentIteration = 0;
- }
-
- /**
* A constructor that takes <code>float</code> values.
*
* @param duration The length of the animation, in milliseconds.
@@ -305,6 +359,61 @@ public class Animator extends Animatable {
}
/**
+ * Internal constructor that takes a single <code>float</code> value.
+ * This constructor is called by PropertyAnimator.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @param valueFrom The initial value of the property when the animation begins.
+ * @param valueTo The value to which the property will animate.
+ */
+ Animator(long duration, float valueTo) {
+ this(duration, null, valueTo, float.class);
+ }
+
+ /**
+ * Internal constructor that takes a single <code>int</code> value.
+ * This constructor is called by PropertyAnimator.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @param valueFrom The initial value of the property when the animation begins.
+ * @param valueTo The value to which the property will animate.
+ */
+ Animator(long duration, int valueTo) {
+ this(duration, null, valueTo, int.class);
+ }
+
+ /**
+ * Internal constructor that takes a single <code>double</code> value.
+ * This constructor is called by PropertyAnimator.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @param valueFrom The initial value of the property when the animation begins.
+ * @param valueTo The value to which the property will animate.
+ */
+ Animator(long duration, double valueTo) {
+ this(duration, null, valueTo, double.class);
+ }
+
+ /**
+ * This function is called immediately before processing the first animation
+ * frame of an animation. If there is a nonzero <code>startDelay</code>, the
+ * function is called after that delay ends.
+ * It takes care of the final initialization steps for the
+ * animation.
+ *
+ * <p>Overrides of this method should call the superclass method to ensure
+ * that internal mechanisms for the animation are set up correctly.</p>
+ */
+ void initAnimation() {
+ if (mEvaluator == null) {
+ mEvaluator = (mValueType == int.class) ? sIntEvaluator :
+ (mValueType == double.class) ? sDoubleEvaluator : sFloatEvaluator;
+ }
+ mPlayingBackwards = false;
+ mCurrentIteration = 0;
+ }
+
+ /**
* This custom, static handler handles the timing pulse that is shared by
* all active animations. This approach ensures that the setting of animation
* values will happen on the UI thread and that all animations will share
@@ -454,6 +563,9 @@ public class Animator extends Animatable {
* @return Object The starting value for the animation.
*/
public Object getValueFrom() {
+ if (mKeyframeSet != null) {
+ return mKeyframeSet.mKeyframes.get(0).getValue();
+ }
return mValueFrom;
}
@@ -461,7 +573,12 @@ public class Animator extends Animatable {
* Sets the value that this animation will start from.
*/
public void setValueFrom(Object valueFrom) {
- mValueFrom = valueFrom;
+ if (mKeyframeSet != null) {
+ Keyframe kf = mKeyframeSet.mKeyframes.get(0);
+ kf.setValue(valueFrom);
+ } else {
+ mValueFrom = valueFrom;
+ }
}
/**
@@ -470,6 +587,10 @@ public class Animator extends Animatable {
* @return Object The ending value for the animation.
*/
public Object getValueTo() {
+ if (mKeyframeSet != null) {
+ int numKeyframes = mKeyframeSet.mKeyframes.size();
+ return mKeyframeSet.mKeyframes.get(numKeyframes - 1).getValue();
+ }
return mValueTo;
}
@@ -479,7 +600,13 @@ public class Animator extends Animatable {
* @return Object The ending value for the animation.
*/
public void setValueTo(Object valueTo) {
- mValueTo = valueTo;
+ if (mKeyframeSet != null) {
+ int numKeyframes = mKeyframeSet.mKeyframes.size();
+ Keyframe kf = mKeyframeSet.mKeyframes.get(numKeyframes - 1);
+ kf.setValue(valueTo);
+ } else {
+ mValueTo = valueTo;
+ }
}
/**
diff --git a/core/java/android/animation/Keyframe.java b/core/java/android/animation/Keyframe.java
index b98994a37440..0f3963549ede 100644
--- a/core/java/android/animation/Keyframe.java
+++ b/core/java/android/animation/Keyframe.java
@@ -142,6 +142,15 @@ public class Keyframe {
}
/**
+ * Sets the value for this Keyframe.
+ *
+ * @param The value for this Keyframe.
+ */
+ public void setValue(Object value) {
+ mValue = value;
+ }
+
+ /**
* Gets the time for this keyframe, as a fraction of the overall animation duration.
*
* @return The time associated with this keyframe, as a fraction of the overall animation
@@ -152,6 +161,16 @@ public class Keyframe {
}
/**
+ * Sets the time for this keyframe, as a fraction of the overall animation duration.
+ *
+ * @param The time associated with this keyframe, as a fraction of the overall animation
+ * duration. This should be a value between 0 and 1.
+ */
+ public void setFraction(float fraction) {
+ mFraction = fraction;
+ }
+
+ /**
* Gets the optional interpolator for this Keyframe. A value of <code>null</code> indicates
* that there is no interpolation, which is the same as linear interpolation.
*
diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java
index 59e9708e3deb..d144b9ca594b 100644
--- a/core/java/android/animation/KeyframeSet.java
+++ b/core/java/android/animation/KeyframeSet.java
@@ -29,7 +29,7 @@ class KeyframeSet {
private int mNumKeyframes;
- private ArrayList<Keyframe> mKeyframes;
+ ArrayList<Keyframe> mKeyframes;
public KeyframeSet(Keyframe... keyframes) {
mKeyframes = new ArrayList<Keyframe>();
diff --git a/core/java/android/animation/PropertyAnimator.java b/core/java/android/animation/PropertyAnimator.java
index 937dd586441f..4d7ab8471398 100644
--- a/core/java/android/animation/PropertyAnimator.java
+++ b/core/java/android/animation/PropertyAnimator.java
@@ -16,6 +16,9 @@
package android.animation;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
@@ -152,6 +155,24 @@ public final class PropertyAnimator extends Animator {
}
/**
+ * Creates a new animation whose parameters come from the specified context and
+ * attributes set.
+ *
+ * @param context the application environment
+ * @param attrs the set of attributes holding the animation parameters
+ */
+ public PropertyAnimator(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.PropertyAnimator);
+
+ mPropertyName = a.getString(com.android.internal.R.styleable.PropertyAnimator_propertyName);
+
+
+ a.recycle();
+ }
+ /**
* Determine the setter or getter function using the JavaBeans convention of setFoo or
* getFoo for a property named 'foo'. This function figures out what the name of the
* function should be and uses reflection to find the Method with that name on the
@@ -160,20 +181,23 @@ public final class PropertyAnimator extends Animator {
* @param prefix "set" or "get", depending on whether we need a setter or getter.
* @return Method the method associated with mPropertyName.
*/
- private Method getPropertyFunction(String prefix) {
+ private Method getPropertyFunction(String prefix, Class valueType) {
// TODO: faster implementation...
Method returnVal = null;
String firstLetter = mPropertyName.substring(0, 1);
String theRest = mPropertyName.substring(1);
firstLetter = firstLetter.toUpperCase();
String setterName = prefix + firstLetter + theRest;
- Class args[] = new Class[1];
- args[0] = mValueType;
+ Class args[] = null;
+ if (valueType != null) {
+ args = new Class[1];
+ args[0] = valueType;
+ }
try {
returnVal = mTarget.getClass().getMethod(setterName, args);
} catch (NoSuchMethodException e) {
Log.e("PropertyAnimator",
- "Couldn't find setter for property " + mPropertyName + ": " + e);
+ "Couldn't find setter/getter for property " + mPropertyName + ": " + e);
}
return returnVal;
}
@@ -203,6 +227,31 @@ public final class PropertyAnimator extends Animator {
}
/**
+ * A constructor that takes a single <code>float</code> value, which is the value that the
+ * target object will animate to. When this constructor
+ * is called, the system expects to find a setter for <code>propertyName</code> on
+ * the target object that takes a value of the same type as the <code>Object</code>s. The
+ * system also expects to find a similar getter function with which to derive the starting
+ * value for the animation.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @param target The object whose property is to be animated. This object should
+ * have a public function on it called <code>setName()</code>, where <code>name</code> is
+ * the name of the property passed in as the <code>propertyName</code> parameter.
+ * @param propertyName The name of the property on the <code>target</code> object
+ * that will be animated. Given this name, the constructor will search for a
+ * setter on the target object with the name <code>setPropertyName</code>. For example,
+ * if the constructor is called with <code>propertyName = "foo"</code>, then the
+ * target object should have a setter function with the name <code>setFoo()</code>.
+ * @param valueTo The value to which the property will animate.
+ */
+ public PropertyAnimator(int duration, Object target, String propertyName, float valueTo) {
+ super(duration, valueTo);
+ mTarget = target;
+ mPropertyName = propertyName;
+ }
+
+ /**
* A constructor that takes <code>int</code> values. When this constructor
* is called, the system expects to find a setter for <code>propertyName</code> on
* the target object that takes a <code>int</code> value.
@@ -227,6 +276,31 @@ public final class PropertyAnimator extends Animator {
}
/**
+ * A constructor that takes a single <code>int</code> value, which is the value that the
+ * target object will animate to. When this constructor
+ * is called, the system expects to find a setter for <code>propertyName</code> on
+ * the target object that takes a value of the same type as the <code>Object</code>s. The
+ * system also expects to find a similar getter function with which to derive the starting
+ * value for the animation.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @param target The object whose property is to be animated. This object should
+ * have a public function on it called <code>setName()</code>, where <code>name</code> is
+ * the name of the property passed in as the <code>propertyName</code> parameter.
+ * @param propertyName The name of the property on the <code>target</code> object
+ * that will be animated. Given this name, the constructor will search for a
+ * setter on the target object with the name <code>setPropertyName</code>. For example,
+ * if the constructor is called with <code>propertyName = "foo"</code>, then the
+ * target object should have a setter function with the name <code>setFoo()</code>.
+ * @param valueTo The value to which the property will animate.
+ */
+ public PropertyAnimator(int duration, Object target, String propertyName, int valueTo) {
+ super(duration, valueTo);
+ mTarget = target;
+ mPropertyName = propertyName;
+ }
+
+ /**
* A constructor that takes <code>double</code> values. When this constructor
* is called, the system expects to find a setter for <code>propertyName</code> on
* the target object that takes a <code>double</code> value.
@@ -251,6 +325,31 @@ public final class PropertyAnimator extends Animator {
}
/**
+ * A constructor that takes a single <code>double</code> value, which is the value that the
+ * target object will animate to. When this constructor
+ * is called, the system expects to find a setter for <code>propertyName</code> on
+ * the target object that takes a value of the same type as the <code>Object</code>s. The
+ * system also expects to find a similar getter function with which to derive the starting
+ * value for the animation.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @param target The object whose property is to be animated. This object should
+ * have a public function on it called <code>setName()</code>, where <code>name</code> is
+ * the name of the property passed in as the <code>propertyName</code> parameter.
+ * @param propertyName The name of the property on the <code>target</code> object
+ * that will be animated. Given this name, the constructor will search for a
+ * setter on the target object with the name <code>setPropertyName</code>. For example,
+ * if the constructor is called with <code>propertyName = "foo"</code>, then the
+ * target object should have a setter function with the name <code>setFoo()</code>.
+ * @param valueTo The value to which the property will animate.
+ */
+ public PropertyAnimator(int duration, Object target, String propertyName, double valueTo) {
+ super(duration, valueTo);
+ mTarget = target;
+ mPropertyName = propertyName;
+ }
+
+ /**
* A constructor that takes <code>Object</code> values. When this constructor
* is called, the system expects to find a setter for <code>propertyName</code> on
* the target object that takes a value of the same type as the <code>Object</code>s.
@@ -275,6 +374,29 @@ public final class PropertyAnimator extends Animator {
}
/**
+ * A constructor that takes a single <code>Object</code> value, which is the value that the
+ * target object will animate to. When this constructor
+ * is called, the system expects to find a setter for <code>propertyName</code> on
+ * the target object that takes a value of the same type as the <code>Object</code>s. The
+ * system also expects to find a similar getter function with which to derive the starting
+ * value for the animation.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @param target The object whose property is to be animated. This object should
+ * have a public function on it called <code>setName()</code>, where <code>name</code> is
+ * the name of the property passed in as the <code>propertyName</code> parameter.
+ * @param propertyName The name of the property on the <code>target</code> object
+ * that will be animated. Given this name, the constructor will search for a
+ * setter on the target object with the name <code>setPropertyName</code>. For example,
+ * if the constructor is called with <code>propertyName = "foo"</code>, then the
+ * target object should have a setter function with the name <code>setFoo()</code>.
+ * @param valueTo The value to which the property will animate.
+ */
+ public PropertyAnimator(int duration, Object target, String propertyName, Object valueTo) {
+ this(duration, target, propertyName, null, valueTo);
+ }
+
+ /**
* A constructor that takes <code>Keyframe</code>s. When this constructor
* is called, the system expects to find a setter for <code>propertyName</code> on
* the target object that takes a value of the same type as that returned from
@@ -329,7 +451,7 @@ public final class PropertyAnimator extends Animator {
return;
}
}
- mSetter = getPropertyFunction("set");
+ mSetter = getPropertyFunction("set", mValueType);
if (propertyMap == null) {
propertyMap = new HashMap<String, Method>();
sSetterPropertyMap.put(mTarget, propertyMap);
@@ -352,7 +474,7 @@ public final class PropertyAnimator extends Animator {
return;
}
}
- mGetter = getPropertyFunction("get");
+ mGetter = getPropertyFunction("get", null);
if (propertyMap == null) {
propertyMap = new HashMap<String, Method>();
sGetterPropertyMap.put(mTarget, propertyMap);
@@ -390,6 +512,15 @@ public final class PropertyAnimator extends Animator {
}
/**
+ * Sets the target object whose property will be animated by this animation
+ *
+ * @param target The object being animated
+ */
+ public void setTarget(Object target) {
+ mTarget = target;
+ }
+
+ /**
* This method is called with the elapsed fraction of the animation during every
* animation frame. This function turns the elapsed fraction into an interpolated fraction
* and then into an animated value (from the evaluator. The function is called mostly during
diff --git a/core/java/android/animation/Sequencer.java b/core/java/android/animation/Sequencer.java
index 74d81e9b9469..c283e0fda1ef 100644
--- a/core/java/android/animation/Sequencer.java
+++ b/core/java/android/animation/Sequencer.java
@@ -16,6 +16,11 @@
package android.animation;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.animation.AnimationUtils;
+
import java.util.ArrayList;
import java.util.HashMap;
@@ -119,6 +124,22 @@ public final class Sequencer extends Animatable {
}
/**
+ * Returns the current list of child Animatable objects controlled by this
+ * Sequencer. This is a copy of the internal list; modifications to the returned list
+ * will not affect the Sequencer, although changes to the underlying Animatable objects
+ * will affect those objects being managed by the Sequencer.
+ *
+ * @return ArrayList<Animatable> The list of child animations of this Sequencer.
+ */
+ public ArrayList<Animatable> getChildAnimations() {
+ ArrayList<Animatable> childList = new ArrayList<Animatable>();
+ for (Node node : mNodes) {
+ childList.add(node.animation);
+ }
+ return childList;
+ }
+
+ /**
* This method creates a <code>Builder</code> object, which is used to
* set up playing constraints. This initial <code>play()</code> method
* tells the <code>Builder</code> the animation that is the dependency for
@@ -692,7 +713,7 @@ public final class Sequencer extends Animatable {
*/
public void after(long delay) {
// setup dummy Animator just to run the clock
- Animator anim = new Animator(delay, 0, 1);
+ Animator anim = new Animator(delay, 0f, 1f);
Node node = new Node(anim);
mNodes.add(node);
Dependency dependency = new Dependency(node, Dependency.AFTER);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 61fd5f3690a3..9beaec03008c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2059,7 +2059,7 @@ public class Activity extends ContextThemeWrapper
* removed. Otherwise, all entries up to but not including that entry
* will be removed
*/
- static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
+ public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
/**
* Pop the top state off the back stack. Returns true if there was one
@@ -4080,13 +4080,13 @@ public class Activity extends ContextThemeWrapper
*
* @see ActionMode
*/
- public ActionMode startContextMode(ActionMode.Callback callback) {
+ public ActionMode startActionMode(ActionMode.Callback callback) {
return mWindow.getDecorView().startActionMode(callback);
}
public ActionMode onStartActionMode(ActionMode.Callback callback) {
if (mActionBar != null) {
- return mActionBar.startContextMode(callback);
+ return mActionBar.startActionMode(callback);
}
return null;
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 8eded3d9117a..10ebd607ec4d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -62,6 +62,8 @@ import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.hardware.SensorManager;
+import android.location.CountryDetector;
+import android.location.ICountryDetector;
import android.location.ILocationManager;
import android.location.LocationManager;
import android.media.AudioManager;
@@ -168,6 +170,7 @@ class ContextImpl extends Context {
private static ThrottleManager sThrottleManager;
private static WifiManager sWifiManager;
private static LocationManager sLocationManager;
+ private static CountryDetector sCountryDetector;
private static final HashMap<File, SharedPreferencesImpl> sSharedPrefs =
new HashMap<File, SharedPreferencesImpl>();
@@ -954,6 +957,8 @@ class ContextImpl extends Context {
return AccessibilityManager.getInstance(this);
} else if (LOCATION_SERVICE.equals(name)) {
return getLocationManager();
+ } else if (COUNTRY_DETECTOR.equals(name)) {
+ return getCountryDetector();
} else if (SEARCH_SERVICE.equals(name)) {
return getSearchManager();
} else if (SENSOR_SERVICE.equals(name)) {
@@ -1120,6 +1125,17 @@ class ContextImpl extends Context {
return sLocationManager;
}
+ private CountryDetector getCountryDetector() {
+ synchronized (sSync) {
+ if (sCountryDetector == null) {
+ IBinder b = ServiceManager.getService(COUNTRY_DETECTOR);
+ ICountryDetector service = ICountryDetector.Stub.asInterface(b);
+ sCountryDetector = new CountryDetector(service);
+ }
+ }
+ return sCountryDetector;
+ }
+
private SearchManager getSearchManager() {
synchronized (mSync) {
if (mSearchManager == null) {
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 3d64984a8ffc..879103ee1168 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1380,7 +1380,16 @@ public abstract class Context {
* @see android.location.LocationManager
*/
public static final String LOCATION_SERVICE = "location";
-
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.location.CountryDetector} for detecting the country that
+ * the user is in.
+ *
+ * @hide
+ */
+ public static final String COUNTRY_DETECTOR = "country_detector";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.app.SearchManager} for handling searches.
@@ -1566,7 +1575,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/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 0f2e872e54e1..d8cce216db75 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -24,10 +24,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>
*
@@ -51,8 +57,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
@@ -96,13 +103,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
*/
@@ -183,6 +197,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.
@@ -262,9 +281,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/Usb.java b/core/java/android/hardware/Usb.java
index 57271d4b72f8..1028f9f56bf6 100644
--- a/core/java/android/hardware/Usb.java
+++ b/core/java/android/hardware/Usb.java
@@ -55,6 +55,24 @@ public class Usb {
public static final String ACTION_USB_STATE =
"android.hardware.action.USB_STATE";
+ /**
+ * Broadcast Action: A broadcast for USB camera attached event.
+ *
+ * This intent is sent when a USB device supporting PTP is attached to the host USB bus.
+ * The intent's data contains a Uri for the device in the MTP provider.
+ */
+ public static final String ACTION_USB_CAMERA_ATTACHED =
+ "android.hardware.action.USB_CAMERA_ATTACHED";
+
+ /**
+ * Broadcast Action: A broadcast for USB camera detached event.
+ *
+ * This intent is sent when a USB device supporting PTP is detached from the host USB bus.
+ * The intent's data contains a Uri for the device in the MTP provider.
+ */
+ public static final String ACTION_USB_CAMERA_DETACHED =
+ "android.hardware.action.USB_CAMERA_DETACHED";
+
/**
* Boolean extra indicating whether USB is connected or disconnected.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast.
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/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/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
new file mode 100644
index 000000000000..f85bc9e5b7dd
--- /dev/null
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.preference;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+
+/**
+ * Shows a hierarchy of {@link Preference} objects as
+ * lists. These preferences will
+ * automatically save to {@link SharedPreferences} as the user interacts with
+ * them. To retrieve an instance of {@link SharedPreferences} that the
+ * preference hierarchy in this fragment will use, call
+ * {@link PreferenceManager#getDefaultSharedPreferences(android.content.Context)}
+ * with a context in the same package as this fragment.
+ * <p>
+ * The preference hierarchy can be formed in multiple ways:
+ * <li> From an XML file specifying the hierarchy
+ * <li> From different {@link Activity Activities} that each specify its own
+ * preferences in an XML file via {@link Activity} meta-data
+ * <li> From an object hierarchy rooted with {@link PreferenceScreen}
+ * <p>
+ * To inflate from XML, use the {@link #addPreferencesFromResource(int)}. The
+ * root element should be a {@link PreferenceScreen}. Subsequent elements can point
+ * to actual {@link Preference} subclasses. As mentioned above, subsequent
+ * {@link PreferenceScreen} in the hierarchy will result in the screen break.
+ * <p>
+ * To specify an {@link Intent} to query {@link Activity Activities} that each
+ * have preferences, use {@link #addPreferencesFromIntent}. Each
+ * {@link Activity} can specify meta-data in the manifest (via the key
+ * {@link PreferenceManager#METADATA_KEY_PREFERENCES}) that points to an XML
+ * resource. These XML resources will be inflated into a single preference
+ * hierarchy and shown by this fragment.
+ * <p>
+ * To specify an object hierarchy rooted with {@link PreferenceScreen}, use
+ * {@link #setPreferenceScreen(PreferenceScreen)}.
+ * <p>
+ * As a convenience, this fragment implements a click listener for any
+ * preference in the current hierarchy, see
+ * {@link #onPreferenceTreeClick(PreferenceScreen, Preference)}.
+ * <p>
+ * See {@link PreferenceActivity} for more details.
+ *
+ * <a name="SampleCode"></a>
+ * <h3>Sample Code</h3>
+ *
+ * <p>The following sample code shows the use if a PreferenceFragment to
+ * embed preferences in a larger activity and switch between them. The content
+ * layout of the activity is:</p>
+ *
+ * {@sample development/samples/ApiDemos/res/layout/fragment_preferences.xml layout}
+ *
+ * <p>The code using this layout consists of an activity and three fragments.
+ * One of the fragments is a list of categories the user can select; the other
+ * two are the different preference options for the categories.</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentPreferences.java
+ * activity}
+ *
+ * @see Preference
+ * @see PreferenceScreen
+ */
+public abstract class PreferenceFragment extends Fragment implements
+ PreferenceManager.OnPreferenceTreeClickListener {
+
+ private static final String PREFERENCES_TAG = "android:preferences";
+
+ private PreferenceManager mPreferenceManager;
+ private ListView mList;
+ private boolean mHavePrefs;
+ private boolean mInitDone;
+
+ /**
+ * The starting request code given out to preference framework.
+ */
+ private static final int FIRST_REQUEST_CODE = 100;
+
+ private static final int MSG_BIND_PREFERENCES = 0;
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+
+ case MSG_BIND_PREFERENCES:
+ bindPreferences();
+ break;
+ }
+ }
+ };
+
+ final private Runnable mRequestFocus = new Runnable() {
+ public void run() {
+ mList.focusableViewAvailable(mList);
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mPreferenceManager = new PreferenceManager(getActivity(), FIRST_REQUEST_CODE);
+ mPreferenceManager.setOnPreferenceTreeClickListener(this);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(com.android.internal.R.layout.preference_list_content,
+ container, false);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
+
+ if (mHavePrefs) {
+ bindPreferences();
+ }
+
+ mInitDone = true;
+
+ if (savedInstanceState != null) {
+ Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG);
+ if (container != null) {
+ final PreferenceScreen preferenceScreen = getPreferenceScreen();
+ if (preferenceScreen != null) {
+ preferenceScreen.restoreHierarchyState(container);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ mPreferenceManager.dispatchActivityStop();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mPreferenceManager.dispatchActivityDestroy();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ final PreferenceScreen preferenceScreen = getPreferenceScreen();
+ if (preferenceScreen != null) {
+ Bundle container = new Bundle();
+ preferenceScreen.saveHierarchyState(container);
+ outState.putBundle(PREFERENCES_TAG, container);
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ mPreferenceManager.dispatchActivityResult(requestCode, resultCode, data);
+ }
+
+ /**
+ * Returns the {@link PreferenceManager} used by this fragment.
+ * @return The {@link PreferenceManager}.
+ */
+ public PreferenceManager getPreferenceManager() {
+ return mPreferenceManager;
+ }
+
+ /**
+ * Sets the root of the preference hierarchy that this fragment is showing.
+ *
+ * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
+ */
+ public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
+ if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
+ mHavePrefs = true;
+ if (mInitDone) {
+ postBindPreferences();
+ }
+ }
+ }
+
+ /**
+ * Gets the root of the preference hierarchy that this fragment is showing.
+ *
+ * @return The {@link PreferenceScreen} that is the root of the preference
+ * hierarchy.
+ */
+ public PreferenceScreen getPreferenceScreen() {
+ return mPreferenceManager.getPreferenceScreen();
+ }
+
+ /**
+ * Adds preferences from activities that match the given {@link Intent}.
+ *
+ * @param intent The {@link Intent} to query activities.
+ */
+ public void addPreferencesFromIntent(Intent intent) {
+ requirePreferenceManager();
+
+ setPreferenceScreen(mPreferenceManager.inflateFromIntent(intent, getPreferenceScreen()));
+ }
+
+ /**
+ * Inflates the given XML resource and adds the preference hierarchy to the current
+ * preference hierarchy.
+ *
+ * @param preferencesResId The XML resource ID to inflate.
+ */
+ public void addPreferencesFromResource(int preferencesResId) {
+ requirePreferenceManager();
+
+ setPreferenceScreen(mPreferenceManager.inflateFromResource(getActivity(),
+ preferencesResId, getPreferenceScreen()));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+ return false;
+ }
+
+ /**
+ * Finds a {@link Preference} based on its key.
+ *
+ * @param key The key of the preference to retrieve.
+ * @return The {@link Preference} with the key, or null.
+ * @see PreferenceGroup#findPreference(CharSequence)
+ */
+ public Preference findPreference(CharSequence key) {
+ if (mPreferenceManager == null) {
+ return null;
+ }
+ return mPreferenceManager.findPreference(key);
+ }
+
+ private void requirePreferenceManager() {
+ if (mPreferenceManager == null) {
+ throw new RuntimeException("This should be called after super.onCreate.");
+ }
+ }
+
+ private void postBindPreferences() {
+ if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
+ mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
+ }
+
+ private void bindPreferences() {
+ final PreferenceScreen preferenceScreen = getPreferenceScreen();
+ if (preferenceScreen != null) {
+ preferenceScreen.bind(getListView());
+ }
+ }
+
+ private ListView getListView() {
+ ensureList();
+ return mList;
+ }
+
+ private void ensureList() {
+ if (mList != null) {
+ return;
+ }
+ View root = getView();
+ if (root == null) {
+ throw new IllegalStateException("Content view not yet created");
+ }
+ View rawListView = root.findViewById(android.R.id.list);
+ if (!(rawListView instanceof ListView)) {
+ throw new RuntimeException(
+ "Content has view with id attribute 'android.R.id.list' "
+ + "that is not a ListView class");
+ }
+ mList = (ListView)rawListView;
+ if (mList == null) {
+ throw new RuntimeException(
+ "Your content must have a ListView whose id attribute is " +
+ "'android.R.id.list'");
+ }
+ mHandler.post(mRequestFocus);
+ }
+}
diff --git a/core/java/android/view/ActionMode.java b/core/java/android/view/ActionMode.java
index d4f4b933e0ba..4a7d7ad43a9a 100644
--- a/core/java/android/view/ActionMode.java
+++ b/core/java/android/view/ActionMode.java
@@ -29,21 +29,45 @@ public abstract class ActionMode {
*
* @param title Title string to set
*
+ * @see #setTitle(int)
* @see #setCustomView(View)
*/
public abstract void setTitle(CharSequence title);
/**
+ * Set the title of the action mode. This method will have no visible effect if
+ * a custom view has been set.
+ *
+ * @param resId Resource ID of a string to set as the title
+ *
+ * @see #setTitle(CharSequence)
+ * @see #setCustomView(View)
+ */
+ public abstract void setTitle(int resId);
+
+ /**
* Set the subtitle of the action mode. This method will have no visible effect if
* a custom view has been set.
*
* @param subtitle Subtitle string to set
*
+ * @see #setSubtitle(int)
* @see #setCustomView(View)
*/
public abstract void setSubtitle(CharSequence subtitle);
/**
+ * Set the subtitle of the action mode. This method will have no visible effect if
+ * a custom view has been set.
+ *
+ * @param resId Resource ID of a string to set as the subtitle
+ *
+ * @see #setSubtitle(CharSequence)
+ * @see #setCustomView(View)
+ */
+ public abstract void setSubtitle(int resId);
+
+ /**
* Set a custom view for this action mode. The custom view will take the place of
* the title and subtitle. Useful for things like search boxes.
*
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/MenuInflater.java b/core/java/android/view/MenuInflater.java
index 4a966b5d77c0..a959e0d45e6e 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -350,6 +350,10 @@ public class MenuInflater {
.setShowAsAction(itemShowAsAction);
if (itemListenerMethodName != null) {
+ if (mContext.isRestricted()) {
+ throw new IllegalStateException("The android:onClick attribute cannot "
+ + "be used within a restricted context");
+ }
item.setOnMenuItemClickListener(
new InflatedOnMenuItemClickListener(mContext, itemListenerMethodName));
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 67bce996e2f4..c7be7515ce5d 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.
@@ -1614,28 +1614,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/view/View.java b/core/java/android/view/View.java
index 363aced9b7e9..745839d4dc32 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2510,11 +2510,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
- * Call this view's OnLongClickListener, if it is defined. Invokes the context menu
- * if the OnLongClickListener did not consume the event.
+ * Call this view's OnLongClickListener, if it is defined. Invokes the context menu if the
+ * OnLongClickListener did not consume the event.
*
- * @return True there was an assigned OnLongClickListener that was called, false
- * otherwise is returned.
+ * @return True if one of the above receivers consumed the event, false otherwise.
*/
public boolean performLongClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
@@ -4359,6 +4358,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* Show the context menu for this view. It is not safe to hold on to the
* menu after returning from this method.
*
+ * You should normally not overload this method. Overload
+ * {@link #onCreateContextMenu(ContextMenu)} or define an
+ * {@link OnCreateContextMenuListener} to add items to the context menu.
+ *
* @param menu The context menu to populate
*/
public void createContextMenu(ContextMenu menu) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index ed02456ce807..0d4e84b0e329 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -56,18 +56,18 @@ public abstract class Window {
public static final int FEATURE_CONTEXT_MENU = 6;
/** Flag for custom title. You cannot combine this feature with other title features. */
public static final int FEATURE_CUSTOM_TITLE = 7;
- /** Flag for asking for an OpenGL enabled window.
- All 2D graphics will be handled by OpenGL ES.
- @hide
- */
- public static final int FEATURE_OPENGL = 8;
/**
* Flag for enabling the Action Bar.
* This is enabled by default for some devices. The Action Bar
* replaces the title bar and provides an alternate location
* for an on-screen menu button on some devices.
*/
- public static final int FEATURE_ACTION_BAR = 9;
+ public static final int FEATURE_ACTION_BAR = 8;
+ /**
+ * Flag for specifying the behavior of action modes when an Action Bar is not present.
+ * If overlay is enabled, the action mode UI will be allowed to cover existing window content.
+ */
+ public static final int FEATURE_ACTION_MODE_OVERLAY = 9;
/** Flag for setting the progress bar's visibility to VISIBLE */
public static final int PROGRESS_VISIBILITY_ON = -1;
/** Flag for setting the progress bar's visibility to GONE */
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 30883827db8e..bfc30a5eae78 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -16,6 +16,12 @@
package android.view.animation;
+import android.animation.Animatable;
+import android.animation.Animator;
+import android.animation.PropertyAnimator;
+import android.animation.Sequencer;
+import android.content.res.TypedArray;
+import android.util.TypedValue;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -27,12 +33,21 @@ import android.util.Xml;
import android.os.SystemClock;
import java.io.IOException;
+import java.util.ArrayList;
/**
* Defines common utilities for working with animations.
*
*/
public class AnimationUtils {
+
+ /**
+ * These flags are used when parsing Sequencer objects
+ */
+ private static final int TOGETHER = 0;
+ private static final int SEQUENTIALLY = 1;
+
+
/**
* Returns the current animation time in milliseconds. This time should be used when invoking
* {@link Animation#setStartTime(long)}. Refer to {@link android.os.SystemClock} for more
@@ -49,7 +64,7 @@ public class AnimationUtils {
/**
* Loads an {@link Animation} object from a resource
- *
+ *
* @param context Application context used to access resources
* @param id The resource id of the animation to load
* @return The animation object reference by the specified id
@@ -77,17 +92,54 @@ public class AnimationUtils {
}
}
+ /**
+ * Loads an {@link Animation} object from a resource
+ *
+ * @param context Application context used to access resources
+ * @param id The resource id of the animation to load
+ * @return The animation object reference by the specified id
+ * @throws NotFoundException when the animation cannot be loaded
+ * @hide
+ */
+ public static Animatable loadAnimator(Context context, int id)
+ throws NotFoundException {
+
+ XmlResourceParser parser = null;
+ try {
+ parser = context.getResources().getAnimation(id);
+ return createAnimatableFromXml(context, parser);
+ } catch (XmlPullParserException ex) {
+ NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
+ Integer.toHexString(id));
+ rnf.initCause(ex);
+ throw rnf;
+ } catch (IOException ex) {
+ NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
+ Integer.toHexString(id));
+ rnf.initCause(ex);
+ throw rnf;
+ } finally {
+ if (parser != null) parser.close();
+ }
+ }
+
+ private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+
+ return createAnimatableFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
+ }
+
private static Animation createAnimationFromXml(Context c, XmlPullParser parser)
throws XmlPullParserException, IOException {
return createAnimationFromXml(c, parser, null, Xml.asAttributeSet(parser));
}
-
+
private static Animation createAnimationFromXml(Context c, XmlPullParser parser,
AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException {
-
+
Animation anim = null;
-
+
// Make sure we are on a start tag.
int type;
int depth = parser.getDepth();
@@ -100,7 +152,7 @@ public class AnimationUtils {
}
String name = parser.getName();
-
+
if (name.equals("set")) {
anim = new AnimationSet(c, attrs);
createAnimationFromXml(c, parser, (AnimationSet)anim, attrs);
@@ -120,7 +172,67 @@ public class AnimationUtils {
parent.addAnimation(anim);
}
}
-
+
+ return anim;
+
+ }
+
+ private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser,
+ AttributeSet attrs, Sequencer parent, int sequenceOrdering)
+ throws XmlPullParserException, IOException {
+
+ Animatable anim = null;
+ ArrayList<Animatable> childAnims = null;
+
+ // Make sure we are on a start tag.
+ int type;
+ int depth = parser.getDepth();
+
+ while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+
+ if (name.equals("property")) {
+ anim = new PropertyAnimator(c, attrs);
+ } else if (name.equals("animator")) {
+ anim = new Animator(c, attrs);
+ } else if (name.equals("sequencer")) {
+ anim = new Sequencer();
+ TypedArray a = c.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.Sequencer);
+ int ordering = a.getInt(com.android.internal.R.styleable.Sequencer_ordering,
+ TOGETHER);
+ createAnimatableFromXml(c, parser, attrs, (Sequencer) anim, ordering);
+ a.recycle();
+ } else {
+ throw new RuntimeException("Unknown animator name: " + parser.getName());
+ }
+
+ if (parent != null) {
+ if (childAnims == null) {
+ childAnims = new ArrayList<Animatable>();
+ }
+ childAnims.add(anim);
+ }
+ }
+ if (parent != null && childAnims != null) {
+ Animatable[] animsArray = new Animatable[childAnims.size()];
+ int index = 0;
+ for (Animatable a : childAnims) {
+ animsArray[index++] = a;
+ }
+ if (sequenceOrdering == TOGETHER) {
+ parent.playTogether(animsArray);
+ } else {
+ parent.playSequentially(animsArray);
+ }
+ }
+
return anim;
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 52e992b2bd8e..681ef701e3d3 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -19,12 +19,11 @@ package android.webkit;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
-import android.content.res.Configuration;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.util.EventLog;
-import java.lang.SecurityException;
+
import java.util.Locale;
/**
@@ -433,9 +432,8 @@ public class WebSettings {
buffer.append(" Build/");
buffer.append(id);
}
- String mobile = ((mContext.getResources().getConfiguration().screenLayout
- & Configuration.SCREENLAYOUT_SIZE_MASK)
- == Configuration.SCREENLAYOUT_SIZE_XLARGE) ? "" : "Mobile ";
+ String mobile = mContext.getResources().getText(
+ com.android.internal.R.string.web_user_agent_target_content).toString();
final String base = mContext.getResources().getText(
com.android.internal.R.string.web_user_agent).toString();
return String.format(base, buffer, mobile);
diff --git a/core/java/android/widget/BaseExpandableListAdapter.java b/core/java/android/widget/BaseExpandableListAdapter.java
index 396b7ae5cc41..b4d6ad76efbe 100644
--- a/core/java/android/widget/BaseExpandableListAdapter.java
+++ b/core/java/android/widget/BaseExpandableListAdapter.java
@@ -43,14 +43,14 @@ public abstract class BaseExpandableListAdapter implements ExpandableListAdapter
}
/**
- * {@see DataSetObservable#notifyInvalidated()}
+ * @see DataSetObservable#notifyInvalidated()
*/
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
/**
- * {@see DataSetObservable#notifyChanged()}
+ * @see DataSetObservable#notifyChanged()
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
diff --git a/core/java/android/widget/HeterogeneousExpandableList.java b/core/java/android/widget/HeterogeneousExpandableList.java
index 12927337e440..e7e09337209f 100644
--- a/core/java/android/widget/HeterogeneousExpandableList.java
+++ b/core/java/android/widget/HeterogeneousExpandableList.java
@@ -23,12 +23,13 @@ import android.view.ViewGroup;
* Additional methods that when implemented make an
* {@link ExpandableListAdapter} take advantage of the {@link Adapter} view type
* mechanism.
- *
- * An {@link ExpandableListAdapter} declares one view type for its group items
+ * <p>
+ * An {@link ExpandableListAdapter} declares it has one view type for its group items
* and one view type for its child items. Although adapted for most {@link ExpandableListView}s,
- * these values should be tuned heterogeneous {@link ExpandableListView}s. Lists that contain
- * different types of group and/or child item views, should use an adapter that implements this
- * interface. This way, the recycled views that will be provided to
+ * these values should be tuned for heterogeneous {@link ExpandableListView}s.
+ * </p>
+ * Lists that contain different types of group and/or child item views, should use an adapter that
+ * implements this interface. This way, the recycled views that will be provided to
* {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
* and
* {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
@@ -48,7 +49,7 @@ public interface HeterogeneousExpandableList {
* . Note: Integers must be in the range 0 to {@link #getGroupTypeCount} - 1.
* {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
* @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
- * @see getGroupTypeCount()
+ * @see #getGroupTypeCount()
*/
int getGroupType(int groupPosition);
@@ -65,7 +66,7 @@ public interface HeterogeneousExpandableList {
* Note: Integers must be in the range 0 to {@link #getChildTypeCount} - 1.
* {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
* @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
- * @see getChildTypeCount()
+ * @see #getChildTypeCount()
*/
int getChildType(int groupPosition, int childPosition);
@@ -78,13 +79,11 @@ public interface HeterogeneousExpandableList {
* . If the adapter always returns the same type of View for all group items, this method should
* return 1.
* </p>
- * <p>
* This method will only be called when the adapter is set on the {@link AdapterView}.
- * </p>
*
* @return The number of types of group Views that will be created by this adapter.
- * @see getChildTypeCount()
- * @see getGroupType()
+ * @see #getChildTypeCount()
+ * @see #getGroupType(int)
*/
int getGroupTypeCount();
@@ -97,13 +96,11 @@ public interface HeterogeneousExpandableList {
* , for any group. If the adapter always returns the same type of View for
* all child items, this method should return 1.
* </p>
- * <p>
* This method will only be called when the adapter is set on the {@link AdapterView}.
- * </p>
*
* @return The total number of types of child Views that will be created by this adapter.
- * @see getGroupTypeCount()
- * @see getChildType()
+ * @see #getGroupTypeCount()
+ * @see #getChildType(int, int)
*/
int getChildTypeCount();
}
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/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0ce81640ebc9..24f14dc8bdc7 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -22,7 +22,7 @@ import com.android.internal.widget.EditableInputConnection;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
-import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -83,10 +83,13 @@ import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.util.TypedValue;
+import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
@@ -927,7 +930,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
setClickable(clickable);
setLongClickable(longClickable);
- prepareCursorController();
+ prepareCursorControllers();
}
private void setTypefaceByIndex(int typefaceIndex, int styleIndex) {
@@ -1133,7 +1136,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
setText(mText);
fixFocusableAndClickableSettings();
- prepareCursorController();
+
+ // SelectionModifierCursorController depends on canSelectText, which depends on mMovement
+ prepareCursorControllers();
}
private void fixFocusableAndClickableSettings() {
@@ -2702,8 +2707,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sendAfterTextChanged((Editable) text);
}
- // Depends on canSelectText, which depends on text
- prepareCursorController();
+ // SelectionModifierCursorController depends on canSelectText, which depends on text
+ prepareCursorControllers();
}
/**
@@ -3727,6 +3732,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
changed = bringTextIntoView();
}
+ if (mShouldStartSelectionActionMode) {
+ startSelectionActionMode();
+ mShouldStartSelectionActionMode = false;
+ }
mPreDrawState = PREDRAW_DONE;
return !changed;
}
@@ -4964,7 +4973,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mLayout = new StaticLayout(mTransformed, mTextPaint,
w, alignment, mSpacingMult, mSpacingAdd,
mIncludePad);
- // Log.e("aaa", "Boring but wide: " + mTransformed);
}
} else if (shouldEllipsize) {
mLayout = new StaticLayout(mTransformed,
@@ -5062,6 +5070,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
}
+
+ // CursorControllers need a non-null mLayout
+ prepareCursorControllers();
}
private boolean compressText(float width) {
@@ -5944,7 +5955,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
} else if (mBlink != null) {
mBlink.removeCallbacks(mBlink);
}
- prepareCursorController();
+
+ // InsertionPointCursorController depends on mCursorVisible
+ prepareCursorControllers();
}
private boolean canMarquee() {
@@ -6533,6 +6546,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mError != null) {
showError();
}
+
+ // We cannot start the selection mode immediately. The layout may be null here and is
+ // needed by the cursor controller. Layout creation is deferred up to drawing. The
+ // selection action mode will be started in onPreDraw().
+ if (selStart != selEnd) {
+ mShouldStartSelectionActionMode = true;
+ }
} else {
if (mError != null) {
hideError();
@@ -6540,12 +6560,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Don't leave us in the middle of a batch edit.
onEndBatchEdit();
- if (mInsertionPointCursorController != null) {
- mInsertionPointCursorController.hide();
- }
- if (mSelectionModifierCursorController != null) {
- mSelectionModifierCursorController.hide();
- }
+ hideInsertionPointCursorController();
+ stopSelectionActionMode();
}
startStopMarquee(focused);
@@ -6633,8 +6649,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (resultCode == InputMethodManager.RESULT_SHOWN) {
start = mPrevStart;
end = mPrevEnd;
- } else if (mInsertionPointCursorController != null) {
- mInsertionPointCursorController.show();
+ } else {
+ if ((mPrevStart != mPrevEnd) && (start == end)) {
+ if ((start >= mPrevStart) && (start <= mPrevEnd)) {
+ // Tapping inside the selection does nothing
+ Selection.setSelection((Spannable) mText, mPrevStart, mPrevEnd);
+ return;
+ } else {
+ // Tapping outside stops selection mode, if any
+ finishSelectionActionMode();
+ }
+ }
+
+ if (mInsertionPointCursorController != null) {
+ mInsertionPointCursorController.show();
+ }
}
final int len = mText.length();
@@ -6673,8 +6702,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if ((mMovement != null || onCheckIsTextEditor()) &&
mText instanceof Spannable && mLayout != null) {
- boolean handled = false;
-
int oldSelStart = getSelectionStart();
int oldSelEnd = getSelectionEnd();
@@ -6685,6 +6712,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mSelectionModifierCursorController.onTouchEvent(event);
}
+ boolean handled = false;
+
if (mMovement != null) {
handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
}
@@ -6715,11 +6744,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return superResult;
}
- private void prepareCursorController() {
+ private void prepareCursorControllers() {
boolean atLeastOneController = false;
// TODO Add an extra android:cursorController flag to disable the controller?
- if (mCursorVisible) {
+ if (mCursorVisible && mLayout != null) {
atLeastOneController = true;
if (mInsertionPointCursorController == null) {
mInsertionPointCursorController = new InsertionPointCursorController();
@@ -6728,24 +6757,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mInsertionPointCursorController = null;
}
- if (canSelectText()) {
+ if (canSelectText() && mLayout != null) {
atLeastOneController = true;
if (mSelectionModifierCursorController == null) {
mSelectionModifierCursorController = new SelectionModifierCursorController();
}
} else {
mSelectionModifierCursorController = null;
+ // Stop selection mode if the controller becomes unavailable.
+ finishSelectionActionMode();
}
if (atLeastOneController) {
- if (sCursorControllerTempRect == null) {
- sCursorControllerTempRect = new Rect();
- }
Resources res = mContext.getResources();
mCursorControllerVerticalOffset = res.getDimensionPixelOffset(
com.android.internal.R.dimen.cursor_controller_vertical_offset);
- } else {
- sCursorControllerTempRect = null;
}
}
@@ -6990,24 +7016,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private boolean canSelectAll() {
- if (mText instanceof Spannable && mText.length() != 0 &&
- mMovement != null && mMovement.canSelectArbitrarily()) {
- return true;
- }
-
- return false;
+ return canSelectText();
}
private boolean canSelectText() {
// prepareCursorController() relies on this method.
// If you change this condition, make sure prepareCursorController is called anywhere
// the value of this condition might be changed.
- if (mText instanceof Spannable && mText.length() != 0 &&
- mMovement != null && mMovement.canSelectArbitrarily()) {
- return true;
- }
-
- return false;
+ return (mText instanceof Spannable &&
+ mText.length() != 0 &&
+ mMovement != null &&
+ mMovement.canSelectArbitrarily());
}
private boolean canCut() {
@@ -7037,16 +7056,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private boolean canPaste() {
- if (mText instanceof Editable && mInput != null &&
- getSelectionStart() >= 0 && getSelectionEnd() >= 0) {
- ClipboardManager clip = (ClipboardManager)getContext()
- .getSystemService(Context.CLIPBOARD_SERVICE);
- if (clip.hasText()) {
- return true;
- }
- }
-
- return false;
+ return (mText instanceof Editable &&
+ mInput != null &&
+ getSelectionStart() >= 0 &&
+ getSelectionEnd() >= 0 &&
+ ((ClipboardManager)getContext().getSystemService(Context.CLIPBOARD_SERVICE)).
+ hasText());
}
/**
@@ -7139,21 +7154,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return (((long) start) << 32) | end;
}
- /**
- * Returns a word to add to the dictionary from the context menu,
- * or null if there is no cursor or no word at the cursor.
- */
- private String getWordForDictionary() {
- long wordLimits = getWordLimitsAt(getSelectionEnd());
- if (wordLimits < 0) {
- return null;
- } else {
- int start = (int) (wordLimits >>> 32);
- int end = (int) (wordLimits & 0x00000000FFFFFFFFL);
- return TextUtils.substring(mTransformed, start, end);
- }
- }
-
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
if (!isShown()) {
@@ -7195,84 +7195,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
super.onCreateContextMenu(menu);
boolean added = false;
- if (!isFocused()) {
- if (isFocusable() && mInput != null) {
- if (canCopy()) {
- MenuHandler handler = new MenuHandler();
- int name = com.android.internal.R.string.copyAll;
-
- menu.add(0, ID_COPY, 0, name).
- setOnMenuItemClickListener(handler).
- setAlphabeticShortcut('c');
- menu.setHeaderTitle(com.android.internal.R.string.
- editTextMenuTitle);
- }
- }
-
- return;
- }
-
MenuHandler handler = new MenuHandler();
- if (canSelectAll()) {
- menu.add(0, ID_SELECT_ALL, 0,
- com.android.internal.R.string.selectAll).
- setOnMenuItemClickListener(handler).
- setAlphabeticShortcut('a');
- added = true;
- }
-
- boolean selection = getSelectionStart() != getSelectionEnd();
-
- if (canSelectText()) {
- if (MetaKeyKeyListener.getMetaState(mText, MetaKeyKeyListener.META_SELECTING) != 0) {
- menu.add(0, ID_STOP_SELECTING_TEXT, 0,
- com.android.internal.R.string.stopSelectingText).
- setOnMenuItemClickListener(handler);
- added = true;
- } else {
- menu.add(0, ID_START_SELECTING_TEXT, 0,
- com.android.internal.R.string.selectText).
- setOnMenuItemClickListener(handler);
- added = true;
- }
- }
-
- if (canCut()) {
- int name;
- if (selection) {
- name = com.android.internal.R.string.cut;
- } else {
- name = com.android.internal.R.string.cutAll;
- }
-
- menu.add(0, ID_CUT, 0, name).
- setOnMenuItemClickListener(handler).
- setAlphabeticShortcut('x');
- added = true;
- }
-
- if (canCopy()) {
- int name;
- if (selection) {
- name = com.android.internal.R.string.copy;
- } else {
- name = com.android.internal.R.string.copyAll;
- }
-
- menu.add(0, ID_COPY, 0, name).
- setOnMenuItemClickListener(handler).
- setAlphabeticShortcut('c');
- added = true;
- }
-
- if (canPaste()) {
- menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
- setOnMenuItemClickListener(handler).
- setAlphabeticShortcut('v');
- added = true;
- }
-
if (mText instanceof Spanned) {
int selStart = getSelectionStart();
int selEnd = getSelectionEnd();
@@ -7286,25 +7210,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
menu.add(0, ID_COPY_URL, 0,
com.android.internal.R.string.copyUrl).
setOnMenuItemClickListener(handler);
+
added = true;
}
}
-
- if (isInputMethodTarget()) {
- menu.add(1, ID_SWITCH_INPUT_METHOD, 0, com.android.internal.R.string.inputMethod).
- setOnMenuItemClickListener(handler);
+
+ // The context menu is not empty, which will prevent the selection mode from starting.
+ // Add a entry to start it in the context menu.
+ // TODO Does not handle the case where a subclass does not call super.thisMethod or
+ // populates the menu AFTER this call.
+ if (menu.size() > 0) {
+ menu.add(0, ID_SELECTION_MODE, 0, com.android.internal.R.string.selectTextMode).
+ setOnMenuItemClickListener(handler);
added = true;
}
- String word = getWordForDictionary();
- if (word != null) {
- menu.add(1, ID_ADD_TO_DICTIONARY, 0,
- getContext().getString(com.android.internal.R.string.addToDictionary, word)).
- setOnMenuItemClickListener(handler);
- added = true;
-
- }
-
if (added) {
menu.setHeaderTitle(com.android.internal.R.string.editTextMenuTitle);
}
@@ -7319,15 +7239,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return imm != null && imm.isActive(this);
}
+ // Selection context mode
private static final int ID_SELECT_ALL = android.R.id.selectAll;
- private static final int ID_START_SELECTING_TEXT = android.R.id.startSelectingText;
- private static final int ID_STOP_SELECTING_TEXT = android.R.id.stopSelectingText;
private static final int ID_CUT = android.R.id.cut;
private static final int ID_COPY = android.R.id.copy;
private static final int ID_PASTE = android.R.id.paste;
+ // Context menu entries
private static final int ID_COPY_URL = android.R.id.copyUrl;
- private static final int ID_SWITCH_INPUT_METHOD = android.R.id.switchInputMethod;
- private static final int ID_ADD_TO_DICTIONARY = android.R.id.addToDictionary;
+ private static final int ID_SELECTION_MODE = android.R.id.selectTextMode;
private class MenuHandler implements MenuItem.OnMenuItemClickListener {
public boolean onMenuItemClick(MenuItem item) {
@@ -7337,11 +7256,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* Called when a context menu option for the text view is selected. Currently
- * this will be one of: {@link android.R.id#selectAll},
- * {@link android.R.id#startSelectingText}, {@link android.R.id#stopSelectingText},
- * {@link android.R.id#cut}, {@link android.R.id#copy},
- * {@link android.R.id#paste}, {@link android.R.id#copyUrl},
- * or {@link android.R.id#switchInputMethod}.
+ * this will be {@link android.R.id#copyUrl} or {@link android.R.id#selectTextMode}.
*/
public boolean onTextContextMenuItem(int id) {
int selStart = getSelectionStart();
@@ -7352,198 +7267,278 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
selEnd = mText.length();
}
- int min = Math.min(selStart, selEnd);
- int max = Math.max(selStart, selEnd);
-
- if (min < 0) {
- min = 0;
- }
- if (max < 0) {
- max = 0;
- }
+ int min = Math.max(0, Math.min(selStart, selEnd));
+ int max = Math.max(0, Math.max(selStart, selEnd));
ClipboardManager clip = (ClipboardManager)getContext()
.getSystemService(Context.CLIPBOARD_SERVICE);
switch (id) {
- case ID_SELECT_ALL:
- Selection.setSelection((Spannable) mText, 0,
- mText.length());
- return true;
+ case ID_COPY_URL:
+ MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
- case ID_START_SELECTING_TEXT:
- MetaKeyKeyListener.startSelecting(this, (Spannable) mText);
+ URLSpan[] urls = ((Spanned) mText).getSpans(min, max, URLSpan.class);
+ if (urls.length == 1) {
+ clip.setText(urls[0].getURL());
+ }
return true;
- case ID_STOP_SELECTING_TEXT:
- MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
- Selection.setSelection((Spannable) mText, getSelectionEnd());
+ case ID_SELECTION_MODE:
+ startSelectionActionMode();
return true;
+ }
- case ID_CUT:
- MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
+ return false;
+ }
- if (min == max) {
- min = 0;
- max = mText.length();
- }
+ @Override
+ public boolean performLongClick() {
+ if (super.performLongClick()) {
+ mEatTouchRelease = true;
+ return true;
+ }
+
+ if (startSelectionActionMode()) {
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ mEatTouchRelease = true;
+ return true;
+ }
- clip.setText(mTransformed.subSequence(min, max));
- ((Editable) mText).delete(min, max);
- return true;
+ return false;
+ }
- case ID_COPY:
- MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
+ private boolean touchPositionIsInSelection() {
+ int selectionStart = getSelectionStart();
+ int selectionEnd = getSelectionEnd();
- if (min == max) {
- min = 0;
- max = mText.length();
- }
+ if (selectionStart == selectionEnd) {
+ return false;
+ }
- clip.setText(mTransformed.subSequence(min, max));
- return true;
+ if (selectionStart > selectionEnd) {
+ int tmp = selectionStart;
+ selectionStart = selectionEnd;
+ selectionEnd = tmp;
+ Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
+ }
- case ID_PASTE:
- MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
+ SelectionModifierCursorController selectionModifierCursorController =
+ ((SelectionModifierCursorController) mSelectionModifierCursorController);
+ int minOffset = selectionModifierCursorController.getMinTouchOffset();
+ int maxOffset = selectionModifierCursorController.getMaxTouchOffset();
- CharSequence paste = clip.getText();
+ return ((minOffset >= selectionStart) && (maxOffset < selectionEnd));
+ }
- if (paste != null) {
- Selection.setSelection((Spannable) mText, max);
- ((Editable) mText).replace(min, max, paste);
- }
+ /**
+ * Provides the callback used to start a selection action mode.
+ *
+ * @return A callback instance that will be used to start selection mode, or null if selection
+ * mode is not available.
+ */
+ private ActionMode.Callback getActionModeCallback() {
+ // Long press in the current selection.
+ // Should initiate a drag. Return false, to rely on context menu for now.
+ if (canSelectText() && !touchPositionIsInSelection()) {
+ return new SelectionActionModeCallback();
+ }
+ return null;
+ }
- return true;
+ /**
+ *
+ * @return true if the selection mode was actually started.
+ */
+ private boolean startSelectionActionMode() {
+ if (mSelectionActionMode != null) {
+ // Selection action mode is already started
+ return false;
+ }
- case ID_COPY_URL:
- MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
+ ActionMode.Callback actionModeCallback = getActionModeCallback();
+ if (actionModeCallback != null) {
+ mSelectionActionMode = startActionMode(actionModeCallback);
+ return mSelectionActionMode != null;
+ }
- URLSpan[] urls = ((Spanned) mText).getSpans(min, max,
- URLSpan.class);
- if (urls.length == 1) {
- clip.setText(urls[0].getURL());
- }
+ return false;
+ }
- return true;
+ /**
+ * Same as {@link #finishSelectionActionMode()}, except that there is no cursor controller
+ * fade out animation. Needed since the drawable and their alpha values are shared by all
+ * TextViews. Switching from one TextView to another would fade the cursor controllers in the
+ * new one otherwise.
+ */
+ private void stopSelectionActionMode() {
+ finishSelectionActionMode();
+ if (mSelectionModifierCursorController != null) {
+ SelectionModifierCursorController selectionModifierCursorController =
+ (SelectionModifierCursorController) mSelectionModifierCursorController;
+ selectionModifierCursorController.cancelFadeOutAnimation();
+ }
+ }
- case ID_SWITCH_INPUT_METHOD:
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) {
- imm.showInputMethodPicker();
- }
- return true;
+ private void finishSelectionActionMode() {
+ if (mSelectionActionMode != null) {
+ mSelectionActionMode.finish();
+ }
+ }
- case ID_ADD_TO_DICTIONARY:
- String word = getWordForDictionary();
+ private class SelectionActionModeCallback implements ActionMode.Callback {
- if (word != null) {
- Intent i = new Intent("com.android.settings.USER_DICTIONARY_INSERT");
- i.putExtra("word", word);
- i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(i);
- }
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ if (mSelectionModifierCursorController == null) {
+ Log.w(LOG_TAG, "TextView has no selection controller. Action mode cancelled.");
+ return false;
+ }
- return true;
+ if (!requestFocus()) {
+ return false;
}
- return false;
- }
+ mode.setTitle(mContext.getString(com.android.internal.R.string.textSelectionCABTitle));
+ mode.setSubtitle(null);
- @Override
- public boolean performLongClick() {
- // TODO This behavior should be moved to View
- // TODO handle legacy code that added items to context menu
- if (canSelectText()) {
- if (startSelectionMode()) {
- mEatTouchRelease = true;
- return true;
+ selectCurrentWord();
+
+ boolean atLeastOne = false;
+
+ if (canSelectAll()) {
+ menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
+ setIcon(com.android.internal.R.drawable.ic_menu_chat_dashboard).
+ setAlphabeticShortcut('a');
+ atLeastOne = true;
}
- }
- if (super.performLongClick()) {
- mEatTouchRelease = true;
- return true;
- }
+ if (canCut()) {
+ menu.add(0, ID_CUT, 0, com.android.internal.R.string.cut).
+ setIcon(com.android.internal.R.drawable.ic_menu_compose).
+ setAlphabeticShortcut('x');
+ atLeastOne = true;
+ }
- return false;
- }
+ if (canCopy()) {
+ menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy).
+ setIcon(com.android.internal.R.drawable.ic_menu_attachment).
+ setAlphabeticShortcut('c');
+ atLeastOne = true;
+ }
- private boolean startSelectionMode() {
- if (mSelectionModifierCursorController != null) {
- int offset = ((SelectionModifierCursorController) mSelectionModifierCursorController).
- getTouchOffset();
+ if (canPaste()) {
+ menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
+ setIcon(com.android.internal.R.drawable.ic_menu_camera).
+ setAlphabeticShortcut('v');
+ atLeastOne = true;
+ }
- int selectionStart, selectionEnd;
+ if (atLeastOne) {
+ mSelectionModifierCursorController.show();
+ return true;
+ } else {
+ return false;
+ }
+ }
+ private void selectCurrentWord() {
+ // In case selection mode is started after an orientation change, use the current
+ // selection instead of creating one
if (hasSelection()) {
- selectionStart = getSelectionStart();
- selectionEnd = getSelectionEnd();
- if (selectionStart > selectionEnd) {
- int tmp = selectionStart;
- selectionStart = selectionEnd;
- selectionEnd = tmp;
- }
- if ((offset >= selectionStart) && (offset <= selectionEnd)) {
- // Long press in the current selection.
- // Should initiate a drag. Return false, to rely on context menu for now.
- return false;
- }
+ return;
}
- long wordLimits = getWordLimitsAt(offset);
+ int selectionStart, selectionEnd;
+
+ SelectionModifierCursorController selectionModifierCursorController =
+ ((SelectionModifierCursorController) mSelectionModifierCursorController);
+ int minOffset = selectionModifierCursorController.getMinTouchOffset();
+ int maxOffset = selectionModifierCursorController.getMaxTouchOffset();
+
+ long wordLimits = getWordLimitsAt(minOffset);
if (wordLimits >= 0) {
selectionStart = (int) (wordLimits >>> 32);
+ } else {
+ selectionStart = Math.max(minOffset - 5, 0);
+ }
+
+ wordLimits = getWordLimitsAt(maxOffset);
+ if (wordLimits >= 0) {
selectionEnd = (int) (wordLimits & 0x00000000FFFFFFFFL);
} else {
- selectionStart = Math.max(offset - 5, 0);
- selectionEnd = Math.min(offset + 5, mText.length());
+ selectionEnd = Math.min(maxOffset + 5, mText.length());
}
Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
+ }
- // Has to be done AFTER selection has been changed to correctly position controllers.
- mSelectionModifierCursorController.show();
-
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return true;
}
- return false;
- }
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ final int itemId = item.getItemId();
+
+ if (itemId == ID_SELECT_ALL) {
+ Selection.setSelection((Spannable) mText, 0, mText.length());
+ // Update controller positions after selection change.
+ if (mSelectionModifierCursorController != null) {
+ mSelectionModifierCursorController.show();
+ }
+ return true;
+ }
- /**
- * Get the offset character closest to the specified absolute position.
- *
- * @param x The horizontal absolute position of a point on screen
- * @param y The vertical absolute position of a point on screen
- * @return the character offset for the character whose position is closest to the specified
- * position.
- *
- * @hide
- */
- public int getOffset(int x, int y) {
- x -= getTotalPaddingLeft();
- y -= getTotalPaddingTop();
+ ClipboardManager clip = (ClipboardManager) getContext().
+ getSystemService(Context.CLIPBOARD_SERVICE);
- // Clamp the position to inside of the view.
- if (x < 0) {
- x = 0;
- } else if (x >= (getWidth() - getTotalPaddingRight())) {
- x = getWidth()-getTotalPaddingRight() - 1;
- }
- if (y < 0) {
- y = 0;
- } else if (y >= (getHeight() - getTotalPaddingBottom())) {
- y = getHeight()-getTotalPaddingBottom() - 1;
- }
+ int min = 0;
+ int max = mText.length();
- x += getScrollX();
- y += getScrollY();
+ if (isFocused()) {
+ final int selStart = getSelectionStart();
+ final int selEnd = getSelectionEnd();
- Layout layout = getLayout();
- final int line = layout.getLineForVertical(y);
- final int offset = layout.getOffsetForHorizontal(line, x);
- return offset;
- }
+ min = Math.max(0, Math.min(selStart, selEnd));
+ max = Math.max(0, Math.max(selStart, selEnd));
+ }
+
+ switch (item.getItemId()) {
+ case ID_PASTE:
+ CharSequence paste = clip.getText();
+
+ if (paste != null) {
+ Selection.setSelection((Spannable) mText, max);
+ ((Editable) mText).replace(min, max, paste);
+ finishSelectionActionMode();
+ }
+ return true;
+
+ case ID_CUT:
+ clip.setText(mTransformed.subSequence(min, max));
+ ((Editable) mText).delete(min, max);
+ finishSelectionActionMode();
+ return true;
+
+ case ID_COPY:
+ clip.setText(mTransformed.subSequence(min, max));
+ finishSelectionActionMode();
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ Selection.setSelection((Spannable) mText, getSelectionStart());
+ if (mSelectionModifierCursorController != null) {
+ mSelectionModifierCursorController.hide();
+ }
+ mSelectionActionMode = null;
+ }
+ }
/**
* A CursorController instance can be used to control a cursor in the text.
@@ -7624,9 +7619,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Has to be done after updatePosition, so that previous position invalidate
// in only done if necessary.
mIsVisible = true;
- if (mSelectionModifierCursorController != null) {
- mSelectionModifierCursorController.hide();
- }
}
public void hide() {
@@ -7661,6 +7653,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
public void updatePosition(int offset) {
+ if (offset == getSelectionStart()) {
+ return; // No change, no need to redraw
+ }
Selection.setSelection((Spannable) mText, offset);
updateDrawablePosition();
}
@@ -7704,13 +7699,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Prevent possible scrollView parent from scrolling, so that
// we can use auto-scrolling.
mParent.requestDisallowInterceptTouchEvent(true);
+ }
- final Rect bounds = mDrawable.getBounds();
- mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
- mOffsetY = bounds.top - mCursorControllerVerticalOffset - y;
+ final Rect bounds = mDrawable.getBounds();
+ mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
+ mOffsetY = bounds.top - mCursorControllerVerticalOffset - y;
- mOnDownTimerStart = event.getEventTime();
- }
+ mOnDownTimerStart = event.getEventTime();
}
break;
}
@@ -7719,7 +7714,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
int time = (int) (event.getEventTime() - mOnDownTimerStart);
if (time <= ViewConfiguration.getTapTimeout()) {
- // A tap on the controller is not grabbed, move the cursor instead
+ // A tap on the controller (not a drag) will move the cursor
int offset = getOffset((int) event.getX(), (int) event.getY());
Selection.setSelection((Spannable) mText, offset);
@@ -7752,8 +7747,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private final Drawable mStartDrawable, mEndDrawable;
// Offset between finger hot point on active cursor controller and actual cursor
private float mOffsetX, mOffsetY;
- // The offset of that last touch down event. Remembered to start selection there.
- private int mTouchOffset;
+ // The offsets of that last touch down event. Remembered to start selection there.
+ private int mMinTouchOffset, mMaxTouchOffset;
SelectionModifierCursorController() {
Resources res = mContext.getResources();
@@ -7767,9 +7762,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// in only done if necessary.
mIsVisible = true;
mFadeOutTimerStart = -1;
- if (mInsertionPointCursorController != null) {
- mInsertionPointCursorController.hide();
- }
+ hideInsertionPointCursorController();
}
public void hide() {
@@ -7780,6 +7773,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
+ public void cancelFadeOutAnimation() {
+ mIsVisible = false;
+ postInvalidate(mStartDrawable);
+ postInvalidate(mEndDrawable);
+ }
+
public void draw(Canvas canvas) {
if (mIsVisible) {
if (mFadeOutTimerStart >= 0) {
@@ -7808,6 +7807,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Handle the case where start and end are swapped, making sure start <= end
if (mStartIsDragged) {
if (offset <= selectionEnd) {
+ if (selectionStart == offset) {
+ return; // no change, no need to redraw;
+ }
selectionStart = offset;
} else {
selectionStart = selectionEnd;
@@ -7816,6 +7818,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
} else {
if (offset >= selectionStart) {
+ if (selectionEnd == offset) {
+ return; // no change, no need to redraw;
+ }
selectionEnd = offset;
} else {
selectionEnd = selectionStart;
@@ -7853,41 +7858,72 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
public void onTouchEvent(MotionEvent event) {
- if (isFocused() && isTextEditable() &&
- (event.getActionMasked() == MotionEvent.ACTION_DOWN)) {
- final int x = (int) event.getX();
- final int y = (int) event.getY();
-
- // Remember finger down position, to be able to start selection on that point
- mTouchOffset = getOffset(x, y);
-
- if (mIsVisible) {
- if (mMovement instanceof ArrowKeyMovementMethod) {
- boolean isOnStart = fingerIsOnDrawable(x, y, mStartDrawable);
- boolean isOnEnd = fingerIsOnDrawable(x, y, mEndDrawable);
- if (isOnStart || isOnEnd) {
- if (mParent != null) {
- // Prevent possible scrollView parent from scrolling, so that
- // we can use auto-scrolling.
- mParent.requestDisallowInterceptTouchEvent(true);
- }
+ if (isTextEditable()) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
- // Start handle will be dragged in case BOTH controller are under finger
- mStartIsDragged = isOnStart;
- final Rect bounds =
- (mStartIsDragged ? mStartDrawable : mEndDrawable).getBounds();
- mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
- mOffsetY = bounds.top - mCursorControllerVerticalOffset - y;
+ // Remember finger down position, to be able to start selection on that point
+ mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
+
+ if (mIsVisible) {
+ if (mMovement instanceof ArrowKeyMovementMethod) {
+ boolean isOnStart = fingerIsOnDrawable(x, y, mStartDrawable);
+ boolean isOnEnd = fingerIsOnDrawable(x, y, mEndDrawable);
+ if (isOnStart || isOnEnd) {
+ if (mParent != null) {
+ // Prevent possible scrollView parent from scrolling, so that
+ // we can use auto-scrolling.
+ mParent.requestDisallowInterceptTouchEvent(true);
+ }
+
+ // Start handle will be dragged in case BOTH controller are under finger
+ mStartIsDragged = isOnStart;
+ final Rect bounds =
+ (mStartIsDragged ? mStartDrawable : mEndDrawable).getBounds();
+ mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
+ mOffsetY = bounds.top - mCursorControllerVerticalOffset - y;
+
+ ((ArrowKeyMovementMethod)mMovement).setCursorController(this);
+ }
+ }
+ }
+ break;
- ((ArrowKeyMovementMethod)mMovement).setCursorController(this);
+ case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_POINTER_UP:
+ // Handle multi-point gestures. Keep min and max offset positions.
+ // Only activated for devices that correctly handle multi-touch.
+ if (mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)) {
+ updateMinAndMaxOffsets(event);
}
- }
+ break;
}
}
}
- public int getTouchOffset() {
- return mTouchOffset;
+ /**
+ * @param event
+ */
+ private void updateMinAndMaxOffsets(MotionEvent event) {
+ int pointerCount = event.getPointerCount();
+ for (int index = 0; index < pointerCount; index++) {
+ final int x = (int) event.getX(index);
+ final int y = (int) event.getY(index);
+ int offset = getOffset(x, y);
+ if (offset < mMinTouchOffset) mMinTouchOffset = offset;
+ if (offset > mMaxTouchOffset) mMaxTouchOffset = offset;
+ }
+ }
+
+ public int getMinTouchOffset() {
+ return mMinTouchOffset;
+ }
+
+ public int getMaxTouchOffset() {
+ return mMaxTouchOffset;
}
public float getOffsetX() {
@@ -7955,6 +7991,52 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
postInvalidateDelayed(delay, bounds.left, bounds.top, bounds.right, bounds.bottom);
}
+ private void hideInsertionPointCursorController() {
+ if (mInsertionPointCursorController != null) {
+ mInsertionPointCursorController.hide();
+ }
+ }
+
+ /**
+ * Get the offset character closest to the specified absolute position.
+ *
+ * @param x The horizontal absolute position of a point on screen
+ * @param y The vertical absolute position of a point on screen
+ * @return the character offset for the character whose position is closest to the specified
+ * position. Returns -1 if there is no layout.
+ *
+ * @hide
+ */
+ public int getOffset(int x, int y) {
+ x -= getTotalPaddingLeft();
+ y -= getTotalPaddingTop();
+
+ // Clamp the position to inside of the view.
+ if (x < 0) {
+ x = 0;
+ } else if (x >= (getWidth() - getTotalPaddingRight())) {
+ x = getWidth()-getTotalPaddingRight() - 1;
+ }
+ if (y < 0) {
+ y = 0;
+ } else if (y >= (getHeight() - getTotalPaddingBottom())) {
+ y = getHeight()-getTotalPaddingBottom() - 1;
+ }
+
+ x += getScrollX();
+ y += getScrollY();
+
+ Layout layout = getLayout();
+ if (layout != null) {
+ final int line = layout.getLineForVertical(y);
+ final int offset = layout.getOffsetForHorizontal(line, x);
+ return offset;
+ } else {
+ return -1;
+ }
+ }
+
+
@ViewDebug.ExportedProperty
private CharSequence mText;
private CharSequence mTransformed;
@@ -7988,8 +8070,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private CursorController mSelectionModifierCursorController;
// Stored once and for all.
private int mCursorControllerVerticalOffset;
+ private boolean mShouldStartSelectionActionMode = false;
+ private ActionMode mSelectionActionMode;
// Created once and shared by different CursorController helper methods.
- private static Rect sCursorControllerTempRect;
+ // Only one cursor controller is active at any time which prevent race conditions.
+ private static Rect sCursorControllerTempRect = new Rect();
private boolean mSelectAllOnFocus = false;
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 6515f256b7b1..99dbe4ce6a44 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -66,7 +66,7 @@ public class ActionBarImpl extends ActionBar {
private TabImpl mSelectedTab;
private int mTabSwitchMode = TAB_SWITCH_ADD_REMOVE;
- private ActionMode mContextMode;
+ private ActionMode mActionMode;
private static final int CONTEXT_DISPLAY_NORMAL = 0;
private static final int CONTEXT_DISPLAY_SPLIT = 1;
@@ -229,9 +229,9 @@ public class ActionBarImpl extends ActionBar {
return mActionView.getDisplayOptions();
}
- public ActionMode startContextMode(ActionMode.Callback callback) {
- if (mContextMode != null) {
- mContextMode.finish();
+ public ActionMode startActionMode(ActionMode.Callback callback) {
+ if (mActionMode != null) {
+ mActionMode.finish();
}
// Don't wait for the close context mode animation to finish.
@@ -241,7 +241,7 @@ public class ActionBarImpl extends ActionBar {
mCloseContext.run();
}
- ActionMode mode = new ContextModeImpl(callback);
+ ActionMode mode = new ActionModeImpl(callback);
if (callback.onCreateActionMode(mode, mode.getMenu())) {
mode.invalidate();
mUpperContextView.initForMode(mode);
@@ -250,7 +250,7 @@ public class ActionBarImpl extends ActionBar {
// TODO animate this
mLowerContextView.setVisibility(View.VISIBLE);
}
- mContextMode = mode;
+ mActionMode = mode;
return mode;
}
return null;
@@ -361,12 +361,12 @@ public class ActionBarImpl extends ActionBar {
/**
* @hide
*/
- public class ContextModeImpl extends ActionMode implements MenuBuilder.Callback {
+ public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback {
private ActionMode.Callback mCallback;
private MenuBuilder mMenu;
private WeakReference<View> mCustomView;
- public ContextModeImpl(ActionMode.Callback callback) {
+ public ActionModeImpl(ActionMode.Callback callback) {
mCallback = callback;
mMenu = new MenuBuilder(mActionView.getContext());
mMenu.setCallback(this);
@@ -379,8 +379,8 @@ public class ActionBarImpl extends ActionBar {
@Override
public void finish() {
- if (mContextMode != this) {
- // Not the active context mode - no-op
+ if (mActionMode != this) {
+ // Not the active action mode - no-op
return;
}
@@ -395,7 +395,7 @@ public class ActionBarImpl extends ActionBar {
// TODO Animate this
mLowerContextView.setVisibility(View.GONE);
}
- mContextMode = null;
+ mActionMode = null;
}
@Override
@@ -422,6 +422,16 @@ public class ActionBarImpl extends ActionBar {
}
@Override
+ public void setTitle(int resId) {
+ setTitle(mActivity.getString(resId));
+ }
+
+ @Override
+ public void setSubtitle(int resId) {
+ setSubtitle(mActivity.getString(resId));
+ }
+
+ @Override
public CharSequence getTitle() {
return mUpperContextView.getTitle();
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 9fcd3f5f11bf..f8a77a1242ba 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -523,7 +523,7 @@ public class ZygoteInit {
String args[] = {
"--setuid=1000",
"--setgid=1000",
- "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003",
+ "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003",
"--capabilities=130104352,130104352",
"--runtime-init",
"--nice-name=system_server",
diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java
new file mode 100644
index 000000000000..d8fd3642ab2f
--- /dev/null
+++ b/core/java/com/android/internal/view/StandaloneActionMode.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.view;
+
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuPopupHelper;
+import com.android.internal.view.menu.SubMenuBuilder;
+import com.android.internal.widget.ActionBarContextView;
+
+import android.content.Context;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+import java.lang.ref.WeakReference;
+
+public class StandaloneActionMode extends ActionMode implements MenuBuilder.Callback {
+ private Context mContext;
+ private ActionBarContextView mContextView;
+ private ActionMode.Callback mCallback;
+ private WeakReference<View> mCustomView;
+ private boolean mFinished;
+
+ private MenuBuilder mMenu;
+
+ public StandaloneActionMode(Context context, ActionBarContextView view,
+ ActionMode.Callback callback) {
+ mContext = context;
+ mContextView = view;
+ mCallback = callback;
+
+ mMenu = new MenuBuilder(context);
+ mMenu.setCallback(this);
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ mContextView.setTitle(title);
+ }
+
+ @Override
+ public void setSubtitle(CharSequence subtitle) {
+ mContextView.setSubtitle(subtitle);
+ }
+
+ @Override
+ public void setTitle(int resId) {
+ setTitle(mContext.getString(resId));
+ }
+
+ @Override
+ public void setSubtitle(int resId) {
+ setSubtitle(mContext.getString(resId));
+ }
+
+ @Override
+ public void setCustomView(View view) {
+ mContextView.setCustomView(view);
+ mCustomView = view != null ? new WeakReference<View>(view) : null;
+ }
+
+ @Override
+ public void invalidate() {
+ mCallback.onPrepareActionMode(this, mMenu);
+ }
+
+ @Override
+ public void finish() {
+ if (mFinished) {
+ return;
+ }
+ mFinished = true;
+
+ mCallback.onDestroyActionMode(this);
+ mContextView.setVisibility(View.GONE);
+ }
+
+ @Override
+ public Menu getMenu() {
+ return mMenu;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mContextView.getTitle();
+ }
+
+ @Override
+ public CharSequence getSubtitle() {
+ return mContextView.getSubtitle();
+ }
+
+ @Override
+ public View getCustomView() {
+ return mCustomView != null ? mCustomView.get() : null;
+ }
+
+ public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ return mCallback.onActionItemClicked(this, item);
+ }
+
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ }
+
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ if (!subMenu.hasVisibleItems()) {
+ return true;
+ }
+
+ new MenuPopupHelper(mContext, subMenu).show();
+ return true;
+ }
+
+ public void onCloseSubMenu(SubMenuBuilder menu) {
+ }
+
+ public void onMenuModeChange(MenuBuilder menu) {
+ }
+}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index a185e2123164..0bedc4e0ce1f 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -70,9 +70,9 @@ public class ActionBarContextView extends ViewGroup {
mItemPadding = a.getDimensionPixelOffset(
com.android.internal.R.styleable.Theme_actionButtonPadding, 0);
setBackgroundDrawable(a.getDrawable(
- com.android.internal.R.styleable.Theme_actionBarContextBackground));
+ com.android.internal.R.styleable.Theme_actionModeBackground));
mCloseDrawable = a.getDrawable(
- com.android.internal.R.styleable.Theme_actionBarCloseContextDrawable);
+ com.android.internal.R.styleable.Theme_actionModeCloseDrawable);
mItemMargin = mItemPadding / 2;
mContentHeight =
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/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a66fe86d3093..01ded6805a36 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -83,6 +83,8 @@
<protected-broadcast android:name="android.hardware.action.USB_CONNECTED" />
<protected-broadcast android:name="android.hardware.action.USB_DISCONNECTED" />
<protected-broadcast android:name="android.hardware.action.USB_STATE" />
+ <protected-broadcast android:name="android.hardware.action.USB_CAMERA_ATTACHED" />
+ <protected-broadcast android:name="android.hardware.action.USB_CAMERA_DETACHED" />
<!-- ====================================== -->
<!-- Permissions for things that cost money -->
diff --git a/core/res/res/layout/screen_xlarge_action_bar.xml b/core/res/res/layout-xlarge/screen_action_bar.xml
index c51ca13031b5..41ce8e467490 100644
--- a/core/res/res/layout/screen_xlarge_action_bar.xml
+++ b/core/res/res/layout-xlarge/screen_action_bar.xml
@@ -4,9 +4,9 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -37,7 +37,7 @@ This is an optimized layout for a screen with the Action Bar enabled.
android:layout_height="wrap_content" />
</ViewAnimator>
<FrameLayout android:id="@android:id/content"
- android:layout_width="match_parent"
+ android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
diff --git a/core/res/res/layout/screen.xml b/core/res/res/layout/screen.xml
index dfa973198047..0269bf1aaa6f 100644
--- a/core/res/res/layout/screen.xml
+++ b/core/res/res/layout/screen.xml
@@ -25,6 +25,13 @@ This is the basic layout for a screen, with all of its features enabled.
android:layout_width="match_parent"
android:layout_height="match_parent"
>
+ <!-- Popout bar for action modes -->
+ <com.android.internal.widget.ActionBarContextView
+ android:id="@+id/action_mode_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
<!-- Title bar -->
<RelativeLayout android:id="@android:id/title_container"
style="?android:attr/windowTitleBackgroundStyle"
diff --git a/core/res/res/layout/screen_custom_title.xml b/core/res/res/layout/screen_custom_title.xml
index 34c9de0d4f1a..fe06dddc8065 100644
--- a/core/res/res/layout/screen_custom_title.xml
+++ b/core/res/res/layout/screen_custom_title.xml
@@ -21,6 +21,13 @@ This is an custom layout for a screen.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
+ <!-- Popout bar for action modes -->
+ <com.android.internal.widget.ActionBarContextView
+ android:id="@+id/action_mode_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
<FrameLayout android:id="@android:id/title_container"
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
diff --git a/core/res/res/layout/screen_progress.xml b/core/res/res/layout/screen_progress.xml
index 6e694c160e1f..966953305ad4 100644
--- a/core/res/res/layout/screen_progress.xml
+++ b/core/res/res/layout/screen_progress.xml
@@ -26,6 +26,13 @@ This is the basic layout for a screen, with all of its features enabled.
android:layout_width="match_parent"
android:layout_height="match_parent"
>
+ <!-- Popout bar for action modes -->
+ <com.android.internal.widget.ActionBarContextView
+ android:id="@+id/action_mode_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
<RelativeLayout android:id="@android:id/title_container"
style="?android:attr/windowTitleBackgroundStyle"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/screen_title.xml b/core/res/res/layout/screen_title.xml
index 39ea1318aad0..26597af6e2e1 100644
--- a/core/res/res/layout/screen_title.xml
+++ b/core/res/res/layout/screen_title.xml
@@ -22,6 +22,12 @@ enabled.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
+ <!-- Popout bar for action modes -->
+ <com.android.internal.widget.ActionBarContextView
+ android:id="@+id/action_mode_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
diff --git a/core/res/res/layout/screen_title_icons.xml b/core/res/res/layout/screen_title_icons.xml
index 62a0228b15c7..c8cf334d5707 100644
--- a/core/res/res/layout/screen_title_icons.xml
+++ b/core/res/res/layout/screen_title_icons.xml
@@ -23,6 +23,12 @@ This is the basic layout for a screen, with all of its features enabled.
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <!-- Popout bar for action modes -->
+ <com.android.internal.widget.ActionBarContextView
+ android:id="@+id/action_mode_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
<RelativeLayout android:id="@android:id/title_container"
style="?android:attr/windowTitleBackgroundStyle"
android:layout_width="match_parent"
diff --git a/core/res/res/values-xlarge/strings.xml b/core/res/res/values-xlarge/strings.xml
new file mode 100644
index 000000000000..fc20be631234
--- /dev/null
+++ b/core/res/res/values-xlarge/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Do not translate. WebView User Agent targeted content -->
+ <string name="web_user_agent_target_content" translatable="false"></string>
+
+</resources> \ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4edd96459668..6c14a3b82343 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -251,6 +251,10 @@
<!-- Reference to a style for the Action Bar -->
<attr name="windowActionBarStyle" format="reference" />
+ <!-- Flag indicating whether action modes should overlay window content
+ when there is not reserved space for their UI (such as an Action Bar). -->
+ <attr name="windowActionModeOverlay" format="boolean" />
+
<!-- Defines the default soft input state that this window would
like when it is displayed. -->
<attr name="windowSoftInputMode">
@@ -444,8 +448,15 @@
<eat-comment />
<!-- Default amount of padding to use between action buttons. -->
<attr name="actionButtonPadding" format="dimension" />
- <attr name="actionBarContextBackground" format="reference" />
- <attr name="actionBarCloseContextDrawable" format="reference" />
+
+ <!-- =================== -->
+ <!-- Action mode styles -->
+ <!-- =================== -->
+ <eat-comment />
+ <!-- Background drawable to use for action mode UI -->
+ <attr name="actionModeBackground" format="reference" />
+ <!-- Drawable to use for the close action mode button -->
+ <attr name="actionModeCloseDrawable" format="reference" />
<!-- =================== -->
<!-- Preference styles -->
@@ -987,6 +998,7 @@
<attr name="backgroundDimAmount" />
<attr name="windowActionBar" />
<attr name="windowActionBarStyle" />
+ <attr name="windowActionModeOverlay" />
</declare-styleable>
<!-- The set of attributes that describe a AlertDialog's theme. -->
@@ -2917,6 +2929,69 @@
</declare-styleable>
<!-- ========================== -->
+ <!-- Animator class attributes -->
+ <!-- ========================== -->
+ <eat-comment />
+
+ <declare-styleable name="Animator">
+ <!-- Defines the interpolator used to smooth the animation movement in time. -->
+ <attr name="interpolator" />
+ <!-- When set to true, fillAfter is taken into account. -->
+ <!-- Amount of time (in milliseconds) for the animation to run. -->
+ <attr name="duration" />
+ <!-- Delay in milliseconds before the animation runs, once start time is reached. -->
+ <attr name="startOffset"/>
+ <!-- Defines how many times the animation should repeat. The default value is 0. -->
+ <attr name="repeatCount"/>
+ <!-- Defines the animation behavior when it reaches the end and the repeat count is
+ greater than 0 or infinite. The default value is restart. -->
+ <attr name="repeatMode"/>
+ <!-- Value the animation starts from. -->
+ <attr name="valueFrom" format="float|integer"/>
+ <!-- Value the animation animates to. -->
+ <attr name="valueTo" format="float|integer"/>
+ <!-- The type of valueFrom and valueTo. -->
+ <attr name="valueType">
+ <!-- valueFrom and valueTo are floats. -->
+ <enum name="floatType" value="0" />
+ <!-- valueFrom and valueTo are integers. -->
+ <enum name="intType" value="1" />
+ <!-- valueFrom and valueTo are doubles. -->
+ <enum name="doubleType" value="2" />
+ <!-- valueFrom and valueTo are colors. -->
+ <enum name="colorType" value="3" />
+ <!-- valueFrom and valueTo are a custom type. -->
+ <enum name="customType" value="4" />
+ </attr>
+ </declare-styleable>
+
+ <!-- ========================== -->
+ <!-- PropertyAnimator class attributes -->
+ <!-- ========================== -->
+ <eat-comment />
+
+ <declare-styleable name="PropertyAnimator">
+ <!-- Name of the property being animated. -->
+ <attr name="propertyName" format="string"/>
+ </declare-styleable>
+
+
+ <!-- ========================== -->
+ <!-- Sequencer class attributes -->
+ <!-- ========================== -->
+ <eat-comment />
+
+ <declare-styleable name="Sequencer">
+ <!-- Name of the property being animated. -->
+ <attr name="ordering">
+ <!-- child animations should be played together. -->
+ <enum name="together" value="0" />
+ <!-- child animations should be played sequentially, in the same order as the xml. -->
+ <enum name="sequentially" value="1" />
+ </attr>
+ </declare-styleable>
+
+ <!-- ========================== -->
<!-- State attributes -->
<!-- ========================== -->
<eat-comment />
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index e607fad5e683..33cd10034f7a 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -59,6 +59,7 @@
<item type="id" name="copy" />
<item type="id" name="paste" />
<item type="id" name="copyUrl" />
+ <item type="id" name="selectTextMode" />
<item type="id" name="switchInputMethod" />
<item type="id" name="keyboardView" />
<item type="id" name="closeButton" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 34dd46c57f4d..d1021a552bfb 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1306,10 +1306,19 @@
<public type="attr" name="showAsAction" />
<public type="attr" name="actionButtonPadding" />
<public type="attr" name="previewImage" />
- <public type="attr" name="actionBarContextBackground" />
- <public type="attr" name="actionBarCloseContextDrawable" />
+ <public type="attr" name="actionModeBackground" />
+ <public type="attr" name="actionModeCloseDrawable" />
+ <public type="attr" name="windowActionModeOverlay" />
+ <public type="attr" name="valueFrom" />
+ <public type="attr" name="valueTo" />
+ <public type="attr" name="valueType" />
+ <public type="attr" name="propertyName" />
+ <public type="attr" name="ordering" />
<public type="id" name="home" />
+ <!-- Context menu ID for the "Select text..." menu item to switch to text
+ selection context mode in text views. -->
+ <public type="id" name="selectTextMode" />
<public type="style" name="Theme.WithActionBar" />
<public type="style" name="Widget.Spinner.DropDown" />
@@ -1322,5 +1331,6 @@
content to still retain all of the standard functionality of
the base class. -->
<public type="layout" name="list_content" />
-
+
+ <public type="string" name="selectTextMode" id="0x01040030" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7a80884a21e7..8894514d7fd5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1605,7 +1605,9 @@
<!-- Do not translate. WebView User Agent string -->
<string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>)
- AppleWebKit/534.3 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.3</string>
+ AppleWebKit/534.1 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.1</string>
+ <!-- Do not translate. WebView User Agent targeted content -->
+ <string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
<!-- Title for a JavaScript dialog. "The page at <url of current page> says:" -->
<string name="js_dialog_title">The page at \'<xliff:g id="title">%s</xliff:g>\' says:</string>
@@ -1868,39 +1870,29 @@
<!-- Item on EditText context menu. This action is used to select all text in the edit field. -->
<string name="selectAll">Select all</string>
- <!-- Item on EditText context menu. This action is used to start selecting text in the edit field. -->
- <string name="selectText">Select text</string>
-
- <!-- Item on EditText context menu. This action is used to start selecting text in the edit field. -->
- <string name="stopSelectingText">Stop selecting text</string>
-
<!-- Item on EditText context menu. This action is used to cut selected the text into the clipboard. -->
<string name="cut">Cut</string>
- <!-- Item on EditText context menu. This action is used to cut all the text into the clipboard. -->
- <string name="cutAll">Cut all</string>
-
<!-- Item on EditText context menu. This action is used to cut selected the text into the clipboard. -->
<string name="copy">Copy</string>
- <!-- Item on EditText context menu. This action is used to copy all the text into the clipboard. -->
- <string name="copyAll">Copy all</string>
-
<!-- Item on EditText context menu. This action is used t o paste from the clipboard into the eidt field -->
<string name="paste">Paste</string>
<!-- Item on EditText context menu. This action is used to copy a URL from the edit field into the clipboard. -->
<string name="copyUrl">Copy URL</string>
+ <!-- Item on EditText context menu. Added only when the context menu is not empty, it enable selection context mode. -->
+ <string name="selectTextMode">Select text...</string>
+
+ <!-- Text selection contextual mode title, displayed in the CAB.. -->
+ <string name="textSelectionCABTitle">Text selection</string>
+
<!-- EditText context menu -->
<string name="inputMethod">Input method</string>
- <!-- Item on EditText context menu, used to add a word to the
- input method dictionary. -->
- <string name="addToDictionary">"Add \"<xliff:g id="word" example="rickroll">%s</xliff:g>\" to dictionary</string>
-
<!-- Title for EditText context menu -->
- <string name="editTextMenuTitle">Edit text</string>
+ <string name="editTextMenuTitle">Text actions</string>
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. -->
<string name="low_internal_storage_view_title">Low on space</string>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index bce0e02f67bb..998480d835b5 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -206,8 +206,8 @@
<!-- Action bar styles -->
<item name="actionButtonPadding">12dip</item>
- <item name="actionBarContextBackground">@android:drawable/action_bar_context_background</item>
- <item name="actionBarCloseContextDrawable">@android:drawable/ic_menu_close_clear_cancel</item>
+ <item name="actionModeBackground">@android:drawable/action_bar_context_background</item>
+ <item name="actionModeCloseDrawable">@android:drawable/ic_menu_close_clear_cancel</item>
</style>
<!-- Variant of the default (dark) theme with no title bar -->
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
index cdaefc853079..ad8d444114d7 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
@@ -91,30 +91,30 @@ public class ConnectivityManagerMobileTest
// DISCONNECTING, DISCONNECTED, UNKNOWN
private void waitForNetworkState(int networkType, State expectedState, long timeout) {
long startTime = System.currentTimeMillis();
- // In case the broadcast is already sent out, no need to wait
- if (cmActivity.mCM.getNetworkInfo(networkType).getState() == expectedState) {
- return;
- } else {
- while (true) {
- if ((System.currentTimeMillis() - startTime) > timeout) {
+ while (true) {
+ if ((System.currentTimeMillis() - startTime) > timeout) {
+ if (cmActivity.mCM.getNetworkInfo(networkType).getState() != expectedState) {
assertFalse("Wait for network state timeout", true);
+ } else {
+ // the broadcast has been sent out. the state has been changed.
+ return;
+ }
+ }
+ Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType +
+ " to be " + expectedState.toString());
+ synchronized (cmActivity.connectivityObject) {
+ try {
+ cmActivity.connectivityObject.wait(STATE_TRANSITION_SHORT_TIMEOUT);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
}
- Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType +
- " to be " + expectedState.toString());
- synchronized (cmActivity.connectivityObject) {
- try {
- cmActivity.connectivityObject.wait(STATE_TRANSITION_SHORT_TIMEOUT);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if ((cmActivity.mNetworkInfo.getType() != networkType) ||
- (cmActivity.mNetworkInfo.getState() != expectedState)) {
- Log.v(LOG_TAG, "network state for " + cmActivity.mNetworkInfo.getType() +
- "is: " + cmActivity.mNetworkInfo.getState());
- continue;
- }
- break;
+ if ((cmActivity.mNetworkInfo.getType() != networkType) ||
+ (cmActivity.mNetworkInfo.getState() != expectedState)) {
+ Log.v(LOG_TAG, "network state for " + cmActivity.mNetworkInfo.getType() +
+ "is: " + cmActivity.mNetworkInfo.getState());
+ continue;
}
+ break;
}
}
}
@@ -123,26 +123,26 @@ public class ConnectivityManagerMobileTest
// WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
private void waitForWifiState(int expectedState, long timeout) {
long startTime = System.currentTimeMillis();
- if (cmActivity.mWifiState == expectedState) {
- return;
- } else {
- while (true) {
- if ((System.currentTimeMillis() - startTime) > timeout) {
+ while (true) {
+ if ((System.currentTimeMillis() - startTime) > timeout) {
+ if (cmActivity.mWifiState != expectedState) {
assertFalse("Wait for Wifi state timeout", true);
+ } else {
+ return;
+ }
+ }
+ Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState);
+ synchronized (cmActivity.wifiObject) {
+ try {
+ cmActivity.wifiObject.wait(5*1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
}
- Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState);
- synchronized (cmActivity.wifiObject) {
- try {
- cmActivity.wifiObject.wait(5*1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if (cmActivity.mWifiState != expectedState) {
- Log.v(LOG_TAG, "Wifi state is: " + cmActivity.mWifiNetworkInfo.getState());
- continue;
- }
- break;
+ if (cmActivity.mWifiState != expectedState) {
+ Log.v(LOG_TAG, "Wifi state is: " + cmActivity.mWifiNetworkInfo.getState());
+ continue;
}
+ break;
}
}
}
diff --git a/core/tests/hosttests/Android.mk b/core/tests/hosttests/Android.mk
index 00012015a489..07d99cb26306 100644
--- a/core/tests/hosttests/Android.mk
+++ b/core/tests/hosttests/Android.mk
@@ -23,7 +23,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_MODULE := FrameworkCoreHostTests
-LOCAL_JAVA_LIBRARIES := hosttestlib ddmlib junit
+LOCAL_JAVA_LIBRARIES := hosttestlib ddmlib-prebuilt junit
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/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/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/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/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/InputManager.h b/include/ui/InputManager.h
index e7552381162c..7ebec10dd9ad 100644
--- a/include/ui/InputManager.h
+++ b/include/ui/InputManager.h
@@ -96,22 +96,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 {
@@ -140,14 +146,17 @@ public:
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/hwui/Android.mk b/libs/hwui/Android.mk
index 172952a8f49d..a9714c7ab79b 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -10,6 +10,7 @@ LOCAL_SRC_FILES:= \
Patch.cpp \
PatchCache.cpp \
Program.cpp \
+ ProgramCache.cpp \
TextureCache.cpp
LOCAL_C_INCLUDES += \
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 2e44e122ea39..0ed62768b899 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -45,32 +45,22 @@ namespace uirenderer {
#define MB(s) s * 1024 * 1024
// Generates simple and textured vertices
-#define SV(x, y) { { x, y } }
#define FV(x, y, u, v) { { x, y }, { u, v } }
///////////////////////////////////////////////////////////////////////////////
// Globals
///////////////////////////////////////////////////////////////////////////////
-static const SimpleVertex gDrawColorVertices[] = {
- SV(0.0f, 0.0f),
- SV(1.0f, 0.0f),
- SV(0.0f, 1.0f),
- SV(1.0f, 1.0f)
-};
-static const GLsizei gDrawColorVertexStride = sizeof(SimpleVertex);
-static const GLsizei gDrawColorVertexCount = 4;
-
// This array is never used directly but used as a memcpy source in the
// OpenGLRenderer constructor
-static const TextureVertex gDrawTextureVertices[] = {
+static const TextureVertex gMeshVertices[] = {
FV(0.0f, 0.0f, 0.0f, 0.0f),
FV(1.0f, 0.0f, 1.0f, 0.0f),
FV(0.0f, 1.0f, 0.0f, 1.0f),
FV(1.0f, 1.0f, 1.0f, 1.0f)
};
-static const GLsizei gDrawTextureVertexStride = sizeof(TextureVertex);
-static const GLsizei gDrawTextureVertexCount = 4;
+static const GLsizei gMeshStride = sizeof(TextureVertex);
+static const GLsizei gMeshCount = 4;
// In this array, the index of each Blender equals the value of the first
// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
@@ -143,7 +133,18 @@ OpenGLRenderer::OpenGLRenderer():
mLastTexture = 0;
- memcpy(mDrawTextureVertices, gDrawTextureVertices, sizeof(gDrawTextureVertices));
+ memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
+
+ ProgramDescription d;
+ mProgramCache.get(d);
+ d.hasTexture = true;
+ mProgramCache.get(d);
+ d.hasAlpha8Texture = true;
+ d.hasGradient = true;
+ d.hasBitmap = true;
+ d.shadersMode = SkXfermode::kDstOut_Mode;
+ d.colorOp = ProgramDescription::kColorMatrix;
+ mProgramCache.get(d);
}
OpenGLRenderer::~OpenGLRenderer() {
@@ -650,9 +651,13 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot
mModelView.scale(right - left, bottom - top, 1.0f);
if (!useProgram(mDrawColorProgram)) {
- const GLvoid* p = &gDrawColorVertices[0].position[0];
+ const GLvoid* vertices = &mMeshVertices[0].position[0];
+ const GLvoid* texCoords = &mMeshVertices[0].texture[0];
+
glVertexAttribPointer(mDrawColorProgram->position, 2, GL_FLOAT, GL_FALSE,
- gDrawColorVertexStride, p);
+ gMeshStride, vertices);
+ glVertexAttribPointer(mDrawColorProgram->texCoords, 2, GL_FLOAT, GL_FALSE,
+ gMeshStride, texCoords);
}
if (!ignoreTransform) {
@@ -664,7 +669,7 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot
glUniform4f(mDrawColorProgram->color, r, g, b, a);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawColorVertexCount);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
}
void OpenGLRenderer::drawLinearGradientShader(float left, float top, float right, float bottom,
@@ -717,9 +722,9 @@ void OpenGLRenderer::drawLinearGradientShader(float left, float top, float right
&screenSpace.data[0]);
glVertexAttribPointer(mDrawLinearGradientProgram->position, 2, GL_FLOAT, GL_FALSE,
- gDrawTextureVertexStride, &mDrawTextureVertices[0].position[0]);
+ gMeshStride, &mMeshVertices[0].position[0]);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
}
void OpenGLRenderer::drawBitmapShader(float left, float top, float right, float bottom,
@@ -757,7 +762,7 @@ void OpenGLRenderer::drawBitmapShader(float left, float top, float right, float
resetDrawTextureTexCoords(u1, v1, u2, v2);
drawTextureMesh(left, top, right, bottom, texture->id, alpha, mode, texture->blend,
- &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
+ &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], NULL);
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
}
@@ -769,13 +774,13 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b
getAlphaAndMode(paint, &alpha, &mode);
drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, texture->blend,
- &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
+ &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], NULL);
}
void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
GLuint texture, float alpha, SkXfermode::Mode mode, bool blend) {
drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend,
- &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
+ &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], NULL);
}
void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
@@ -794,12 +799,12 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b
glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha);
glVertexAttribPointer(mDrawTextureProgram->position, 2, GL_FLOAT, GL_FALSE,
- gDrawTextureVertexStride, vertices);
+ gMeshStride, vertices);
glVertexAttribPointer(mDrawTextureProgram->texCoords, 2, GL_FLOAT, GL_FALSE,
- gDrawTextureVertexStride, texCoords);
+ gMeshStride, texCoords);
if (!indices) {
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
} else {
glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_SHORT, indices);
}
@@ -842,7 +847,7 @@ bool OpenGLRenderer::useProgram(const sp<Program>& program) {
}
void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
- TextureVertex* v = &mDrawTextureVertices[0];
+ TextureVertex* v = &mMeshVertices[0];
TextureVertex::setUV(v++, u1, v1);
TextureVertex::setUV(v++, u2, v1);
TextureVertex::setUV(v++, u1, v2);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 248c9c3c1b6d..975be056b15f 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -41,6 +41,7 @@
#include "PatchCache.h"
#include "Vertex.h"
#include "FontRenderer.h"
+#include "ProgramCache.h"
namespace android {
namespace uirenderer {
@@ -268,7 +269,7 @@ private:
SkXfermode::Mode mode);
/**
- * Resets the texture coordinates stored in mDrawTextureVertices. Setting the values
+ * Resets the texture coordinates stored in mMeshVertices. Setting the values
* back to default is achieved by calling:
*
* resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
@@ -337,7 +338,7 @@ private:
sp<DrawLinearGradientProgram> mDrawLinearGradientProgram;
// Used to draw textured quads
- TextureVertex mDrawTextureVertices[4];
+ TextureVertex mMeshVertices[4];
// Current texture state
GLuint mLastTexture;
@@ -372,6 +373,7 @@ private:
TextureCache mTextureCache;
LayerCache mLayerCache;
GradientCache mGradientCache;
+ ProgramCache mProgramCache;
PatchCache mPatchCache;
}; // class OpenGLRenderer
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 6e608084f5bb..86fc154bb9c2 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -27,7 +27,6 @@ namespace uirenderer {
#define SHADER_SOURCE(name, source) const char* name = #source
-#include "shaders/drawColor.vert"
#include "shaders/drawColor.frag"
#include "shaders/drawTexture.vert"
@@ -127,7 +126,7 @@ GLuint Program::buildShader(const char* source, GLenum type) {
///////////////////////////////////////////////////////////////////////////////
DrawColorProgram::DrawColorProgram():
- Program(gDrawColorVertexShader, gDrawColorFragmentShader) {
+ Program(gDrawTextureVertexShader, gDrawColorFragmentShader) {
getAttribsAndUniforms();
}
@@ -138,6 +137,7 @@ DrawColorProgram::DrawColorProgram(const char* vertex, const char* fragment):
void DrawColorProgram::getAttribsAndUniforms() {
position = addAttrib("position");
+ texCoords = addAttrib("texCoords");
color = addUniform("color");
transform = addUniform("transform");
}
@@ -154,11 +154,13 @@ void DrawColorProgram::set(const mat4& projectionMatrix, const mat4& modelViewMa
void DrawColorProgram::use() {
Program::use();
glEnableVertexAttribArray(position);
+ glEnableVertexAttribArray(texCoords);
}
void DrawColorProgram::remove() {
Program::remove();
glDisableVertexAttribArray(position);
+ glDisableVertexAttribArray(texCoords);
}
///////////////////////////////////////////////////////////////////////////////
@@ -167,26 +169,21 @@ void DrawColorProgram::remove() {
DrawTextureProgram::DrawTextureProgram():
DrawColorProgram(gDrawTextureVertexShader, gDrawTextureFragmentShader) {
- texCoords = addAttrib("texCoords");
sampler = addUniform("sampler");
}
DrawTextureProgram::DrawTextureProgram(const char* vertex, const char* fragment):
DrawColorProgram(vertex, fragment) {
- texCoords = addAttrib("texCoords");
sampler = addUniform("sampler");
}
void DrawTextureProgram::use() {
DrawColorProgram::use();
- glActiveTexture(GL_TEXTURE0);
glUniform1i(sampler, 0);
- glEnableVertexAttribArray(texCoords);
}
void DrawTextureProgram::remove() {
DrawColorProgram::remove();
- glDisableVertexAttribArray(texCoords);
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 824aa0525e44..2cdd90512d95 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -53,6 +53,16 @@ public:
virtual void remove();
/**
+ * Returns the OpenGL name of the specified attribute.
+ */
+ int getAttrib(const char* name);
+
+ /**
+ * Returns the OpenGL name of the specified uniform.
+ */
+ int getUniform(const char* name);
+
+ /**
* Indicates whether this program is currently in use with
* the GL context.
*/
@@ -67,10 +77,6 @@ protected:
* @return The OpenGL name of the attribute.
*/
int addAttrib(const char* name);
- /**
- * Returns the OpenGL name of the specified attribute.
- */
- int getAttrib(const char* name);
/**
* Adds a uniform with the specified name.
@@ -78,10 +84,6 @@ protected:
* @return The OpenGL name of the uniform.
*/
int addUniform(const char* name);
- /**
- * Returns the OpenGL name of the specified uniform.
- */
- int getUniform(const char* name);
private:
/**
@@ -145,6 +147,11 @@ public:
int position;
/**
+ * Name of the texture coordinates attribute.
+ */
+ int texCoords;
+
+ /**
* Name of the color uniform.
*/
int color;
@@ -184,11 +191,6 @@ public:
* Name of the texture sampler uniform.
*/
int sampler;
-
- /**
- * Name of the texture coordinates attribute.
- */
- int texCoords;
};
class DrawTextProgram: public DrawTextureProgram {
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
new file mode 100644
index 000000000000..5a89eb6a3a1a
--- /dev/null
+++ b/libs/hwui/ProgramCache.cpp
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "OpenGLRenderer"
+
+#include <utils/String8.h>
+
+#include "ProgramCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Vertex shaders snippets
+///////////////////////////////////////////////////////////////////////////////
+
+// TODO: Implement BitmapShader, implement repeat/mirror for npot
+
+const char* gVS_Header_Attributes =
+ "attribute vec4 position;\n";
+const char* gVS_Header_Attributes_TexCoords =
+ "attribute vec2 texCoords;\n";
+const char* gVS_Header_Uniforms =
+ "uniform mat4 transform;\n";
+const char* gVS_Header_Uniforms_HasGradient =
+ "uniform float gradientLength;\n"
+ "uniform vec2 gradient;\n"
+ "uniform vec2 gradientStart;\n"
+ "uniform mat4 screenSpace;\n";
+const char* gVS_Header_Varyings_HasTexture =
+ "varying vec2 outTexCoords;\n";
+const char* gVS_Header_Varyings_HasBitmap =
+ "varying vec2 outBitmapTexCoords;\n";
+const char* gVS_Header_Varyings_HasGradient =
+ "varying float index;\n";
+const char* gVS_Main =
+ "\nvoid main(void) {\n";
+const char* gVS_Main_OutTexCoords =
+ " outTexCoords = texCoords;\n";
+const char* gVS_Main_OutGradientIndex =
+ " vec4 location = screenSpace * position;\n"
+ " index = dot(location.xy - gradientStart, gradient) * gradientLength;\n";
+const char* gVS_Main_Position =
+ " gl_Position = transform * position;\n";
+const char* gVS_Footer =
+ "}\n\n";
+
+///////////////////////////////////////////////////////////////////////////////
+// Fragment shaders snippets
+///////////////////////////////////////////////////////////////////////////////
+
+const char* gFS_Header =
+ "precision mediump float;\n\n";
+const char* gFS_Uniforms_Color =
+ "uniform vec4 color;\n";
+const char* gFS_Uniforms_TextureSampler =
+ "uniform sampler2D sampler;\n";
+const char* gFS_Uniforms_GradientSampler =
+ "uniform sampler2D gradientSampler;\n";
+const char* gFS_Uniforms_BitmapSampler =
+ "uniform sampler2D bitmapSampler;\n";
+const char* gFS_Uniforms_ColorOp[4] = {
+ // None
+ "",
+ // Matrix
+ "uniform mat4 colorMatrix;\n"
+ "uniform vec4 colorMatrixVector;\n",
+ // Lighting
+ "uniform float lightingMul;\n"
+ "uniform float lightingAdd;\n",
+ // PorterDuff
+ "uniform vec4 colorBLend;\n"
+};
+const char* gFS_Main =
+ "\nvoid main(void) {\n"
+ " vec4 fragColor;\n";
+const char* gFS_Main_FetchColor =
+ " fragColor = color;\n";
+const char* gFS_Main_FetchTexture =
+ " fragColor = color * texture2D(sampler, outTexCoords);\n";
+const char* gFS_Main_FetchA8Texture =
+ " fragColor = color * texture2D(sampler, outTexCoords).a;\n";
+const char* gFS_Main_FetchGradient =
+ " vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n";
+const char* gFS_Main_FetchBitmap =
+ " vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n";
+const char* gFS_Main_BlendShadersBG =
+ " fragColor = blendShaders(bitmapColor, gradientColor)";
+const char* gFS_Main_BlendShadersGB =
+ " fragColor = blendShaders(gradientColor, bitmapColor)";
+const char* gFS_Main_BlendShaders_Modulate =
+ " * fragColor.a;\n";
+const char* gFS_Main_FragColor =
+ " gl_FragColor = fragColor;\n";
+const char* gFS_Main_ApplyColorOp[4] = {
+ // None
+ "",
+ // Matrix
+ " fragColor *= colorMatrix;\n"
+ " fragColor += colorMatrixVector;\n",
+ // Lighting
+ " fragColor *= lightingMul;\n"
+ " fragColor += lightingAdd;\n",
+ // PorterDuff
+ " fragColor = blendColors(colorBlend, fragColor);\n"
+};
+const char* gFS_Footer =
+ "}\n\n";
+
+///////////////////////////////////////////////////////////////////////////////
+// PorterDuff snippets
+///////////////////////////////////////////////////////////////////////////////
+
+const char* gPorterDuff[12] = {
+ // Clear
+ "return vec4(0.0, 0.0, 0.0, 0.0);\n",
+ // Src
+ "return src;\n",
+ // Dst
+ "return dst;\n",
+ // SrcOver
+ "return vec4(src.rgb + (1.0 - src.a) * dst.rgb, src.a + dst.a - src.a * dst.a);\n",
+ // DstOver
+ "return vec4(dst.rgb + (1.0 - dst.a) * src.rgb, src.a + dst.a - src.a * dst.a);\n",
+ // SrcIn
+ "return vec4(src.rgb * dst.a, src.a * dst.a);\n",
+ // DstIn
+ "return vec4(dst.rgb * src.a, src.a * dst.a);\n",
+ // SrcOut
+ "return vec4(src.rgb * (1.0 - dst.a), src.a * (1.0 - dst.a));\n",
+ // DstOut
+ "return vec4(dst.rgb * (1.0 - src.a), dst.a * (1.0 - src.a));\n",
+ // SrcAtop
+ "return vec4(src.rgb * dst.a + (1.0 - src.a) * dst.rgb, dst.a);\n",
+ // DstAtop
+ "return vec4(dst.rgb * src.a + (1.0 - dst.a) * src.rgb, src.a);\n",
+ // Xor
+ "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, "
+ "src.a + dst.a - 2.0 * src.a * dst.a);\n",
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructors
+///////////////////////////////////////////////////////////////////////////////
+
+ProgramCache::ProgramCache() {
+}
+
+ProgramCache::~ProgramCache() {
+ clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache management
+///////////////////////////////////////////////////////////////////////////////
+
+void ProgramCache::clear() {
+ size_t count = mCache.size();
+ for (size_t i = 0; i < count; i++) {
+ delete mCache.valueAt(i);
+ }
+ mCache.clear();
+}
+
+Program* ProgramCache::get(const ProgramDescription& description) {
+ programid key = description.key();
+ ssize_t index = mCache.indexOfKey(key);
+ Program* program = NULL;
+ if (index < 0) {
+ PROGRAM_LOGD("Could not find program with key 0x%x", key);
+ program = generateProgram(description, key);
+ mCache.add(key, program);
+ } else {
+ program = mCache.valueAt(index);
+ }
+ return program;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Program generation
+///////////////////////////////////////////////////////////////////////////////
+
+Program* ProgramCache::generateProgram(const ProgramDescription& description, programid key) {
+ String8 vertexShader = generateVertexShader(description);
+ String8 fragmentShader = generateFragmentShader(description);
+
+ Program* program = new Program(vertexShader.string(), fragmentShader.string());
+ return program;
+}
+
+String8 ProgramCache::generateVertexShader(const ProgramDescription& description) {
+ // Add attributes
+ String8 shader(gVS_Header_Attributes);
+ if (description.hasTexture || description.hasBitmap) {
+ shader.append(gVS_Header_Attributes_TexCoords);
+ }
+ // Uniforms
+ shader.append(gVS_Header_Uniforms);
+ if (description.hasGradient) {
+ shader.append(gVS_Header_Uniforms_HasGradient);
+ }
+ // Varyings
+ if (description.hasTexture) {
+ shader.append(gVS_Header_Varyings_HasTexture);
+ }
+ if (description.hasGradient) {
+ shader.append(gVS_Header_Varyings_HasGradient);
+ }
+ if (description.hasBitmap) {
+ shader.append(gVS_Header_Varyings_HasBitmap);
+ }
+
+ // Begin the shader
+ shader.append(gVS_Main); {
+ if (description.hasTexture) {
+ shader.append(gVS_Main_OutTexCoords);
+ }
+ if (description.hasGradient) {
+ shader.append(gVS_Main_OutGradientIndex);
+ }
+ // Output transformed position
+ shader.append(gVS_Main_Position);
+ }
+ // End the shader
+ shader.append(gVS_Footer);
+
+ PROGRAM_LOGD("*** Generated vertex shader:\n\n%s", shader.string());
+
+ return shader;
+}
+
+String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) {
+ // Set the default precision
+ String8 shader(gFS_Header);
+
+ // Varyings
+ if (description.hasTexture) {
+ shader.append(gVS_Header_Varyings_HasTexture);
+ }
+ if (description.hasGradient) {
+ shader.append(gVS_Header_Varyings_HasGradient);
+ }
+ if (description.hasBitmap) {
+ shader.append(gVS_Header_Varyings_HasBitmap);
+ }
+
+
+ // Uniforms
+ shader.append(gFS_Uniforms_Color);
+ if (description.hasTexture) {
+ shader.append(gFS_Uniforms_TextureSampler);
+ }
+ if (description.hasGradient) {
+ shader.append(gFS_Uniforms_GradientSampler);
+ }
+ if (description.hasBitmap) {
+ shader.append(gFS_Uniforms_BitmapSampler);
+ }
+ shader.append(gFS_Uniforms_ColorOp[description.colorOp]);
+
+ // Generate required functions
+ if (description.hasGradient && description.hasBitmap) {
+ generatePorterDuffBlend(shader, "blendShaders", description.shadersMode);
+ }
+ if (description.colorOp == ProgramDescription::kColorBlend) {
+ generatePorterDuffBlend(shader, "blendColors", description.colorMode);
+ }
+
+ // Begin the shader
+ shader.append(gFS_Main); {
+ // Stores the result in fragColor directly
+ if (description.hasTexture) {
+ if (description.hasAlpha8Texture) {
+ shader.append(gFS_Main_FetchA8Texture);
+ } else {
+ shader.append(gFS_Main_FetchTexture);
+ }
+ } else {
+ shader.append(gFS_Main_FetchColor);
+ }
+ if (description.hasGradient) {
+ shader.append(gFS_Main_FetchGradient);
+ }
+ if (description.hasBitmap) {
+ shader.append(gFS_Main_FetchBitmap);
+ }
+ // Case when we have two shaders set
+ if (description.hasGradient && description.hasBitmap) {
+ if (description.isBitmapFirst) {
+ shader.append(gFS_Main_BlendShadersBG);
+ } else {
+ shader.append(gFS_Main_BlendShadersGB);
+ }
+ shader.append(gFS_Main_BlendShaders_Modulate);
+ }
+ // Apply the color op if needed
+ shader.append(gFS_Main_ApplyColorOp[description.colorOp]);
+ // Output the fragment
+ shader.append(gFS_Main_FragColor);
+ }
+ // End the shader
+ shader.append(gFS_Footer);
+
+ PROGRAM_LOGD("*** Generated fragment shader:\n\n%s", shader.string());
+ return shader;
+}
+
+void ProgramCache::generatePorterDuffBlend(String8& shader, const char* name,
+ SkXfermode::Mode mode) {
+ shader.append("\nvec4 ");
+ shader.append(name);
+ shader.append("(vec4 src, vec4 dst) {\n");
+ shader.append(" ");
+ shader.append(gPorterDuff[mode]);
+ shader.append("}\n");
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
new file mode 100644
index 000000000000..7f2f5faf33e8
--- /dev/null
+++ b/libs/hwui/ProgramCache.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_PROGRAM_CACHE_H
+#define ANDROID_UI_PROGRAM_CACHE_H
+
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+
+#include <SkXfermode.h>
+
+#include "Program.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_PROGRAM_CACHE 0
+
+// Debug
+#if DEBUG_PROGRAM_CACHE
+ #define PROGRAM_LOGD(...) LOGD(__VA_ARGS__)
+#else
+ #define PROGRAM_LOGD(...)
+#endif
+
+#define PROGRAM_KEY_TEXTURE 0x1
+#define PROGRAM_KEY_A8_TEXTURE 0x2
+#define PROGRAM_KEY_BITMAP 0x4
+#define PROGRAM_KEY_GRADIENT 0x8
+#define PROGRAM_KEY_BITMAP_FIRST 0x10
+#define PROGRAM_KEY_COLOR_MATRIX 0x20
+#define PROGRAM_KEY_COLOR_LIGHTING 0x40
+#define PROGRAM_KEY_COLOR_BLEND 0x80
+
+// Support only the 12 Porter-Duff modes for now
+#define PROGRAM_MAX_XFERMODE 0xC
+#define PROGRAM_XFERMODE_SHADER_SHIFT 24
+#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
+
+///////////////////////////////////////////////////////////////////////////////
+// Types
+///////////////////////////////////////////////////////////////////////////////
+
+typedef uint32_t programid;
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Describe the features required for a given program. The features
+ * determine the generation of both the vertex and fragment shaders.
+ * A ProgramDescription must be used in conjunction with a ProgramCache.
+ */
+struct ProgramDescription {
+ enum ColorModifier {
+ kColorNone,
+ kColorMatrix,
+ kColorLighting,
+ kColorBlend
+ };
+
+ ProgramDescription():
+ hasTexture(false), hasAlpha8Texture(false),
+ hasBitmap(false), hasGradient(false), shadersMode(SkXfermode::kClear_Mode),
+ colorOp(kColorNone), colorMode(SkXfermode::kClear_Mode) {
+ }
+
+ // Texturing
+ bool hasTexture;
+ bool hasAlpha8Texture;
+
+ // Shaders
+ bool hasBitmap;
+ bool hasGradient;
+ SkXfermode::Mode shadersMode;
+ bool isBitmapFirst;
+
+ // Color operations
+ int colorOp;
+ SkXfermode::Mode colorMode;
+
+ programid key() const {
+ programid key = 0;
+ if (hasTexture) key |= PROGRAM_KEY_TEXTURE;
+ if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE;
+ if (hasBitmap) key |= PROGRAM_KEY_BITMAP;
+ if (hasGradient) key |= PROGRAM_KEY_GRADIENT;
+ if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST;
+ if (hasBitmap && hasGradient) {
+ key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT;
+ }
+ switch (colorOp) {
+ case kColorMatrix:
+ key |= PROGRAM_KEY_COLOR_MATRIX;
+ break;
+ case kColorLighting:
+ key |= PROGRAM_KEY_COLOR_LIGHTING;
+ break;
+ case kColorBlend:
+ key |= PROGRAM_KEY_COLOR_BLEND;
+ key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
+ break;
+ case kColorNone:
+ break;
+ }
+ return key;
+ }
+}; // struct ProgramDescription
+
+/**
+ * Generates and caches program. Programs are generated based on
+ * ProgramDescriptions.
+ */
+class ProgramCache {
+public:
+ ProgramCache();
+ ~ProgramCache();
+
+ Program* get(const ProgramDescription& description);
+
+ void clear();
+
+private:
+ Program* generateProgram(const ProgramDescription& description, programid key);
+ String8 generateVertexShader(const ProgramDescription& description);
+ String8 generateFragmentShader(const ProgramDescription& description);
+ void generatePorterDuffBlend(String8& shader, const char* name, SkXfermode::Mode mode);
+
+ KeyedVector<programid, Program*> mCache;
+
+}; // class ProgramCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_PROGRAM_CACHE_H
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index ffd063384846..1f540864f17f 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -21,14 +21,6 @@ namespace android {
namespace uirenderer {
/**
- * Simple structure to describe a vertex with a position.
- * This is used to draw filled rectangles without a texture.
- */
-struct SimpleVertex {
- float position[2];
-}; // struct SimpleVertex
-
-/**
* Simple structure to describe a vertex with a position and a texture.
*/
struct TextureVertex {
diff --git a/libs/hwui/shaders/drawColor.vert b/libs/hwui/shaders/drawColor.vert
deleted file mode 100644
index 20e26363e1e4..000000000000
--- a/libs/hwui/shaders/drawColor.vert
+++ /dev/null
@@ -1,11 +0,0 @@
-SHADER_SOURCE(gDrawColorVertexShader,
-
-attribute vec4 position;
-
-uniform mat4 transform;
-
-void main(void) {
- gl_Position = transform * position;
-}
-
-);
diff --git a/libs/hwui/shaders/drawLinearGradient.vert b/libs/hwui/shaders/drawLinearGradient.vert
index f5c669b0607e..d2f857d4d958 100644
--- a/libs/hwui/shaders/drawLinearGradient.vert
+++ b/libs/hwui/shaders/drawLinearGradient.vert
@@ -2,10 +2,10 @@ SHADER_SOURCE(gDrawLinearGradientVertexShader,
attribute vec4 position;
+uniform mat4 transform;
uniform float gradientLength;
uniform vec2 gradient;
uniform vec2 start;
-uniform mat4 transform;
uniform mat4 screenSpace;
varying float index;
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/fountain.rs b/libs/rs/java/Fountain/src/com/android/fountain/fountain.rs
index 34bea2fcf532..f87ef596c8f4 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/fountain.rs
+++ b/libs/rs/java/Fountain/src/com/android/fountain/fountain.rs
@@ -19,13 +19,14 @@ Point_t *point;
#pragma rs export_func(addParticles)
int root() {
+ float dt = min(rsGetDt(), 0.1f);
rsgClearColor(0.f, 0.f, 0.f, 1.f);
const float height = rsgGetHeight();
const int size = rsAllocationGetDimX(rsGetAllocation(point));
-
+ float dy2 = dt * (10.f);
Point_t * p = point;
for (int ct=0; ct < size; ct++) {
- p->delta.y += 0.15f;
+ p->delta.y += dy2;
p->position += p->delta;
if ((p->position.y > height) && (p->delta.y > 0)) {
p->delta.y *= -0.3f;
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
index 5dcd372bfaa5..8e198c743580 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
@@ -17,6 +17,7 @@ rs_script vBlurScript;
rs_script hBlurScript;
rs_script levelsScript;
+const int CMD_FINISHED = 1;
// Store our coefficients here
static float gaussian[MAX_RADIUS * 2 + 1];
@@ -89,14 +90,11 @@ void filter() {
rsForEach(levelsScript, rsGetAllocation(InPixel), rsGetAllocation(OutPixel), 0);
}
- int count = 0;
- rsSendToClient(&count, 1, 4, 0);
+ rsSendToClientBlocking(CMD_FINISHED);
}
void filterBenchmark() {
blur();
-
- int count = 0;
- rsSendToClient(&count, 1, 4, 0);
+ rsSendToClientBlocking(CMD_FINISHED);
}
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index d6df581af33e..d1784f3e3af4 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -780,9 +780,13 @@ bool Context::sendMessageToClient(void *data, uint32_t cmdID, size_t len, bool w
}
}
//LOGE("sendMessageToClient 2");
- void *p = mIO.mToClient.reserve(len);
- memcpy(p, data, len);
- mIO.mToClient.commit(cmdID, len);
+ if (len > 0) {
+ void *p = mIO.mToClient.reserve(len);
+ memcpy(p, data, len);
+ mIO.mToClient.commit(cmdID, len);
+ } else {
+ mIO.mToClient.commit(cmdID, 0);
+ }
//LOGE("sendMessageToClient 3");
return true;
}
diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h
index 0717059fbd8b..455ece742d01 100644
--- a/libs/rs/rsScript.h
+++ b/libs/rs/rsScript.h
@@ -40,7 +40,8 @@ public:
virtual ~Script();
struct Enviroment_t {
- uint32_t mStartTimeMillis;
+ int64_t mStartTimeMillis;
+ int64_t mLastDtTime;
const char* mTimeZone;
ObjectBaseRef<ProgramVertex> mVertex;
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 9c29ca6f640e..ccdde008f089 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -179,112 +179,30 @@ static int32_t SC_year()
return timeinfo->tm_year;
}
-static int64_t SC_uptimeMillis2()
+static int64_t SC_uptimeMillis()
{
return nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC));
}
-static int64_t SC_startTimeMillis2()
+static int64_t SC_startTimeMillis()
{
GET_TLS();
return sc->mEnviroment.mStartTimeMillis;
}
-static int64_t SC_elapsedTimeMillis2()
+static int64_t SC_elapsedTimeMillis()
{
GET_TLS();
return nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC))
- sc->mEnviroment.mStartTimeMillis;
}
-static int32_t SC_uptimeMillis()
-{
- return nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC));
-}
-
-static int32_t SC_startTimeMillis()
-{
- GET_TLS();
- return sc->mEnviroment.mStartTimeMillis;
-}
-
-static int32_t SC_elapsedTimeMillis()
+static float SC_getDt()
{
GET_TLS();
- return nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC))
- - sc->mEnviroment.mStartTimeMillis;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// Matrix routines
-//////////////////////////////////////////////////////////////////////////////
-
-
-static void SC_matrixLoadIdentity(rsc_Matrix *mat)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->loadIdentity();
-}
-
-static void SC_matrixLoadFloat(rsc_Matrix *mat, const float *f)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->load(f);
-}
-
-static void SC_matrixLoadMat(rsc_Matrix *mat, const rsc_Matrix *newmat)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->load(reinterpret_cast<const Matrix *>(newmat));
-}
-
-static void SC_matrixLoadRotate(rsc_Matrix *mat, float rot, float x, float y, float z)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->loadRotate(rot, x, y, z);
-}
-
-static void SC_matrixLoadScale(rsc_Matrix *mat, float x, float y, float z)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->loadScale(x, y, z);
-}
-
-static void SC_matrixLoadTranslate(rsc_Matrix *mat, float x, float y, float z)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->loadTranslate(x, y, z);
-}
-
-static void SC_matrixLoadMultiply(rsc_Matrix *mat, const rsc_Matrix *lhs, const rsc_Matrix *rhs)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->loadMultiply(reinterpret_cast<const Matrix *>(lhs),
- reinterpret_cast<const Matrix *>(rhs));
-}
-
-static void SC_matrixMultiply(rsc_Matrix *mat, const rsc_Matrix *rhs)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->multiply(reinterpret_cast<const Matrix *>(rhs));
-}
-
-static void SC_matrixRotate(rsc_Matrix *mat, float rot, float x, float y, float z)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->rotate(rot, x, y, z);
-}
-
-static void SC_matrixScale(rsc_Matrix *mat, float x, float y, float z)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->scale(x, y, z);
-}
-
-static void SC_matrixTranslate(rsc_Matrix *mat, float x, float y, float z)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->translate(x, y, z);
+ int64_t l = sc->mEnviroment.mLastDtTime;
+ sc->mEnviroment.mLastDtTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ return ((float)(sc->mEnviroment.mLastDtTime - l)) / 1.0e9;
}
@@ -372,22 +290,40 @@ static void SC_debugFv4(const char *s, rsvF_4 fv) {
static void SC_debugI32(const char *s, int32_t i) {
LOGE("%s %i 0x%x", s, i, i);
}
+static void SC_debugU32(const char *s, uint32_t i) {
+ LOGE("%s %i 0x%x", s, i, i);
+}
static void SC_debugP(const char *s, const void *p) {
LOGE("%s %p", s, p);
}
-static uint32_t SC_toClient(void *data, int cmdID, int len, int waitForSpace)
+static uint32_t SC_toClient2(int cmdID, void *data, int len)
+{
+ GET_TLS();
+ //LOGE("SC_toClient %i %i %i", cmdID, len);
+ return rsc->sendMessageToClient(data, cmdID, len, false);
+}
+
+static uint32_t SC_toClient(int cmdID)
{
GET_TLS();
- //LOGE("SC_toClient %i %i %i", cmdID, len, waitForSpace);
- return rsc->sendMessageToClient(data, cmdID, len, waitForSpace != 0);
+ //LOGE("SC_toClient %i", cmdID);
+ return rsc->sendMessageToClient(NULL, cmdID, 0, false);
}
-static void SC_scriptCall(int scriptID)
+static uint32_t SC_toClientBlocking2(int cmdID, void *data, int len)
{
GET_TLS();
- rsc->runScript((Script *)scriptID);
+ //LOGE("SC_toClientBlocking %i %i", cmdID, len);
+ return rsc->sendMessageToClient(data, cmdID, len, true);
+}
+
+static uint32_t SC_toClientBlocking(int cmdID)
+{
+ GET_TLS();
+ //LOGE("SC_toClientBlocking %i", cmdID);
+ return rsc->sendMessageToClient(NULL, cmdID, 0, true);
}
int SC_divsi3(int a, int b)
@@ -453,12 +389,12 @@ static ScriptCState::SymbolTable_t gSyms[] = {
{ "__divsi3", (void *)&SC_divsi3 },
// allocation
- { "rsAllocationGetDimX", (void *)&SC_allocGetDimX },
- { "rsAllocationGetDimY", (void *)&SC_allocGetDimY },
- { "rsAllocationGetDimZ", (void *)&SC_allocGetDimZ },
- { "rsAllocationGetDimLOD", (void *)&SC_allocGetDimLOD },
- { "rsAllocationGetDimFaces", (void *)&SC_allocGetDimFaces },
- { "rsGetAllocation", (void *)&SC_getAllocation },
+ { "_Z19rsAllocationGetDimX13rs_allocation", (void *)&SC_allocGetDimX },
+ { "_Z19rsAllocationGetDimY13rs_allocation", (void *)&SC_allocGetDimY },
+ { "_Z19rsAllocationGetDimZ13rs_allocation", (void *)&SC_allocGetDimZ },
+ { "_Z21rsAllocationGetDimLOD13rs_allocation", (void *)&SC_allocGetDimLOD },
+ { "_Z23rsAllocationGetDimFaces13rs_allocation", (void *)&SC_allocGetDimFaces },
+ { "_Z15rsGetAllocationPKv", (void *)&SC_getAllocation },
{ "_Z14rsGetElementAt13rs_allocationj", (void *)&SC_getElementAtX },
{ "_Z14rsGetElementAt13rs_allocationjj", (void *)&SC_getElementAtXY },
@@ -471,6 +407,7 @@ static ScriptCState::SymbolTable_t gSyms[] = {
{ "_Z7rsDebugPKcDv3_f", (void *)&SC_debugFv3 },
{ "_Z7rsDebugPKcDv4_f", (void *)&SC_debugFv4 },
{ "_Z7rsDebugPKci", (void *)&SC_debugI32 },
+ { "_Z7rsDebugPKcj", (void *)&SC_debugU32 },
{ "_Z7rsDebugPKcPKv", (void *)&SC_debugP },
//extern void __attribute__((overloadable))rsDebug(const char *, const void *);
@@ -483,30 +420,32 @@ static ScriptCState::SymbolTable_t gSyms[] = {
{ "_Z6rsFracf", (void *)&SC_frac },
// time
+ { "_Z8rsSecond", (void *)&SC_second },
+ { "_Z8rsMinute", (void *)&SC_minute },
+ { "_Z6rsHour", (void *)&SC_hour },
+ { "_Z5rsDay", (void *)&SC_day },
+ { "_Z7rsMonth", (void *)&SC_month },
+ { "_Z6rsYear", (void *)&SC_year },
+ { "_Z14rsUptimeMillis", (void*)&SC_uptimeMillis },
+ { "_Z17rsStartTimeMillis", (void*)&SC_startTimeMillis },
+ { "_Z19rsElapsedTimeMillis", (void*)&SC_elapsedTimeMillis },
+ { "_Z7rsGetDt", (void*)&SC_getDt },
+
{ "rsSecond", (void *)&SC_second },
{ "rsMinute", (void *)&SC_minute },
{ "rsHour", (void *)&SC_hour },
{ "rsDay", (void *)&SC_day },
{ "rsMonth", (void *)&SC_month },
{ "rsYear", (void *)&SC_year },
- { "rsUptimeMillis", (void*)&SC_uptimeMillis2 },
- { "rsStartTimeMillis", (void*)&SC_startTimeMillis2 },
- { "rsElapsedTimeMillis", (void*)&SC_elapsedTimeMillis2 },
-
- { "rsSendToClient", (void *)&SC_toClient },
-
- // matrix
- { "rsMatrixLoadIdentity", (void *)&SC_matrixLoadIdentity },
- { "rsMatrixLoadFloat", (void *)&SC_matrixLoadFloat },
- { "rsMatrixLoadMat", (void *)&SC_matrixLoadMat },
- { "rsMatrixLoadRotate", (void *)&SC_matrixLoadRotate },
- { "rsMatrixLoadScale", (void *)&SC_matrixLoadScale },
- { "rsMatrixLoadTranslate", (void *)&SC_matrixLoadTranslate },
- { "rsMatrixLoadMultiply", (void *)&SC_matrixLoadMultiply },
- { "rsMatrixMultiply", (void *)&SC_matrixMultiply },
- { "rsMatrixRotate", (void *)&SC_matrixRotate },
- { "rsMatrixScale", (void *)&SC_matrixScale },
- { "rsMatrixTranslate", (void *)&SC_matrixTranslate },
+ { "rsUptimeMillis", (void*)&SC_uptimeMillis },
+ { "rsStartTimeMillis", (void*)&SC_startTimeMillis },
+ { "rsElapsedTimeMillis", (void*)&SC_elapsedTimeMillis },
+ { "rsGetDt", (void*)&SC_getDt },
+
+ { "_Z14rsSendToClienti", (void *)&SC_toClient },
+ { "_Z14rsSendToClientiPKvj", (void *)&SC_toClient2 },
+ { "_Z22rsSendToClientBlockingi", (void *)&SC_toClientBlocking },
+ { "_Z22rsSendToClientBlockingiPKvj", (void *)&SC_toClientBlocking2 },
{ "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach },
//{ "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach2 },
@@ -516,9 +455,6 @@ static ScriptCState::SymbolTable_t gSyms[] = {
//{ "sinf_fast", (void *)&SC_sinf_fast },
//{ "cosf_fast", (void *)&SC_cosf_fast },
- { "scriptCall", (void *)&SC_scriptCall },
-
-
{ NULL, NULL }
};
diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh
index bd6e5a9e9e52..309a95209847 100644
--- a/libs/rs/scriptc/rs_math.rsh
+++ b/libs/rs/scriptc/rs_math.rsh
@@ -7,12 +7,18 @@
// Allocations
-extern rs_allocation rsGetAllocation(const void *);
-extern uint32_t rsAllocationGetDimX(rs_allocation);
-extern uint32_t rsAllocationGetDimY(rs_allocation);
-extern uint32_t rsAllocationGetDimZ(rs_allocation);
-extern uint32_t rsAllocationGetDimLOD(rs_allocation);
-extern uint32_t rsAllocationGetDimFaces(rs_allocation);
+extern rs_allocation __attribute__((overloadable))
+ rsGetAllocation(const void *);
+extern uint32_t __attribute__((overloadable))
+ rsAllocationGetDimX(rs_allocation);
+extern uint32_t __attribute__((overloadable))
+ rsAllocationGetDimY(rs_allocation);
+extern uint32_t __attribute__((overloadable))
+ rsAllocationGetDimZ(rs_allocation);
+extern uint32_t __attribute__((overloadable))
+ rsAllocationGetDimLOD(rs_allocation);
+extern uint32_t __attribute__((overloadable))
+ rsAllocationGetDimFaces(rs_allocation);
extern const void * __attribute__((overloadable))
rsGetElementAt(rs_allocation, uint32_t x);
@@ -28,30 +34,35 @@ extern void __attribute__((overloadable))rsDebug(const char *, float2);
extern void __attribute__((overloadable))rsDebug(const char *, float3);
extern void __attribute__((overloadable))rsDebug(const char *, float4);
extern void __attribute__((overloadable))rsDebug(const char *, int);
+extern void __attribute__((overloadable))rsDebug(const char *, uint);
extern void __attribute__((overloadable))rsDebug(const char *, const void *);
#define RS_DEBUG(a) rsDebug(#a, a)
#define RS_DEBUG_MARKER rsDebug(__FILE__, __LINE__)
// RS Math
-extern int __attribute__((overloadable)) rsRand(int);
-extern int __attribute__((overloadable)) rsRand(int, int);
-extern float __attribute__((overloadable)) rsRand(float);
-extern float __attribute__((overloadable)) rsRand(float, float);
+extern int __attribute__((overloadable))rsRand(int);
+extern int __attribute__((overloadable))rsRand(int, int);
+extern float __attribute__((overloadable))rsRand(float);
+extern float __attribute__((overloadable))rsRand(float, float);
extern float __attribute__((overloadable)) rsFrac(float);
// time
-extern int32_t rsSecond();
-extern int32_t rsMinute();
-extern int32_t rsHour();
-extern int32_t rsDay();
-extern int32_t rsMonth();
-extern int32_t rsYear();
-extern int64_t rsUptimeMillis();
-extern int64_t rsStartTimeMillis();
-extern int64_t rsElapsedTimeMillis();
-
-extern int rsSendToClient(void *data, int cmdID, int len, int waitForSpace);
+extern int32_t /*__attribute__((overloadable))*/rsSecond();
+extern int32_t /*__attribute__((overloadable))*/rsMinute();
+extern int32_t /*__attribute__((overloadable))*/rsHour();
+extern int32_t /*__attribute__((overloadable))*/rsDay();
+extern int32_t /*__attribute__((overloadable))*/rsMonth();
+extern int32_t /*__attribute__((overloadable))*/rsYear();
+extern int64_t /*__attribute__((overloadable))*/rsUptimeMillis();
+extern int64_t /*__attribute__((overloadable))*/rsStartTimeMillis();
+extern int64_t /*__attribute__((overloadable))*/rsElapsedTimeMillis();
+extern float /*__attribute__((overloadable))*/rsGetDt();
+
+extern bool __attribute__((overloadable))rsSendToClient(int cmdID);
+extern bool __attribute__((overloadable))rsSendToClient(int cmdID, const void *data, uint len);
+extern void __attribute__((overloadable))rsSendToClientBlocking(int cmdID);
+extern void __attribute__((overloadable))rsSendToClientBlocking(int cmdID, const void *data, uint len);
// Script to Script
typedef struct rs_script_call {
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/InputManager.cpp b/libs/ui/InputManager.cpp
index e1d15a4ba977..bf2347902f70 100644
--- a/libs/ui/InputManager.cpp
+++ b/libs/ui/InputManager.cpp
@@ -89,26 +89,35 @@ 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..c5183e41b13c 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 = mOrientedSurfaceWidth - 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 = mOrientedSurfaceWidth - x;
+ y = mOrientedSurfaceHeight - y;
+ orientation = - orientation;
break;
}
case InputReaderPolicyInterface::ROTATION_270: {
float xTemp = x;
- x = mDisplayHeight - y;
+ x = mOrientedSurfaceHeight - 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..c13760a30ac9
--- /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 <unistd.h>
+#include <assert.h>
+
+static inline size_t min(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(count, size_t(mOutTotalSize - mOutCurPosition));
+ while (toRead > 0) {
+ // First, write from whatever we already have decoded and ready to go
+ size_t deliverable = min(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(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/location/java/android/location/Country.aidl b/location/java/android/location/Country.aidl
new file mode 100644
index 000000000000..c83d645aa869
--- /dev/null
+++ b/location/java/android/location/Country.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+parcelable Country; \ No newline at end of file
diff --git a/location/java/android/location/Country.java b/location/java/android/location/Country.java
new file mode 100755
index 000000000000..3c05403a6bc2
--- /dev/null
+++ b/location/java/android/location/Country.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.location;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class wraps the country information.
+ *
+ * @hide
+ */
+public class Country implements Parcelable {
+ /**
+ * The country code came from the mobile network
+ */
+ public static final int COUNTRY_SOURCE_NETWORK = 0;
+
+ /**
+ * The country code came from the location service
+ */
+ public static final int COUNTRY_SOURCE_LOCATION = 1;
+
+ /**
+ * The country code was read from the SIM card
+ */
+ public static final int COUNTRY_SOURCE_SIM = 2;
+
+ /**
+ * The country code came from the system locale setting
+ */
+ public static final int COUNTRY_SOURCE_LOCALE = 3;
+
+ /**
+ * The ISO 3166-1 two letters country code.
+ */
+ private final String mCountryIso;
+
+ /**
+ * Where the country code came from.
+ */
+ private final int mSource;
+
+ private int mHashCode;
+ /**
+ *
+ * @param countryIso the ISO 3166-1 two letters country code.
+ * @param source where the countryIso came from, could be one of below
+ * values
+ * <p>
+ * <ul>
+ * <li>{@link #COUNTRY_SOURCE_NETWORK}</li>
+ * <li>{@link #COUNTRY_SOURCE_LOCATION}</li>
+ * <li>{@link #COUNTRY_SOURCE_SIM}</li>
+ * <li>{@link #COUNTRY_SOURCE_LOCALE}</li>
+ * </ul>
+ */
+ public Country(final String countryIso, final int source) {
+ if (countryIso == null || source < COUNTRY_SOURCE_NETWORK
+ || source > COUNTRY_SOURCE_LOCALE) {
+ throw new IllegalArgumentException();
+ }
+ mCountryIso = countryIso.toLowerCase();
+ mSource = source;
+ }
+
+ public Country(Country country) {
+ mCountryIso = country.mCountryIso;
+ mSource = country.mSource;
+ }
+
+ /**
+ * @return the ISO 3166-1 two letters country code
+ */
+ public final String getCountryIso() {
+ return mCountryIso;
+ }
+
+ /**
+ * @return where the country code came from, could be one of below values
+ * <p>
+ * <ul>
+ * <li>{@link #COUNTRY_SOURCE_NETWORK}</li>
+ * <li>{@link #COUNTRY_SOURCE_LOCATION}</li>
+ * <li>{@link #COUNTRY_SOURCE_SIM}</li>
+ * <li>{@link #COUNTRY_SOURCE_LOCALE}</li>
+ * </ul>
+ */
+ public final int getSource() {
+ return mSource;
+ }
+
+ public static final Parcelable.Creator<Country> CREATOR = new Parcelable.Creator<Country>() {
+ public Country createFromParcel(Parcel in) {
+ return new Country(in.readString(), in.readInt());
+ }
+
+ public Country[] newArray(int size) {
+ return new Country[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mCountryIso);
+ parcel.writeInt(mSource);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == this) {
+ return true;
+ }
+ if (object instanceof Country) {
+ Country c = (Country) object;
+ return mCountryIso.equals(c.getCountryIso()) && mSource == c.getSource();
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = mHashCode;
+ if (hash == 0) {
+ hash = 17;
+ hash = hash * 13 + mCountryIso.hashCode();
+ hash = hash * 13 + mSource;
+ mHashCode = hash;
+ }
+ return mHashCode;
+ }
+
+ /**
+ * Compare the specified country to this country object ignoring the mSource
+ * field, return true if the countryIso fields are equal
+ *
+ * @param country the country to compare
+ * @return true if the specified country's countryIso field is equal to this
+ * country's, false otherwise.
+ */
+ public boolean equalsIgnoreSource(Country country) {
+ return country != null && mCountryIso.equals(country.getCountryIso());
+ }
+}
diff --git a/location/java/android/location/CountryDetector.java b/location/java/android/location/CountryDetector.java
new file mode 100644
index 000000000000..0b780cea8407
--- /dev/null
+++ b/location/java/android/location/CountryDetector.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * 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.location;
+
+import java.util.HashMap;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * This class provides access to the system country detector service. This
+ * service allows applications to obtain the country that the user is in.
+ * <p>
+ * The country will be detected in order of reliability, like
+ * <ul>
+ * <li>Mobile network</li>
+ * <li>Location</li>
+ * <li>SIM's country</li>
+ * <li>Phone's locale</li>
+ * </ul>
+ * <p>
+ * Call the {@link #detectCountry()} to get the available country immediately.
+ * <p>
+ * To be notified of the future country change, use the
+ * {@link #addCountryListener}
+ * <p>
+ * <p>
+ * You do not instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.COUNTRY_DETECTOR)}.
+ * <p>
+ * Both ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions are needed.
+ *
+ * @hide
+ */
+public class CountryDetector {
+
+ /**
+ * The class to wrap the ICountryListener.Stub and CountryListener objects
+ * together. The CountryListener will be notified through the specific
+ * looper once the country changed and detected.
+ */
+ private final static class ListenerTransport extends ICountryListener.Stub {
+
+ private final CountryListener mListener;
+
+ private final Handler mHandler;
+
+ public ListenerTransport(CountryListener listener, Looper looper) {
+ mListener = listener;
+ if (looper != null) {
+ mHandler = new Handler(looper);
+ } else {
+ mHandler = new Handler();
+ }
+ }
+
+ public void onCountryDetected(final Country country) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ mListener.onCountryDetected(country);
+ }
+ });
+ }
+ }
+
+ private final static String TAG = "CountryDetector";
+ private final ICountryDetector mService;
+ private final HashMap<CountryListener, ListenerTransport> mListeners;
+
+ /**
+ * @hide - hide this constructor because it has a parameter of type
+ * ICountryDetector, which is a system private class. The right way to
+ * create an instance of this class is using the factory
+ * Context.getSystemService.
+ */
+ public CountryDetector(ICountryDetector service) {
+ mService = service;
+ mListeners = new HashMap<CountryListener, ListenerTransport>();
+ }
+
+ /**
+ * Start detecting the country that the user is in.
+ *
+ * @return the country if it is available immediately, otherwise null will
+ * be returned.
+ */
+ public Country detectCountry() {
+ try {
+ return mService.detectCountry();
+ } catch (RemoteException e) {
+ Log.e(TAG, "detectCountry: RemoteException", e);
+ return null;
+ }
+ }
+
+ /**
+ * Add a listener to receive the notification when the country is detected
+ * or changed.
+ *
+ * @param listener will be called when the country is detected or changed.
+ * @param looper a Looper object whose message queue will be used to
+ * implement the callback mechanism. If looper is null then the
+ * callbacks will be called on the main thread.
+ */
+ public void addCountryListener(CountryListener listener, Looper looper) {
+ synchronized (mListeners) {
+ if (!mListeners.containsKey(listener)) {
+ ListenerTransport transport = new ListenerTransport(listener, looper);
+ try {
+ mService.addCountryListener(transport);
+ mListeners.put(listener, transport);
+ } catch (RemoteException e) {
+ Log.e(TAG, "addCountryListener: RemoteException", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove the listener
+ */
+ public void removeCountryListener(CountryListener listener) {
+ synchronized (mListeners) {
+ ListenerTransport transport = mListeners.get(listener);
+ if (transport != null) {
+ try {
+ mListeners.remove(listener);
+ mService.removeCountryListener(transport);
+ } catch (RemoteException e) {
+ Log.e(TAG, "removeCountryListener: RemoteException", e);
+ }
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/CountryListener.java b/location/java/android/location/CountryListener.java
new file mode 100644
index 000000000000..e36db412eaec
--- /dev/null
+++ b/location/java/android/location/CountryListener.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+/**
+ * The listener for receiving the notification when the country is detected or
+ * changed
+ *
+ * @hide
+ */
+public interface CountryListener {
+ /**
+ * @param country the changed or detected country.
+ */
+ void onCountryDetected(Country country);
+}
diff --git a/location/java/android/location/ICountryDetector.aidl b/location/java/android/location/ICountryDetector.aidl
new file mode 100644
index 000000000000..6eaf07ca330d
--- /dev/null
+++ b/location/java/android/location/ICountryDetector.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.location.Country;
+import android.location.ICountryListener;
+
+/**
+ * The API for detecting the country where the user is.
+ *
+ * {@hide}
+ */
+interface ICountryDetector
+{
+ /**
+ * Start detecting the country that the user is in.
+ * @return the country if it is available immediately, otherwise null will be returned.
+ */
+ Country detectCountry();
+
+ /**
+ * Add a listener to receive the notification when the country is detected or changed.
+ */
+ void addCountryListener(in ICountryListener listener);
+
+ /**
+ * Remove the listener
+ */
+ void removeCountryListener(in ICountryListener listener);
+} \ No newline at end of file
diff --git a/location/java/android/location/ICountryListener.aidl b/location/java/android/location/ICountryListener.aidl
new file mode 100644
index 000000000000..76ecb134246f
--- /dev/null
+++ b/location/java/android/location/ICountryListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.location;
+
+import android.location.Country;
+/**
+ * {@hide}
+ */
+oneway interface ICountryListener
+{
+ void onCountryDetected(in Country country);
+}
diff --git a/location/tests/locationtests/src/android/location/CountryTester.java b/location/tests/locationtests/src/android/location/CountryTester.java
new file mode 100644
index 000000000000..9802d5a6226d
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/CountryTester.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.test.AndroidTestCase;
+
+public class CountryTester extends AndroidTestCase {
+ public void testCountryEquals() {
+ Country countryA = new Country("US", Country.COUNTRY_SOURCE_NETWORK);
+ Country countryB = new Country("US", Country.COUNTRY_SOURCE_LOCALE);
+ Country countryC = new Country("CN", Country.COUNTRY_SOURCE_LOCALE);
+ Country countryD = new Country("us", Country.COUNTRY_SOURCE_NETWORK);
+ assertTrue(countryA.equalsIgnoreSource(countryB));
+ assertFalse(countryA.equalsIgnoreSource(countryC));
+ assertFalse(countryA.equals(countryC));
+ assertTrue(countryA.equals(countryD));
+ assertTrue(countryA.hashCode() == countryD.hashCode());
+ }
+}
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index e3b9e364976e..8d9f4fee4107 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -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/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 4440447c8b95..4c3ebcae1a3b 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -2348,10 +2348,10 @@ 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;
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 3f9069fe9175..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;
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/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 f747f903e292..b982f93a50b2 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1036,8 +1036,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/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/media/tests/CameraBrowser/AndroidManifest.xml b/media/tests/CameraBrowser/AndroidManifest.xml
index 5a71179c9b05..eae0b015b916 100644
--- a/media/tests/CameraBrowser/AndroidManifest.xml
+++ b/media/tests/CameraBrowser/AndroidManifest.xml
@@ -14,6 +14,14 @@
<activity android:name="StorageBrowser" />
<activity android:name="ObjectBrowser" />
<activity android:name="ObjectViewer" />
+
+ <receiver android:name="UsbReceiver">
+ <intent-filter>
+ <action android:name="android.hardware.action.USB_CAMERA_ATTACHED" />
+ <data android:scheme="content"/>
+ </intent-filter>
+ </receiver>
+
</application>
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/UsbReceiver.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/UsbReceiver.java
new file mode 100644
index 000000000000..c05b239433b4
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/UsbReceiver.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camerabrowser;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.hardware.Usb;
+import android.net.Uri;
+import android.util.Log;
+
+public class UsbReceiver extends BroadcastReceiver
+{
+ private static final String TAG = "UsbReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "onReceive " + intent);
+ if (Usb.ACTION_USB_CAMERA_ATTACHED.equals(intent.getAction())) {
+ Uri uri = intent.getData();
+ intent = new Intent(context, StorageBrowser.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ // TODO - add a wrapper to Mtp.Device for this
+ int id = Integer.parseInt(uri.getPathSegments().get(1));
+ intent.putExtra("device", id);
+ context.startActivity(intent);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "bad device Uri " + uri);
+ }
+ }
+ }
+}
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/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/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 5771e8568ff4..458ac9deb7d4 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -24,12 +24,14 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
+import com.android.internal.view.StandaloneActionMode;
import com.android.internal.view.menu.ContextMenuBuilder;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuDialogHelper;
import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.MenuView;
import com.android.internal.view.menu.SubMenuBuilder;
+import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.ActionBarView;
import android.app.KeyguardManager;
@@ -196,9 +198,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
/* Custom title feature is enabled and the user is trying to enable another feature */
throw new AndroidRuntimeException("You cannot combine custom titles with other title features");
}
- if (featureId == FEATURE_OPENGL) {
- getAttributes().memoryType = WindowManager.LayoutParams.MEMORY_TYPE_GPU;
- }
return super.requestFeature(featureId);
}
@@ -1641,6 +1640,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private boolean mWatchingForMenu;
private int mDownY;
+ private ActionMode mActionMode;
+ private ActionBarContextView mActionModeView;
+
public DecorView(Context context, int featureId) {
super(context);
mFeatureId = featureId;
@@ -1938,7 +1940,33 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public ActionMode startActionMode(ActionMode.Callback callback) {
- return getCallback().onStartActionMode(callback);
+ if (mActionMode != null) {
+ mActionMode.finish();
+ }
+
+ ActionMode mode = getCallback().onStartActionMode(callback);
+ if (mode != null) {
+ mActionMode = mode;
+ } else {
+ if (mActionModeView == null) {
+ mActionModeView = (ActionBarContextView) findViewById(
+ com.android.internal.R.id.action_mode_bar);
+ }
+
+ if (mActionModeView != null) {
+ mode = new StandaloneActionMode(getContext(), mActionModeView,
+ new ActionModeCallbackWrapper(callback));
+ if (callback.onCreateActionMode(mode, mode.getMenu())) {
+ mode.invalidate();
+ mActionModeView.initForMode(mode);
+ mActionModeView.setVisibility(View.VISIBLE);
+ mActionMode = mode;
+ } else {
+ mActionMode = null;
+ }
+ }
+ }
+ return mActionMode;
}
public void startChanging() {
@@ -2120,6 +2148,35 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (keepOn) PhoneWindow.this.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
else PhoneWindow.this.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
+
+ /**
+ * Clears out internal reference when the action mode is destroyed.
+ */
+ private class ActionModeCallbackWrapper implements ActionMode.Callback {
+ private ActionMode.Callback mWrapped;
+
+ public ActionModeCallbackWrapper(ActionMode.Callback wrapped) {
+ mWrapped = wrapped;
+ }
+
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ return mWrapped.onCreateActionMode(mode, menu);
+ }
+
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return mWrapped.onPrepareActionMode(mode, menu);
+ }
+
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return mWrapped.onActionItemClicked(mode, item);
+ }
+
+ public void onDestroyActionMode(ActionMode mode) {
+ mWrapped.onDestroyActionMode(mode);
+ mActionModeView.removeAllViews();
+ mActionMode = null;
+ }
+ }
}
protected DecorView generateDecor() {
@@ -2173,6 +2230,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
requestFeature(FEATURE_ACTION_BAR);
}
+ if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) {
+ requestFeature(FEATURE_ACTION_MODE_OVERLAY);
+ }
+
if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN&(~getForcedWindowFlags()));
}
@@ -2255,13 +2316,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (mIsFloating) {
layoutResource = com.android.internal.R.layout.dialog_title;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
- Configuration config = getContext().getResources().getConfiguration();
- if ((config.screenLayout & Configuration.SCREENLAYOUT_SIZE_XLARGE) ==
- Configuration.SCREENLAYOUT_SIZE_XLARGE) {
- layoutResource = com.android.internal.R.layout.screen_xlarge_action_bar;
- } else {
- layoutResource = com.android.internal.R.layout.screen_action_bar;
- }
+ layoutResource = com.android.internal.R.layout.screen_action_bar;
} else {
layoutResource = com.android.internal.R.layout.screen_title;
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index b88e69d88580..3572d10f6604 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -5408,8 +5408,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 +5432,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 +5451,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 +5470,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 +5495,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 +5578,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 +5609,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 +5634,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 +5849,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 +5878,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 +5891,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 +5934,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..8f667a3251ee 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -933,7 +933,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 +1027,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 +1040,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/CountryDetectorService.java b/services/java/com/android/server/CountryDetectorService.java
new file mode 100644
index 000000000000..3081ebefd05a
--- /dev/null
+++ b/services/java/com/android/server/CountryDetectorService.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.server;
+
+import java.util.HashMap;
+
+import com.android.server.location.ComprehensiveCountryDetector;
+
+import android.content.Context;
+import android.location.Country;
+import android.location.CountryListener;
+import android.location.ICountryDetector;
+import android.location.ICountryListener;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * This class detects the country that the user is in through
+ * {@link ComprehensiveCountryDetector}.
+ *
+ * @hide
+ */
+public class CountryDetectorService extends ICountryDetector.Stub implements Runnable {
+
+ /**
+ * The class represents the remote listener, it will also removes itself
+ * from listener list when the remote process was died.
+ */
+ private final class Receiver implements IBinder.DeathRecipient {
+ private final ICountryListener mListener;
+ private final IBinder mKey;
+
+ public Receiver(ICountryListener listener) {
+ mListener = listener;
+ mKey = listener.asBinder();
+ }
+
+ public void binderDied() {
+ removeListener(mKey);
+ }
+
+ @Override
+ public boolean equals(Object otherObj) {
+ if (otherObj instanceof Receiver) {
+ return mKey.equals(((Receiver) otherObj).mKey);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mKey.hashCode();
+ }
+
+ public ICountryListener getListener() {
+ return mListener;
+ }
+ }
+
+ private final static String TAG = "CountryDetectorService";
+
+ private final HashMap<IBinder, Receiver> mReceivers;
+ private final Context mContext;
+ private ComprehensiveCountryDetector mCountryDetector;
+ private boolean mSystemReady;
+ private Handler mHandler;
+ private CountryListener mLocationBasedDetectorListener;
+
+ public CountryDetectorService(Context context) {
+ super();
+ mReceivers = new HashMap<IBinder, Receiver>();
+ mContext = context;
+ }
+
+ @Override
+ public Country detectCountry() throws RemoteException {
+ if (!mSystemReady) {
+ throw new RemoteException();
+ }
+ return mCountryDetector.detectCountry();
+ }
+
+ /**
+ * Add the ICountryListener into the listener list.
+ */
+ @Override
+ public void addCountryListener(ICountryListener listener) throws RemoteException {
+ if (!mSystemReady) {
+ throw new RemoteException();
+ }
+ addListener(listener);
+ }
+
+ /**
+ * Remove the ICountryListener from the listener list.
+ */
+ @Override
+ public void removeCountryListener(ICountryListener listener) throws RemoteException {
+ if (!mSystemReady) {
+ throw new RemoteException();
+ }
+ removeListener(listener.asBinder());
+ }
+
+ private void addListener(ICountryListener listener) {
+ synchronized (mReceivers) {
+ Receiver r = new Receiver(listener);
+ try {
+ listener.asBinder().linkToDeath(r, 0);
+ mReceivers.put(listener.asBinder(), r);
+ if (mReceivers.size() == 1) {
+ Slog.d(TAG, "The first listener is added");
+ setCountryListener(mLocationBasedDetectorListener);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "linkToDeath failed:", e);
+ }
+ }
+ }
+
+ private void removeListener(IBinder key) {
+ synchronized (mReceivers) {
+ mReceivers.remove(key);
+ if (mReceivers.isEmpty()) {
+ setCountryListener(null);
+ Slog.d(TAG, "No listener is left");
+ }
+ }
+ }
+
+
+ protected void notifyReceivers(Country country) {
+ synchronized(mReceivers) {
+ for (Receiver receiver : mReceivers.values()) {
+ try {
+ receiver.getListener().onCountryDetected(country);
+ } catch (RemoteException e) {
+ // TODO: Shall we remove the receiver?
+ Slog.e(TAG, "notifyReceivers failed:", e);
+ }
+ }
+ }
+ }
+
+ void systemReady() {
+ // Shall we wait for the initialization finish.
+ Thread thread = new Thread(this, "CountryDetectorService");
+ thread.start();
+ }
+
+ private void initialize() {
+ mCountryDetector = new ComprehensiveCountryDetector(mContext);
+ mLocationBasedDetectorListener = new CountryListener() {
+ public void onCountryDetected(final Country country) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ notifyReceivers(country);
+ }
+ });
+ }
+ };
+ }
+
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ Looper.prepare();
+ mHandler = new Handler();
+ initialize();
+ mSystemReady = true;
+ Looper.loop();
+ }
+
+ protected void setCountryListener(final CountryListener listener) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCountryDetector.setCountryListener(listener);
+ }
+ });
+ }
+
+ // For testing
+ boolean isSystemReady() {
+ return mSystemReady;
+ }
+}
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index b4f46ab55a97..9a1d0170a139 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -66,13 +66,14 @@ 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,
@@ -85,20 +86,28 @@ public class InputManager {
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;
+ // 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 +159,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) {
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 01376eb0ed85..34b1fc5130e6 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;
@@ -9549,48 +9551,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.
@@ -9806,10 +9788,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 ad910c412c56..2fb481cbc133 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -1052,6 +1052,7 @@ class PowerManagerService extends IPowerManager.Stub
case SCREEN_DIM:
if (mDimDelay >= 0) {
when = now + mDimDelay;
+ break;
} else {
Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim");
}
@@ -1060,6 +1061,9 @@ class PowerManagerService extends IPowerManager.Stub
when = now + mScreenOffDelay;
}
break;
+ default:
+ when = now;
+ break;
}
} else {
override: {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3586d21df75e..2412b7d860a0 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -206,6 +206,7 @@ class ServerThread extends Thread {
NotificationManagerService notification = null;
WallpaperManagerService wallpaper = null;
LocationManagerService location = null;
+ CountryDetectorService countryDetector = null;
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
try {
@@ -316,6 +317,14 @@ class ServerThread extends Thread {
}
try {
+ Slog.i(TAG, "Country Detector");
+ countryDetector = new CountryDetectorService(context);
+ ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting Country Detector", e);
+ }
+
+ try {
Slog.i(TAG, "Search Service");
ServiceManager.addService(Context.SEARCH_SERVICE,
new SearchManagerService(context));
@@ -479,6 +488,7 @@ class ServerThread extends Thread {
final InputMethodManagerService immF = imm;
final RecognitionManagerService recognitionF = recognition;
final LocationManagerService locationF = location;
+ final CountryDetectorService countryDetectorF = countryDetector;
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
@@ -506,6 +516,7 @@ class ServerThread extends Thread {
if (wallpaperF != null) wallpaperF.systemReady();
if (immF != null) immF.systemReady();
if (locationF != null) locationF.systemReady();
+ if (countryDetectorF != null) countryDetectorF.systemReady();
if (throttleF != null) throttleF.systemReady();
}
});
diff --git a/services/java/com/android/server/UsbObserver.java b/services/java/com/android/server/UsbObserver.java
index d08fe9b3964b..546e5f8d0242 100644
--- a/services/java/com/android/server/UsbObserver.java
+++ b/services/java/com/android/server/UsbObserver.java
@@ -24,6 +24,7 @@ import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.UEventObserver;
+import android.provider.Mtp;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
@@ -147,8 +148,43 @@ class UsbObserver extends UEventObserver {
}
}
+ private native void monitorUsbHostBus();
+
+ // called from JNI in monitorUsbHostBus()
+ private void usbCameraAdded(int deviceID) {
+ Intent intent = new Intent(Usb.ACTION_USB_CAMERA_ATTACHED,
+ Mtp.Device.getContentUri(deviceID));
+ Log.d(TAG, "usbCameraAdded, sending " + intent);
+ mContext.sendBroadcast(intent);
+ }
+
+ // called from JNI in monitorUsbHostBus()
+ private void usbCameraRemoved(int deviceID) {
+ Intent intent = new Intent(Usb.ACTION_USB_CAMERA_DETACHED,
+ Mtp.Device.getContentUri(deviceID));
+ Log.d(TAG, "usbCameraRemoved, sending " + intent);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void initHostSupport() {
+ // Create a thread to call into native code to wait for USB host events.
+ // This thread will call us back on usbCameraAdded and usbCameraRemoved.
+ Runnable runnable = new Runnable() {
+ public void run() {
+ monitorUsbHostBus();
+ }
+ };
+ new Thread(null, runnable, "UsbObserver host thread").start();
+ }
+
void systemReady() {
synchronized (this) {
+ if (mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_hasUsbHostSupport)) {
+ // start monitoring for connected USB devices
+ initHostSupport();
+ }
+
update();
mSystemReady = true;
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index b82ec0197cb8..1a8efa14f798 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -4306,7 +4306,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) {
@@ -4314,7 +4314,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) {
@@ -4322,7 +4322,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) {
@@ -4330,7 +4330,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) {
@@ -4338,7 +4338,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) {
@@ -4346,7 +4346,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) {
@@ -4354,7 +4354,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) {
@@ -4362,7 +4362,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) {
@@ -4370,7 +4370,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) {
@@ -4378,11 +4378,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() {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index fa2ec1fd1327..440ebbd84c85 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6173,7 +6173,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;
@@ -6188,18 +6188,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;
@@ -6207,8 +6221,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;
}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index d27ccc00a824..acedd2617bcc 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;
@@ -136,7 +136,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);
@@ -429,10 +429,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/ComprehensiveCountryDetector.java b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
new file mode 100755
index 000000000000..e692f8d94bfd
--- /dev/null
+++ b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.location;
+
+import java.util.Locale;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import android.content.Context;
+import android.location.Country;
+import android.location.CountryListener;
+import android.location.Geocoder;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Slog;
+
+/**
+ * This class is used to detect the country where the user is. The sources of
+ * country are queried in order of reliability, like
+ * <ul>
+ * <li>Mobile network</li>
+ * <li>Location</li>
+ * <li>SIM's country</li>
+ * <li>Phone's locale</li>
+ * </ul>
+ * <p>
+ * Call the {@link #detectCountry()} to get the available country immediately.
+ * <p>
+ * To be notified of the future country change, using the
+ * {@link #setCountryListener(CountryListener)}
+ * <p>
+ * Using the {@link #stop()} to stop listening to the country change.
+ * <p>
+ * The country information will be refreshed every
+ * {@link #LOCATION_REFRESH_INTERVAL} once the location based country is used.
+ *
+ * @hide
+ */
+public class ComprehensiveCountryDetector extends CountryDetectorBase {
+
+ private final static String TAG = "ComprehensiveCountryDetector";
+
+ /**
+ * The refresh interval when the location based country was used
+ */
+ private final static long LOCATION_REFRESH_INTERVAL = 1000 * 60 * 60 * 24; // 1 day
+
+ protected CountryDetectorBase mLocationBasedCountryDetector;
+ protected Timer mLocationRefreshTimer;
+
+ private final int mPhoneType;
+ private Country mCountry;
+ private TelephonyManager mTelephonyManager;
+ private Country mCountryFromLocation;
+ private boolean mStopped = false;
+ private ServiceState mLastState;
+
+ private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ // TODO: Find out how often we will be notified, if this method is called too
+ // many times, let's consider querying the network.
+ Slog.d(TAG, "onServiceStateChanged");
+ // We only care the state change
+ if (mLastState == null || mLastState.getState() != serviceState.getState()) {
+ detectCountry(true, true);
+ mLastState = new ServiceState(serviceState);
+ }
+ }
+ };
+
+ /**
+ * The listener for receiving the notification from LocationBasedCountryDetector.
+ */
+ private CountryListener mLocationBasedCountryDetectionListener = new CountryListener() {
+ public void onCountryDetected(Country country) {
+ mCountryFromLocation = country;
+ // Don't start the LocationBasedCountryDetector.
+ detectCountry(true, false);
+ stopLocationBasedDetector();
+ }
+ };
+
+ public ComprehensiveCountryDetector(Context context) {
+ super(context);
+ mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ mPhoneType = mTelephonyManager.getPhoneType();
+ }
+
+ @Override
+ public Country detectCountry() {
+ // Don't start the LocationBasedCountryDetector if we have been stopped.
+ return detectCountry(false, !mStopped);
+ }
+
+ @Override
+ public void stop() {
+ Slog.i(TAG, "Stop the detector.");
+ cancelLocationRefresh();
+ removePhoneStateListener();
+ stopLocationBasedDetector();
+ mListener = null;
+ mStopped = true;
+ }
+
+ /**
+ * Get the country from different sources in order of the reliability.
+ */
+ private Country getCountry() {
+ Country result = null;
+ result = getNetworkBasedCountry();
+ if (result == null) {
+ result = getLastKnownLocationBasedCountry();
+ }
+ if (result == null) {
+ result = getSimBasedCountry();
+ }
+ if (result == null) {
+ result = getLocaleCountry();
+ }
+ return result;
+ }
+
+ /**
+ * @return the country from the mobile network.
+ */
+ protected Country getNetworkBasedCountry() {
+ String countryIso = null;
+ // TODO: The document says the result may be unreliable on CDMA networks. Shall we use
+ // it on CDMA phone? We may test the Android primarily used countries.
+ if (mPhoneType == TelephonyManager.PHONE_TYPE_GSM) {
+ countryIso = mTelephonyManager.getNetworkCountryIso();
+ if (!TextUtils.isEmpty(countryIso)) {
+ return new Country(countryIso, Country.COUNTRY_SOURCE_NETWORK);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the cached location based country.
+ */
+ protected Country getLastKnownLocationBasedCountry() {
+ return mCountryFromLocation;
+ }
+
+ /**
+ * @return the country from SIM card
+ */
+ protected Country getSimBasedCountry() {
+ String countryIso = null;
+ countryIso = mTelephonyManager.getSimCountryIso();
+ if (!TextUtils.isEmpty(countryIso)) {
+ return new Country(countryIso, Country.COUNTRY_SOURCE_SIM);
+ }
+ return null;
+ }
+
+ /**
+ * @return the country from the system's locale.
+ */
+ protected Country getLocaleCountry() {
+ Locale defaultLocale = Locale.getDefault();
+ if (defaultLocale != null) {
+ return new Country(defaultLocale.getCountry(), Country.COUNTRY_SOURCE_LOCALE);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @param notifyChange indicates whether the listener should be notified the change of the
+ * country
+ * @param startLocationBasedDetection indicates whether the LocationBasedCountryDetector could
+ * be started if the current country source is less reliable than the location.
+ * @return the current available UserCountry
+ */
+ private Country detectCountry(boolean notifyChange, boolean startLocationBasedDetection) {
+ Country country = getCountry();
+ runAfterDetectionAsync(mCountry != null ? new Country(mCountry) : mCountry, country,
+ notifyChange, startLocationBasedDetection);
+ mCountry = country;
+ return mCountry;
+ }
+
+ /**
+ * Run the tasks in the service's thread.
+ */
+ protected void runAfterDetectionAsync(final Country country, final Country detectedCountry,
+ final boolean notifyChange, final boolean startLocationBasedDetection) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ runAfterDetection(
+ country, detectedCountry, notifyChange, startLocationBasedDetection);
+ }
+ });
+ }
+
+ @Override
+ public void setCountryListener(CountryListener listener) {
+ CountryListener prevListener = mListener;
+ mListener = listener;
+ if (mListener == null) {
+ // Stop listening all services
+ removePhoneStateListener();
+ stopLocationBasedDetector();
+ cancelLocationRefresh();
+ } else if (prevListener == null) {
+ addPhoneStateListener();
+ detectCountry(false, true);
+ }
+ }
+
+ void runAfterDetection(final Country country, final Country detectedCountry,
+ final boolean notifyChange, final boolean startLocationBasedDetection) {
+ if (notifyChange) {
+ notifyIfCountryChanged(country, detectedCountry);
+ }
+ if (startLocationBasedDetection && (detectedCountry == null
+ || detectedCountry.getSource() > Country.COUNTRY_SOURCE_LOCATION)
+ && isAirplaneModeOff() && mListener != null && isGeoCoderImplemented()) {
+ // Start finding location when the source is less reliable than the
+ // location and the airplane mode is off (as geocoder will not
+ // work).
+ // TODO : Shall we give up starting the detector within a
+ // period of time?
+ startLocationBasedDetector(mLocationBasedCountryDetectionListener);
+ }
+ if (detectedCountry == null
+ || detectedCountry.getSource() >= Country.COUNTRY_SOURCE_LOCATION) {
+ // Schedule the location refresh if the country source is
+ // not more reliable than the location or no country is
+ // found.
+ // TODO: Listen to the preference change of GPS, Wifi etc,
+ // and start detecting the country.
+ scheduleLocationRefresh();
+ } else {
+ // Cancel the location refresh once the current source is
+ // more reliable than the location.
+ cancelLocationRefresh();
+ stopLocationBasedDetector();
+ }
+ }
+
+ /**
+ * Find the country from LocationProvider.
+ */
+ private synchronized void startLocationBasedDetector(CountryListener listener) {
+ if (mLocationBasedCountryDetector != null) {
+ return;
+ }
+ mLocationBasedCountryDetector = createLocationBasedCountryDetector();
+ mLocationBasedCountryDetector.setCountryListener(listener);
+ mLocationBasedCountryDetector.detectCountry();
+ }
+
+ private synchronized void stopLocationBasedDetector() {
+ if (mLocationBasedCountryDetector != null) {
+ mLocationBasedCountryDetector.stop();
+ mLocationBasedCountryDetector = null;
+ }
+ }
+
+ protected CountryDetectorBase createLocationBasedCountryDetector() {
+ return new LocationBasedCountryDetector(mContext);
+ }
+
+ protected boolean isAirplaneModeOff() {
+ return Settings.System.getInt(
+ mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 0;
+ }
+
+ /**
+ * Notify the country change.
+ */
+ private void notifyIfCountryChanged(final Country country, final Country detectedCountry) {
+ if (detectedCountry != null && mListener != null
+ && (country == null || !country.equals(detectedCountry))) {
+ Slog.d(TAG,
+ "The country was changed from " + country != null ? country.getCountryIso() :
+ country + " to " + detectedCountry.getCountryIso());
+ notifyListener(detectedCountry);
+ }
+ }
+
+ /**
+ * Schedule the next location refresh. We will do nothing if the scheduled task exists.
+ */
+ private synchronized void scheduleLocationRefresh() {
+ if (mLocationRefreshTimer != null) return;
+ mLocationRefreshTimer = new Timer();
+ mLocationRefreshTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ mLocationRefreshTimer = null;
+ detectCountry(false, true);
+ }
+ }, LOCATION_REFRESH_INTERVAL);
+ }
+
+ /**
+ * Cancel the scheduled refresh task if it exists
+ */
+ private synchronized void cancelLocationRefresh() {
+ if (mLocationRefreshTimer != null) {
+ mLocationRefreshTimer.cancel();
+ mLocationRefreshTimer = null;
+ }
+ }
+
+ protected synchronized void addPhoneStateListener() {
+ if (mPhoneStateListener == null && mPhoneType == TelephonyManager.PHONE_TYPE_GSM) {
+ mLastState = null;
+ mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ // TODO: Find out how often we will be notified, if this
+ // method is called too
+ // many times, let's consider querying the network.
+ Slog.d(TAG, "onServiceStateChanged");
+ // We only care the state change
+ if (mLastState == null || mLastState.getState() != serviceState.getState()) {
+ detectCountry(true, true);
+ mLastState = new ServiceState(serviceState);
+ }
+ }
+ };
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+ }
+ }
+
+ protected synchronized void removePhoneStateListener() {
+ if (mPhoneStateListener != null) {
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ mPhoneStateListener = null;
+ }
+ }
+
+ protected boolean isGeoCoderImplemented() {
+ return Geocoder.isImplemented();
+ }
+}
diff --git a/services/java/com/android/server/location/CountryDetectorBase.java b/services/java/com/android/server/location/CountryDetectorBase.java
new file mode 100644
index 000000000000..8326ef949858
--- /dev/null
+++ b/services/java/com/android/server/location/CountryDetectorBase.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.location.Country;
+import android.location.CountryListener;
+import android.os.Handler;
+
+/**
+ * This class defines the methods need to be implemented by the country
+ * detector.
+ * <p>
+ * Calling {@link #detectCountry} to start detecting the country. The country
+ * could be returned immediately if it is available.
+ *
+ * @hide
+ */
+public abstract class CountryDetectorBase {
+ protected final Handler mHandler;
+ protected final Context mContext;
+ protected CountryListener mListener;
+ protected Country mDetectedCountry;
+
+ public CountryDetectorBase(Context ctx) {
+ mContext = ctx;
+ mHandler = new Handler();
+ }
+
+ /**
+ * Start detecting the country that the user is in.
+ *
+ * @return the country if it is available immediately, otherwise null should
+ * be returned.
+ */
+ public abstract Country detectCountry();
+
+ /**
+ * Register a listener to receive the notification when the country is detected or changed.
+ * <p>
+ * The previous listener will be replaced if it exists.
+ */
+ public void setCountryListener(CountryListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Stop detecting the country. The detector should release all system services and be ready to
+ * be freed
+ */
+ public abstract void stop();
+
+ protected void notifyListener(Country country) {
+ if (mListener != null) {
+ mListener.onCountryDetected(country);
+ }
+ }
+}
diff --git a/services/java/com/android/server/location/LocationBasedCountryDetector.java b/services/java/com/android/server/location/LocationBasedCountryDetector.java
new file mode 100755
index 000000000000..139f05d211fd
--- /dev/null
+++ b/services/java/com/android/server/location/LocationBasedCountryDetector.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.location;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import android.content.Context;
+import android.location.Address;
+import android.location.Country;
+import android.location.Geocoder;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.util.Slog;
+
+/**
+ * This class detects which country the user currently is in through the enabled
+ * location providers and the GeoCoder
+ * <p>
+ * Use {@link #detectCountry} to start querying. If the location can not be
+ * resolved within the given time, the last known location will be used to get
+ * the user country through the GeoCoder. The IllegalStateException will be
+ * thrown if there is a ongoing query.
+ * <p>
+ * The current query can be stopped by {@link #stop()}
+ *
+ * @hide
+ */
+public class LocationBasedCountryDetector extends CountryDetectorBase {
+ private final static String TAG = "LocationBasedCountryDetector";
+ private final static long QUERY_LOCATION_TIMEOUT = 1000 * 60 * 5; // 5 mins
+
+ /**
+ * Used for canceling location query
+ */
+ protected Timer mTimer;
+
+ /**
+ * The thread to query the country from the GeoCoder.
+ */
+ protected Thread mQueryThread;
+ protected List<LocationListener> mLocationListeners;
+
+ private LocationManager mLocationManager;
+ private List<String> mEnabledProviders;
+
+ public LocationBasedCountryDetector(Context ctx) {
+ super(ctx);
+ mLocationManager = (LocationManager) ctx.getSystemService(Context.LOCATION_SERVICE);
+ }
+
+ /**
+ * @return the ISO 3166-1 two letters country code from the location
+ */
+ protected String getCountryFromLocation(Location location) {
+ String country = null;
+ Geocoder geoCoder = new Geocoder(mContext);
+ try {
+ List<Address> addresses = geoCoder.getFromLocation(
+ location.getLatitude(), location.getLongitude(), 1);
+ if (addresses != null && addresses.size() > 0) {
+ country = addresses.get(0).getCountryCode();
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Exception occurs when getting country from location");
+ }
+ return country;
+ }
+
+ /**
+ * Register the listeners with the location providers
+ */
+ protected void registerEnabledProviders(List<LocationListener> listeners) {
+ int total = listeners.size();
+ for (int i = 0; i< total; i++) {
+ mLocationManager.requestLocationUpdates(
+ mEnabledProviders.get(i), 0, 0, listeners.get(i));
+ }
+ }
+
+ /**
+ * Unregister the listeners with the location providers
+ */
+ protected void unregisterProviders(List<LocationListener> listeners) {
+ for (LocationListener listener : listeners) {
+ mLocationManager.removeUpdates(listener);
+ }
+ }
+
+ /**
+ * @return the last known location from all providers
+ */
+ protected Location getLastKnownLocation() {
+ List<String> providers = mLocationManager.getAllProviders();
+ Location bestLocation = null;
+ for (String provider : providers) {
+ Location lastKnownLocation = mLocationManager.getLastKnownLocation(provider);
+ if (lastKnownLocation != null) {
+ if (bestLocation == null || bestLocation.getTime() < lastKnownLocation.getTime()) {
+ bestLocation = lastKnownLocation;
+ }
+ }
+ }
+ return bestLocation;
+ }
+
+ /**
+ * @return the timeout for querying the location.
+ */
+ protected long getQueryLocationTimeout() {
+ return QUERY_LOCATION_TIMEOUT;
+ }
+
+ /**
+ * @return the total number of enabled location providers
+ */
+ protected int getTotalEnabledProviders() {
+ if (mEnabledProviders == null) {
+ mEnabledProviders = mLocationManager.getProviders(true);
+ }
+ return mEnabledProviders.size();
+ }
+
+ /**
+ * Start detecting the country.
+ * <p>
+ * Queries the location from all location providers, then starts a thread to query the
+ * country from GeoCoder.
+ */
+ @Override
+ public synchronized Country detectCountry() {
+ if (mLocationListeners != null) {
+ throw new IllegalStateException();
+ }
+ // Request the location from all enabled providers.
+ int totalProviders = getTotalEnabledProviders();
+ if (totalProviders > 0) {
+ mLocationListeners = new ArrayList<LocationListener>(totalProviders);
+ for (int i = 0; i < totalProviders; i++) {
+ LocationListener listener = new LocationListener () {
+ public void onLocationChanged(Location location) {
+ if (location != null) {
+ LocationBasedCountryDetector.this.stop();
+ queryCountryCode(location);
+ }
+ }
+ public void onProviderDisabled(String provider) {
+ }
+ public void onProviderEnabled(String provider) {
+ }
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+ };
+ mLocationListeners.add(listener);
+ }
+ registerEnabledProviders(mLocationListeners);
+ mTimer = new Timer();
+ mTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ mTimer = null;
+ LocationBasedCountryDetector.this.stop();
+ // Looks like no provider could provide the location, let's try the last
+ // known location.
+ queryCountryCode(getLastKnownLocation());
+ }
+ }, getQueryLocationTimeout());
+ } else {
+ // There is no provider enabled.
+ queryCountryCode(getLastKnownLocation());
+ }
+ return mDetectedCountry;
+ }
+
+ /**
+ * Stop the current query without notifying the listener.
+ */
+ @Override
+ public synchronized void stop() {
+ if (mLocationListeners != null) {
+ unregisterProviders(mLocationListeners);
+ mLocationListeners = null;
+ }
+ if (mTimer != null) {
+ mTimer.cancel();
+ mTimer = null;
+ }
+ }
+
+ /**
+ * Start a new thread to query the country from Geocoder.
+ */
+ private synchronized void queryCountryCode(final Location location) {
+ if (location == null) {
+ notifyListener(null);
+ return;
+ }
+ if (mQueryThread != null) return;
+ mQueryThread = new Thread(new Runnable() {
+ public void run() {
+ String countryIso = null;
+ if (location != null) {
+ countryIso = getCountryFromLocation(location);
+ }
+ if (countryIso != null) {
+ mDetectedCountry = new Country(countryIso, Country.COUNTRY_SOURCE_LOCATION);
+ } else {
+ mDetectedCountry = null;
+ }
+ notifyListener(mDetectedCountry);
+ mQueryThread = null;
+ }
+ });
+ mQueryThread.start();
+ }
+}
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index cdc0a6f8fadc..459551d8e807 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -8,6 +8,7 @@ LOCAL_SRC_FILES:= \
com_android_server_LightsService.cpp \
com_android_server_PowerManagerService.cpp \
com_android_server_SystemServer.cpp \
+ com_android_server_UsbObserver.cpp \
com_android_server_VibratorService.cpp \
com_android_server_location_GpsLocationProvider.cpp \
onload.cpp
@@ -25,6 +26,8 @@ LOCAL_SHARED_LIBRARIES := \
libutils \
libui
+LOCAL_STATIC_LIBRARIES := libusbhost
+
ifeq ($(TARGET_SIMULATOR),true)
ifeq ($(TARGET_OS),linux)
ifeq ($(TARGET_ARCH),x86)
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 0992b33f877d..a332376766e6 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -219,11 +219,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 +342,7 @@ private:
InputApplication* mFocusedApplication;
InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication
+ void dumpDeviceInfo(String8& dump);
void dumpDispatchStateLd(String8& dump);
void logDispatchStateLd();
@@ -409,12 +409,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 +570,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 +605,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 +636,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 +668,20 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
return actions;
}
-int32_t NativeInputManager::interceptTouch(nsecs_t when) {
+int32_t NativeInputManager::interceptGeneric(nsecs_t when, uint32_t& policyFlags) {
#if DEBUG_INPUT_READER_POLICY
- LOGD("interceptTouch - when=%lld", when);
+ LOGD("interceptGeneric - when=%lld, policyFlags=0x%x", when, policyFlags);
#endif
int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
if (isScreenOn()) {
- // Only dispatch touch 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;
- }
- }
-
- return actions;
-}
-
-int32_t NativeInputManager::interceptTrackball(nsecs_t when,
- bool buttonChanged, bool buttonDown, bool rolled) {
-#if DEBUG_INPUT_READER_POLICY
- LOGD("interceptTrackball - when=%lld, buttonChanged=%d, buttonDown=%d, rolled=%d",
- when, buttonChanged, buttonDown, rolled);
-#endif
-
- int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
- if (isScreenOn()) {
- // Only dispatch trackball 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 +689,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 +1706,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 +1937,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 +1976,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;
}
@@ -2102,7 +2142,7 @@ 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 },
diff --git a/services/jni/com_android_server_UsbObserver.cpp b/services/jni/com_android_server_UsbObserver.cpp
new file mode 100644
index 000000000000..7c478d56956f
--- /dev/null
+++ b/services/jni/com_android_server_UsbObserver.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "UsbObserver"
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "utils/Vector.h"
+
+#include <usbhost/usbhost.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
+#include <linux/usb/ch9.h>
+#else
+#include <linux/usb_ch9.h>
+#endif
+
+#include <stdio.h>
+
+namespace android
+{
+
+static jmethodID method_usbCameraAdded;
+static jmethodID method_usbCameraRemoved;
+
+Vector<int> mDeviceList;
+
+static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+ if (env->ExceptionCheck()) {
+ LOGE("An exception was thrown by callback '%s'.", methodName);
+ LOGE_EX(env);
+ env->ExceptionClear();
+ }
+}
+
+static int usb_device_added(const char *devname, void* client_data) {
+ // check to see if it is a camera
+ struct usb_descriptor_header* desc;
+ struct usb_descriptor_iter iter;
+
+ struct usb_device *device = usb_device_open(devname);
+ if (!device) {
+ LOGE("usb_device_open failed\n");
+ return 0;
+ }
+
+ usb_descriptor_iter_init(device, &iter);
+
+ while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
+ if (desc->bDescriptorType == USB_DT_INTERFACE) {
+ struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
+
+ if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
+ interface->bInterfaceSubClass == 1 && // Still Image Capture
+ interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470)
+ {
+ LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
+ usb_device_get_product_name(device));
+
+ // interface should be followed by three endpoints
+ struct usb_endpoint_descriptor *ep;
+ struct usb_endpoint_descriptor *ep_in_desc = NULL;
+ struct usb_endpoint_descriptor *ep_out_desc = NULL;
+ struct usb_endpoint_descriptor *ep_intr_desc = NULL;
+ for (int i = 0; i < 3; i++) {
+ ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
+ if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
+ LOGE("endpoints not found\n");
+ goto done;
+ }
+ if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
+ if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ ep_in_desc = ep;
+ else
+ ep_out_desc = ep;
+ } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
+ ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+ ep_intr_desc = ep;
+ }
+ }
+ if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
+ LOGE("endpoints not found\n");
+ goto done;
+ }
+
+ // if we got here, we found a camera
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject thiz = (jobject)client_data;
+
+ int id = usb_device_get_unique_id_from_name(devname);
+ mDeviceList.add(id);
+
+ env->CallVoidMethod(thiz, method_usbCameraAdded, id);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ }
+ }
+ }
+done:
+ usb_device_close(device);
+ return 0;
+}
+
+static int usb_device_removed(const char *devname, void* client_data) {
+ int id = usb_device_get_unique_id_from_name(devname);
+
+ // see if it is a device we know about
+ for (int i = 0; i < mDeviceList.size(); i++) {
+ if (id == mDeviceList[i]) {
+ mDeviceList.removeAt(i);
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject thiz = (jobject)client_data;
+
+ env->CallVoidMethod(thiz, method_usbCameraRemoved, id);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ break;
+ }
+ }
+ return 0;
+}
+
+static void android_server_UsbObserver_monitorUsbHostBus(JNIEnv *env, jobject thiz)
+{
+ struct usb_host_context* context = usb_host_init();
+ if (!context) {
+ LOGE("usb_host_init failed");
+ return;
+ }
+ // this will never return so it is safe to pass thiz directly
+ usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
+}
+
+static JNINativeMethod method_table[] = {
+ { "monitorUsbHostBus", "()V", (void*)android_server_UsbObserver_monitorUsbHostBus }
+};
+
+int register_android_server_UsbObserver(JNIEnv *env)
+{
+ jclass clazz = env->FindClass("com/android/server/UsbObserver");
+ if (clazz == NULL) {
+ LOGE("Can't find com/android/server/UsbObserver");
+ return -1;
+ }
+ method_usbCameraAdded = env->GetMethodID(clazz, "usbCameraAdded", "(I)V");
+ if (method_usbCameraAdded == NULL) {
+ LOGE("Can't find usbCameraAdded");
+ return -1;
+ }
+ method_usbCameraRemoved = env->GetMethodID(clazz, "usbCameraRemoved", "(I)V");
+ if (method_usbCameraRemoved == NULL) {
+ LOGE("Can't find usbCameraRemoved");
+ return -1;
+ }
+
+ return jniRegisterNativeMethods(env, "com/android/server/UsbObserver",
+ method_table, NELEM(method_table));
+}
+
+};
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index cd4f0a46c517..3502aca68be9 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -9,6 +9,7 @@ int register_android_server_BatteryService(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
+int register_android_server_UsbObserver(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
@@ -32,6 +33,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
register_android_server_LightsService(env);
register_android_server_AlarmManagerService(env);
register_android_server_BatteryService(env);
+ register_android_server_UsbObserver(env);
register_android_server_VibratorService(env);
register_android_server_SystemServer(env);
register_android_server_location_GpsLocationProvider(env);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 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/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java
new file mode 100644
index 000000000000..17a1585614b1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.location.Country;
+import android.location.CountryListener;
+import android.location.ICountryListener;
+import android.os.RemoteException;
+import android.test.AndroidTestCase;
+
+public class CountryDetectorServiceTest extends AndroidTestCase {
+ private class CountryListenerTester extends ICountryListener.Stub {
+ private Country mCountry;
+
+ @Override
+ public void onCountryDetected(Country country) throws RemoteException {
+ mCountry = country;
+ }
+
+ public Country getCountry() {
+ return mCountry;
+ }
+
+ public boolean isNotified() {
+ return mCountry != null;
+ }
+ }
+
+ private class CountryDetectorServiceTester extends CountryDetectorService {
+
+ private CountryListener mListener;
+
+ public CountryDetectorServiceTester(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void notifyReceivers(Country country) {
+ super.notifyReceivers(country);
+ }
+
+ @Override
+ protected void setCountryListener(final CountryListener listener) {
+ mListener = listener;
+ }
+
+ public boolean isListenerSet() {
+ return mListener != null;
+ }
+ }
+
+ public void testAddRemoveListener() throws RemoteException {
+ CountryDetectorServiceTester serviceTester = new CountryDetectorServiceTester(getContext());
+ serviceTester.systemReady();
+ waitForSystemReady(serviceTester);
+ CountryListenerTester listenerTester = new CountryListenerTester();
+ serviceTester.addCountryListener(listenerTester);
+ assertTrue(serviceTester.isListenerSet());
+ serviceTester.removeCountryListener(listenerTester);
+ assertFalse(serviceTester.isListenerSet());
+ }
+
+ public void testNotifyListeners() throws RemoteException {
+ CountryDetectorServiceTester serviceTester = new CountryDetectorServiceTester(getContext());
+ CountryListenerTester listenerTesterA = new CountryListenerTester();
+ CountryListenerTester listenerTesterB = new CountryListenerTester();
+ Country country = new Country("US", Country.COUNTRY_SOURCE_NETWORK);
+ serviceTester.systemReady();
+ waitForSystemReady(serviceTester);
+ serviceTester.addCountryListener(listenerTesterA);
+ serviceTester.addCountryListener(listenerTesterB);
+ serviceTester.notifyReceivers(country);
+ assertTrue(serviceTester.isListenerSet());
+ assertTrue(listenerTesterA.isNotified());
+ assertTrue(listenerTesterB.isNotified());
+ serviceTester.removeCountryListener(listenerTesterA);
+ serviceTester.removeCountryListener(listenerTesterB);
+ assertFalse(serviceTester.isListenerSet());
+ }
+
+ private void waitForSystemReady(CountryDetectorService service) {
+ int count = 5;
+ while (count-- > 0) {
+ try {
+ Thread.sleep(500);
+ } catch (Exception e) {
+ }
+ if (service.isSystemReady()) {
+ return;
+ }
+ }
+ throw new RuntimeException("Wait System Ready timeout");
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/ComprehensiveCountryDetectorTest.java b/services/tests/servicestests/src/com/android/server/location/ComprehensiveCountryDetectorTest.java
new file mode 100644
index 000000000000..98966c032d14
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/ComprehensiveCountryDetectorTest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.location;
+
+import android.location.Country;
+import android.location.CountryListener;
+import android.test.AndroidTestCase;
+
+public class ComprehensiveCountryDetectorTest extends AndroidTestCase {
+ private class TestCountryDetector extends ComprehensiveCountryDetector {
+ public final static String COUNTRY_ISO = "us";
+ private boolean mLocationBasedDetectorStarted;
+ private boolean mLocationBasedDetectorStopped;
+ protected boolean mNotified;
+ private boolean listenerAdded = false;
+
+ private Country mNotifiedCountry;
+ public TestCountryDetector() {
+ super(getContext());
+ }
+
+ public void notifyLocationBasedListener(Country country) {
+ mNotified = true;
+ mNotifiedCountry = country;
+ mLocationBasedCountryDetector.notifyListener(country);
+ }
+
+ public boolean locationBasedDetectorStarted() {
+ return mLocationBasedCountryDetector != null && mLocationBasedDetectorStarted;
+ }
+
+ public boolean locationBasedDetectorStopped() {
+ return mLocationBasedCountryDetector == null && mLocationBasedDetectorStopped;
+ }
+
+ public boolean locationRefreshStarted() {
+ return mLocationRefreshTimer != null;
+ }
+
+ public boolean locationRefreshCancelled() {
+ return mLocationRefreshTimer == null;
+ }
+
+ @Override
+ protected CountryDetectorBase createLocationBasedCountryDetector() {
+ return new CountryDetectorBase(mContext) {
+ @Override
+ public Country detectCountry() {
+ mLocationBasedDetectorStarted = true;
+ return null;
+ }
+
+ @Override
+ public void stop() {
+ mLocationBasedDetectorStopped = true;
+ }
+ };
+ }
+
+ @Override
+ protected Country getNetworkBasedCountry() {
+ return null;
+ }
+
+ @Override
+ protected Country getLastKnownLocationBasedCountry() {
+ return mNotifiedCountry;
+ }
+
+ @Override
+ protected Country getSimBasedCountry() {
+ return null;
+ }
+
+ @Override
+ protected Country getLocaleCountry() {
+ return null;
+ }
+
+ @Override
+ protected void runAfterDetectionAsync(final Country country, final Country detectedCountry,
+ final boolean notifyChange, final boolean startLocationBasedDetection) {
+ runAfterDetection(country, detectedCountry, notifyChange, startLocationBasedDetection);
+ };
+
+ @Override
+ protected boolean isAirplaneModeOff() {
+ return true;
+ }
+
+ @Override
+ protected synchronized void addPhoneStateListener() {
+ listenerAdded = true;
+ }
+
+ @Override
+ protected synchronized void removePhoneStateListener() {
+ listenerAdded = false;
+ }
+
+ @Override
+ protected boolean isGeoCoderImplemented() {
+ return true;
+ }
+
+ public boolean isPhoneStateListenerAdded() {
+ return listenerAdded;
+ }
+ }
+
+ private class CountryListenerImpl implements CountryListener {
+ private boolean mNotified;
+ private Country mCountry;
+
+ public void onCountryDetected(Country country) {
+ mNotified = true;
+ mCountry = country;
+ }
+
+ public boolean notified() {
+ return mNotified;
+ }
+
+ public Country getCountry() {
+ return mCountry;
+ }
+ }
+
+ public void testDetectNetworkBasedCountry() {
+ final Country resultCountry = new Country(
+ TestCountryDetector.COUNTRY_ISO, Country.COUNTRY_SOURCE_NETWORK);
+ TestCountryDetector countryDetector = new TestCountryDetector() {
+ @Override
+ protected Country getNetworkBasedCountry() {
+ return resultCountry;
+ }
+ };
+ CountryListenerImpl listener = new CountryListenerImpl();
+ countryDetector.setCountryListener(listener);
+ Country country = countryDetector.detectCountry();
+ assertTrue(sameCountry(country, resultCountry));
+ assertFalse(listener.notified());
+ assertFalse(countryDetector.locationBasedDetectorStarted());
+ assertFalse(countryDetector.locationRefreshStarted());
+ countryDetector.stop();
+ }
+
+ public void testDetectLocationBasedCountry() {
+ final Country resultCountry = new Country(
+ TestCountryDetector.COUNTRY_ISO, Country.COUNTRY_SOURCE_SIM);
+ final Country locationBasedCountry = new Country(
+ TestCountryDetector.COUNTRY_ISO, Country.COUNTRY_SOURCE_LOCATION);
+ TestCountryDetector countryDetector = new TestCountryDetector() {
+ @Override
+ protected Country getSimBasedCountry() {
+ return resultCountry;
+ }
+ };
+ CountryListenerImpl listener = new CountryListenerImpl();
+ countryDetector.setCountryListener(listener);
+ Country country = countryDetector.detectCountry();
+ assertTrue(sameCountry(country, resultCountry));
+ assertTrue(countryDetector.locationBasedDetectorStarted());
+ countryDetector.notifyLocationBasedListener(locationBasedCountry);
+ assertTrue(listener.notified());
+ assertTrue(sameCountry(listener.getCountry(), locationBasedCountry));
+ assertTrue(countryDetector.locationBasedDetectorStopped());
+ assertTrue(countryDetector.locationRefreshStarted());
+ countryDetector.stop();
+ assertTrue(countryDetector.locationRefreshCancelled());
+ }
+
+ public void testLocaleBasedCountry() {
+ final Country resultCountry = new Country(
+ TestCountryDetector.COUNTRY_ISO, Country.COUNTRY_SOURCE_LOCALE);
+ TestCountryDetector countryDetector = new TestCountryDetector() {
+ @Override
+ protected Country getLocaleCountry() {
+ return resultCountry;
+ }
+ };
+ CountryListenerImpl listener = new CountryListenerImpl();
+ countryDetector.setCountryListener(listener);
+ Country country = countryDetector.detectCountry();
+ assertTrue(sameCountry(country, resultCountry));
+ assertFalse(listener.notified());
+ assertTrue(countryDetector.locationBasedDetectorStarted());
+ assertTrue(countryDetector.locationRefreshStarted());
+ countryDetector.stop();
+ assertTrue(countryDetector.locationRefreshCancelled());
+ }
+
+ public void testStoppingDetector() {
+ // Test stopping detector when LocationBasedCountryDetector was started
+ final Country resultCountry = new Country(
+ TestCountryDetector.COUNTRY_ISO, Country.COUNTRY_SOURCE_SIM);
+ TestCountryDetector countryDetector = new TestCountryDetector() {
+ @Override
+ protected Country getSimBasedCountry() {
+ return resultCountry;
+ }
+ };
+ CountryListenerImpl listener = new CountryListenerImpl();
+ countryDetector.setCountryListener(listener);
+ Country country = countryDetector.detectCountry();
+ assertTrue(sameCountry(country, resultCountry));
+ assertTrue(countryDetector.locationBasedDetectorStarted());
+ countryDetector.stop();
+ // The LocationBasedDetector should be stopped.
+ assertTrue(countryDetector.locationBasedDetectorStopped());
+ // The location refresh should not running.
+ assertTrue(countryDetector.locationRefreshCancelled());
+ }
+
+ public void testLocationBasedCountryNotFound() {
+ final Country resultCountry = new Country(
+ TestCountryDetector.COUNTRY_ISO, Country.COUNTRY_SOURCE_SIM);
+ TestCountryDetector countryDetector = new TestCountryDetector() {
+ @Override
+ protected Country getSimBasedCountry() {
+ return resultCountry;
+ }
+ };
+ CountryListenerImpl listener = new CountryListenerImpl();
+ countryDetector.setCountryListener(listener);
+ Country country = countryDetector.detectCountry();
+ assertTrue(sameCountry(country, resultCountry));
+ assertTrue(countryDetector.locationBasedDetectorStarted());
+ countryDetector.notifyLocationBasedListener(null);
+ assertFalse(listener.notified());
+ assertTrue(sameCountry(listener.getCountry(), null));
+ assertTrue(countryDetector.locationBasedDetectorStopped());
+ assertTrue(countryDetector.locationRefreshStarted());
+ countryDetector.stop();
+ assertTrue(countryDetector.locationRefreshCancelled());
+ }
+
+ public void testNoCountryFound() {
+ TestCountryDetector countryDetector = new TestCountryDetector();
+ CountryListenerImpl listener = new CountryListenerImpl();
+ countryDetector.setCountryListener(listener);
+ Country country = countryDetector.detectCountry();
+ assertTrue(sameCountry(country, null));
+ assertTrue(countryDetector.locationBasedDetectorStarted());
+ countryDetector.notifyLocationBasedListener(null);
+ assertFalse(listener.notified());
+ assertTrue(sameCountry(listener.getCountry(), null));
+ assertTrue(countryDetector.locationBasedDetectorStopped());
+ assertTrue(countryDetector.locationRefreshStarted());
+ countryDetector.stop();
+ assertTrue(countryDetector.locationRefreshCancelled());
+ }
+
+ public void testAddRemoveListener() {
+ TestCountryDetector countryDetector = new TestCountryDetector();
+ CountryListenerImpl listener = new CountryListenerImpl();
+ countryDetector.setCountryListener(listener);
+ assertTrue(countryDetector.isPhoneStateListenerAdded());
+ assertTrue(countryDetector.locationBasedDetectorStarted());
+ countryDetector.setCountryListener(null);
+ assertFalse(countryDetector.isPhoneStateListenerAdded());
+ assertTrue(countryDetector.locationBasedDetectorStopped());
+ }
+
+ public void testGeocoderNotImplemented() {
+ TestCountryDetector countryDetector = new TestCountryDetector() {
+ @Override
+ protected boolean isGeoCoderImplemented() {
+ return false;
+ }
+ };
+ CountryListenerImpl listener = new CountryListenerImpl();
+ countryDetector.setCountryListener(listener);
+ assertTrue(countryDetector.isPhoneStateListenerAdded());
+ assertFalse(countryDetector.locationBasedDetectorStarted());
+ countryDetector.setCountryListener(null);
+ assertFalse(countryDetector.isPhoneStateListenerAdded());
+ }
+
+ private boolean sameCountry(Country country1, Country country2) {
+ return country1 == null && country2 == null || country1 != null && country2 != null &&
+ country1.getCountryIso().equalsIgnoreCase(country2.getCountryIso()) &&
+ country1.getSource() == country2.getSource();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java b/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java
new file mode 100755
index 000000000000..71e8e2a62623
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.server.location;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+
+import android.location.Country;
+import android.location.CountryListener;
+import android.location.Location;
+import android.location.LocationListener;
+import android.test.AndroidTestCase;
+
+public class LocationBasedCountryDetectorTest extends AndroidTestCase {
+ private class TestCountryDetector extends LocationBasedCountryDetector {
+ public static final int TOTAL_PROVIDERS = 2;
+ protected Object countryFoundLocker = new Object();
+ protected boolean notifyCountry = false;
+ private final Location mLocation;
+ private final String mCountry;
+ private final long mQueryLocationTimeout;
+ private List<LocationListener> mListeners;
+
+ public TestCountryDetector(String country, String provider) {
+ this(country, provider, 1000 * 60 * 5);
+ }
+
+ public TestCountryDetector(String country, String provider, long queryLocationTimeout) {
+ super(getContext());
+ mCountry = country;
+ mLocation = new Location(provider);
+ mQueryLocationTimeout = queryLocationTimeout;
+ mListeners = new ArrayList<LocationListener>();
+ }
+
+ @Override
+ protected String getCountryFromLocation(Location location) {
+ synchronized (countryFoundLocker) {
+ if (!notifyCountry) {
+ try {
+ countryFoundLocker.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ if (mLocation.getProvider().endsWith(location.getProvider())) {
+ return mCountry;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected Location getLastKnownLocation() {
+ return mLocation;
+ }
+
+ @Override
+ protected void registerEnabledProviders(List<LocationListener> listeners) {
+ mListeners.addAll(listeners);
+ }
+
+ @Override
+ protected void unregisterProviders(List<LocationListener> listeners) {
+ for (LocationListener listener : mLocationListeners) {
+ assertTrue(mListeners.remove(listener));
+ }
+ }
+
+ @Override
+ protected long getQueryLocationTimeout() {
+ return mQueryLocationTimeout;
+ }
+
+ @Override
+ protected int getTotalEnabledProviders() {
+ return TOTAL_PROVIDERS;
+ }
+
+ public void notifyLocationFound() {
+ // Listener could be removed in the notification.
+ LocationListener[] listeners = new LocationListener[mListeners.size()];
+ mLocationListeners.toArray(listeners);
+ for (LocationListener listener :listeners) {
+ listener.onLocationChanged(mLocation);
+ }
+ }
+
+ public int getListenersCount() {
+ return mListeners.size();
+ }
+
+ public void notifyCountryFound() {
+ synchronized (countryFoundLocker) {
+ notifyCountry = true;
+ countryFoundLocker.notify();
+ }
+ }
+
+ public Timer getTimer() {
+ return mTimer;
+ }
+
+ public Thread getQueryThread() {
+ return mQueryThread;
+ }
+ }
+
+ private class CountryListenerImpl implements CountryListener {
+ private boolean mNotified;
+ private String mCountryCode;
+ public void onCountryDetected(Country country) {
+ mNotified = true;
+ if (country != null) {
+ mCountryCode = country.getCountryIso();
+ }
+ }
+
+ public boolean notified() {
+ return mNotified;
+ }
+
+ public String getCountry() {
+ return mCountryCode;
+ }
+ }
+
+ public void testFindingCountry() {
+ final String country = "us";
+ final String provider = "Good";
+ CountryListenerImpl countryListener = new CountryListenerImpl();
+ TestCountryDetector detector = new TestCountryDetector(country, provider);
+ detector.setCountryListener(countryListener);
+ detector.detectCountry();
+ assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+ detector.notifyLocationFound();
+ // All listeners should be unregistered
+ assertEquals(detector.getListenersCount(), 0);
+ assertNull(detector.getTimer());
+ Thread queryThread = waitForQueryThreadLaunched(detector);
+ detector.notifyCountryFound();
+ // Wait for query thread ending
+ waitForThreadEnding(queryThread);
+ // QueryThread should be set to NULL
+ assertNull(detector.getQueryThread());
+ assertTrue(countryListener.notified());
+ assertEquals(countryListener.getCountry(), country);
+ }
+
+ public void testFindingCountryCancelled() {
+ final String country = "us";
+ final String provider = "Good";
+ CountryListenerImpl countryListener = new CountryListenerImpl();
+ TestCountryDetector detector = new TestCountryDetector(country, provider);
+ detector.setCountryListener(countryListener);
+ detector.detectCountry();
+ assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+ detector.notifyLocationFound();
+ // All listeners should be unregistered
+ assertEquals(detector.getListenersCount(), 0);
+ // The time should be stopped
+ assertNull(detector.getTimer());
+ Thread queryThread = waitForQueryThreadLaunched(detector);
+ detector.stop();
+ // There is no way to stop the thread, let's test it could be stopped, after get country
+ detector.notifyCountryFound();
+ // Wait for query thread ending
+ waitForThreadEnding(queryThread);
+ // QueryThread should be set to NULL
+ assertNull(detector.getQueryThread());
+ assertTrue(countryListener.notified());
+ assertEquals(countryListener.getCountry(), country);
+ }
+
+ public void testFindingLocationCancelled() {
+ final String country = "us";
+ final String provider = "Good";
+ CountryListenerImpl countryListener = new CountryListenerImpl();
+ TestCountryDetector detector = new TestCountryDetector(country, provider);
+ detector.setCountryListener(countryListener);
+ detector.detectCountry();
+ assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+ detector.stop();
+ // All listeners should be unregistered
+ assertEquals(detector.getListenersCount(), 0);
+ // The time should be stopped
+ assertNull(detector.getTimer());
+ // QueryThread should still be NULL
+ assertNull(detector.getQueryThread());
+ assertFalse(countryListener.notified());
+ }
+
+ public void testFindingLocationFailed() {
+ final String country = "us";
+ final String provider = "Good";
+ long timeout = 1000;
+ TestCountryDetector detector = new TestCountryDetector(country, provider, timeout) {
+ @Override
+ protected Location getLastKnownLocation() {
+ return null;
+ }
+ };
+ CountryListenerImpl countryListener = new CountryListenerImpl();
+ detector.setCountryListener(countryListener);
+ detector.detectCountry();
+ assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+ waitForTimerReset(detector);
+ // All listeners should be unregistered
+ assertEquals(detector.getListenersCount(), 0);
+ // QueryThread should still be NULL
+ assertNull(detector.getQueryThread());
+ assertTrue(countryListener.notified());
+ assertNull(countryListener.getCountry());
+ }
+
+ public void testFindingCountryFailed() {
+ final String country = "us";
+ final String provider = "Good";
+ TestCountryDetector detector = new TestCountryDetector(country, provider) {
+ @Override
+ protected String getCountryFromLocation(Location location) {
+ synchronized (countryFoundLocker) {
+ if (! notifyCountry) {
+ try {
+ countryFoundLocker.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ // We didn't find country.
+ return null;
+ }
+ };
+ CountryListenerImpl countryListener = new CountryListenerImpl();
+ detector.setCountryListener(countryListener);
+ detector.detectCountry();
+ assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+ detector.notifyLocationFound();
+ // All listeners should be unregistered
+ assertEquals(detector.getListenersCount(), 0);
+ assertNull(detector.getTimer());
+ Thread queryThread = waitForQueryThreadLaunched(detector);
+ detector.notifyCountryFound();
+ // Wait for query thread ending
+ waitForThreadEnding(queryThread);
+ // QueryThread should be set to NULL
+ assertNull(detector.getQueryThread());
+ // CountryListener should be notified
+ assertTrue(countryListener.notified());
+ assertNull(countryListener.getCountry());
+ }
+
+ public void testFindingCountryWithLastKnownLocation() {
+ final String country = "us";
+ final String provider = "Good";
+ long timeout = 1000;
+ TestCountryDetector detector = new TestCountryDetector(country, provider, timeout);
+ CountryListenerImpl countryListener = new CountryListenerImpl();
+ detector.setCountryListener(countryListener);
+ detector.detectCountry();
+ assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+ waitForTimerReset(detector);
+ // All listeners should be unregistered
+ assertEquals(detector.getListenersCount(), 0);
+ Thread queryThread = waitForQueryThreadLaunched(detector);
+ detector.notifyCountryFound();
+ // Wait for query thread ending
+ waitForThreadEnding(queryThread);
+ // QueryThread should be set to NULL
+ assertNull(detector.getQueryThread());
+ // CountryListener should be notified
+ assertTrue(countryListener.notified());
+ assertEquals(countryListener.getCountry(), country);
+ }
+
+ private void waitForTimerReset(TestCountryDetector detector) {
+ int count = 5;
+ long interval = 1000;
+ try {
+ while (count-- > 0 && detector.getTimer() != null) {
+ Thread.sleep(interval);
+ }
+ } catch (InterruptedException e) {
+ }
+ Timer timer = detector.getTimer();
+ assertTrue(timer == null);
+ }
+
+ private void waitForThreadEnding(Thread thread) {
+ try {
+ thread.join(5000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private Thread waitForQueryThreadLaunched(TestCountryDetector detector) {
+ int count = 5;
+ long interval = 1000;
+ try {
+ while (count-- > 0 && detector.getQueryThread() == null) {
+ Thread.sleep(interval);
+ }
+ } catch (InterruptedException e) {
+ }
+ Thread thread = detector.getQueryThread();
+ assertTrue(thread != null);
+ return thread;
+ }
+}
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);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 96f0354ab57e..388beead9022 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -1207,8 +1207,6 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ
if (DBG) Log.d(TAG, "setWifiState: " + getWifiStateByName());
- if (!ActivityManagerNative.isSystemReady()) return;
-
final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
@@ -1235,7 +1233,6 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ
if (DBG) Log.d(TAG, "setWifiApState: " + getWifiApStateByName());
// Broadcast
- if (!ActivityManagerNative.isSystemReady()) return;
final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
@@ -1706,8 +1703,6 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ
}
private void sendNetworkStateChangeBroadcast(String bssid) {
- if (!ActivityManagerNative.isSystemReady()) return;
-
Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
@@ -1718,8 +1713,6 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ
}
private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
- if (!ActivityManagerNative.isSystemReady()) return;
-
Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
@@ -2967,6 +2960,15 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ
transitionTo(mScanModeState);
} else {
WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ /* If supplicant has already connected, before we could finish establishing
+ * the control channel connection, we miss all the supplicant events.
+ * Disconnect and reconnect when driver has started to ensure we receive
+ * all supplicant events.
+ *
+ * TODO: This is a bit unclean, ideally the supplicant should never
+ * connect until told to do so by the framework
+ */
+ WifiNative.disconnectCommand();
WifiNative.reconnectCommand();
transitionTo(mConnectModeState);
}