和菜鳥一起學android4.0.3原始碼之touchscreen配置+除錯記錄
記得應該是上上週了,終於畢業了,離開了學校,就得面對現實的社會,以前學校實驗室裡,老師給了鑰匙,那電腦隨便用,那元器件隨便玩,什麼51微控制器啊,PIC微控制器啊,FPGA啊,arm11啊什麼的。想著做什麼就直接萬用版+電烙鐵什麼的一起搞定。除錯,寫程式,焊板子都是自己一手操辦啊,多麼自由啊。到了公司,可不依你,對於上市公司來說,管理什麼的總歸還是有些規範化的。
對於嵌入式,雖然早有所耳聞,大三也玩過arm7,編了幾個基於GUI的貪吃蛇啊,黑白棋啊,連連看啊什麼的。自己也買來arm11,燒寫linux系統,搭建環境,最終也成功完成了hello world驅動模組。待以後有時間再好好整理整理。廢話不多說了,既然是anrdroid下的touchscreen的配置,那就專心點,不東扯西扯了。
都說android4.0.3的touchscreen有了很大的變化,菜鳥也不知道這麼龐大的程式碼,各個功能模組式幹嘛的。只能找找資料,瞎折騰了。公司的任務,觸控式螢幕得上了,android的東西,只用滑鼠可不好玩啊。開始除錯SPI模式的ads7846的電阻屏,板子沒有SPI介面,於是就用GPIO模擬SPI的方式來實現SPI的功能,在此要說明下,這個嵌入式的板子就是和51,PIC的板子用起來不一樣啊,什麼工作佇列,什麼中斷下半部分工作,什麼匯流排啊,I2C啊,SPI啊,I2S啊,USB啊,都這麼糾結,頓時覺得學得好少啊,自己又有點懶,什麼都想學,至於什麼也沒學好。除錯好驅動後,總算是完成了一半的工作,接著,是否要直接上android去跑呢?android裡什麼機制都不知道額,雖然一開始android中實現滑鼠的時候小研究過android下的input那個框架,不過只是模模糊糊的概念,根本就沒有弄清楚。看來得下點功夫啊,要不然怎麼混啊。又偏題了,額,正題,正題。。
直接上android了,突然發現出現了一個小圈圈。向滑鼠一樣的。貌似觸控式螢幕變成了滑鼠了。覺得太怪了,怎麼可以這樣?肯定那裡有問題的,找了好久的資料,終於找到了,原來是android4.0.3,他的touchscreen是需要配置檔案的。只要直接建立一個“裝置名.idc”的檔案,直接放到/system/usr/idc/目錄下,就可以了,裝置名是驅動中定義的,在android中的Eventhub中也是可以加列印在logcat中看出來的。
# Basic Parameters touch.deviceType = touchScreen touch.orientationAware = 1 # Size touch.size.calibration = diameter touch.size.scale = 10 touch.size.bias = 0 touch.size.isSummed = 0 # Pressure # Driver reports signal strength as pressure. # # A normal thumb touch typically registers about 200 signal strength # units although we don't expect these values to be accurate. touch.pressure.calibration = amplitude touch.pressure.scale = 0.005 # Orientation touch.orientation.calibration = none
但是就知道了這個配置檔案,具體那裡實現的呢?怎麼配置進去的呢?怎麼看著你個是touch.deviceType = touchScreen這個決定的。不多說來程式碼
frameworks/base/services/input/InputReader.cpp
void TouchInputMapper::configureParameters() { // Use the pointer presentation mode for devices that do not support distinct // multitouch. The spot-based presentation relies on being able to accurately // locate two or more fingers on the touch pad. mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT) ? Parameters::GESTURE_MODE_POINTER : Parameters::GESTURE_MODE_SPOTS; String8 gestureModeString; if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"), gestureModeString)) { if (gestureModeString == "pointer") { mParameters.gestureMode = Parameters::GESTURE_MODE_POINTER; } else if (gestureModeString == "spots") { mParameters.gestureMode = Parameters::GESTURE_MODE_SPOTS; } else if (gestureModeString != "default") { LOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string()); } } if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) { // The device is a touch screen. mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) { // The device is a pointing device like a track pad. mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) || getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { // The device is a cursor device with a touch pad attached. // By default don't use the touch pad to move the pointer. mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; } else { // The device is a touch pad of unknown purpose. mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; } String8 deviceTypeString; if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), deviceTypeString)) { if (deviceTypeString == "touchScreen") { mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; } else if (deviceTypeString == "touchPad") { mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; } else if (deviceTypeString == "pointer") { mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; } else if (deviceTypeString != "default") { LOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); } } mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN; getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), mParameters.orientationAware); mParameters.associatedDisplayId = -1; mParameters.associatedDisplayIsExternal = false; if (mParameters.orientationAware || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { mParameters.associatedDisplayIsExternal = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN && getDevice()->isExternal(); mParameters.associatedDisplayId = 0; } }
還是英文呢,看到第一行,如果沒有配置的話,那就是pointer,pointer不就是滑鼠嗎?只有定義deviceType為touchScreen,那才是我們要的啊。看來英文真的好重要好重
要啊。那到底是那裡去獲取配置檔案的呢?不是一般都是EventHub下開啟什麼檔案嗎?走,咱們去seesee。
frameworks/base/services/input/EventHub.cpp
void EventHub::loadConfigurationLocked(Device* device) {
device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
if (device->configurationFile.isEmpty()) {
LOGD("No input device configuration file found for device '%s'.",
device->identifier.name.string());
} else {
status_t status = PropertyMap::load(device->configurationFile,
&device->configuration);
if (status) {
LOGE("Error loading input device configuration file for device '%s'. "
"Using default configuration.",
device->identifier.name.string());
}
}
}
原來就是這裡去載入配置檔案的。然後再進行配置的,接著我們看看那些配置是什麼功能?上面的deviceType就不用說了,就是型別是觸控式螢幕而不是touchPad和pointer。
pointer是滑鼠類似的游標,touchPad還沒試過,板子上的觸控式螢幕也拆了,也沒法跳了。不知道是什麼,下次有機會去試試。差不多應該和touchScreen差不多。那麼
touch.size.calibration等一些配置是什麼?有什麼作用呢?還是程式碼看起
frameworks/base/services/input/InputReader.cpp
void TouchInputMapper::parseCalibration() {
const PropertyMap& in = getDevice()->getConfiguration();
Calibration& out = mCalibration;
// Size
out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT;
String8 sizeCalibrationString;
if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
if (sizeCalibrationString == "none") {
out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
} else if (sizeCalibrationString == "geometric") {
out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
} else if (sizeCalibrationString == "diameter") {
out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER;
} else if (sizeCalibrationString == "area") {
out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA;
} else if (sizeCalibrationString != "default") {
LOGW("Invalid value for touch.size.calibration: '%s'",
sizeCalibrationString.string());
}
}
out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"),
out.sizeScale);
out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"),
out.sizeBias);
out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"),
out.sizeIsSummed);
// Pressure
out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
String8 pressureCalibrationString;
if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
if (pressureCalibrationString == "none") {
out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
} else if (pressureCalibrationString == "physical") {
out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
} else if (pressureCalibrationString == "amplitude") {
out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
} else if (pressureCalibrationString != "default") {
LOGW("Invalid value for touch.pressure.calibration: '%s'",
pressureCalibrationString.string());
}
}
out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"),
out.pressureScale);
// Orientation
out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
String8 orientationCalibrationString;
if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
if (orientationCalibrationString == "none") {
out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
} else if (orientationCalibrationString == "interpolated") {
out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
} else if (orientationCalibrationString == "vector") {
out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR;
} else if (orientationCalibrationString != "default") {
LOGW("Invalid value for touch.orientation.calibration: '%s'",
orientationCalibrationString.string());
}
}
// Distance
out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT;
String8 distanceCalibrationString;
if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) {
if (distanceCalibrationString == "none") {
out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
} else if (distanceCalibrationString == "scaled") {
out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
} else if (distanceCalibrationString != "default") {
LOGW("Invalid value for touch.distance.calibration: '%s'",
distanceCalibrationString.string());
}
}
out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"),
out.distanceScale);
}
touch.size.calibration分為了1、geometric 2、diameter 3、area,老師說不懂的單詞要查字典,現在都什麼年代了,隨便谷歌一下,手機也行。什麼牛津字典啊的,那都是浮雲了。geometric:幾何圖形?怪怪的,是不是代表不同的觸控的形狀?不懂,diameter倒是還好,直徑嘛應該是一個觸控的點是以這個為直徑的一個圓,至於area嘛,區域,難道是多點的時候?也許吧。touch.pressure.calibration分為了1、physical 2、amplitude
。physical:物理的,是不是理論上的?amplitude是不是幅度就是壓力的大小?不懂額。要不再來看看程式碼?那些配置之後肯定有執行的。不可能簡簡單單的就是賦值了。找找看
frameworks/base/services/input/InputReader.cpp
void TouchInputMapper::cookPointerData() {
uint32_t currentPointerCount = mCurrentRawPointerData.pointerCount;
mCurrentCookedPointerData.clear();
mCurrentCookedPointerData.pointerCount = currentPointerCount;
mCurrentCookedPointerData.hoveringIdBits = mCurrentRawPointerData.hoveringIdBits;
mCurrentCookedPointerData.touchingIdBits = mCurrentRawPointerData.touchingIdBits;
// Walk through the the active pointers and map device coordinates onto
// surface coordinates and adjust for display orientation.
for (uint32_t i = 0; i < currentPointerCount; i++) {
const RawPointerData::Pointer& in = mCurrentRawPointerData.pointers[i];
// Size
float touchMajor, touchMinor, toolMajor, toolMinor, size;
switch (mCalibration.sizeCalibration) {
case Calibration::SIZE_CALIBRATION_GEOMETRIC:
case Calibration::SIZE_CALIBRATION_DIAMETER:
case Calibration::SIZE_CALIBRATION_AREA:
if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {
touchMajor = in.touchMajor;
touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
toolMajor = in.toolMajor;
toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
size = mRawPointerAxes.touchMinor.valid
? avg(in.touchMajor, in.touchMinor) : in.touchMajor;
} else if (mRawPointerAxes.touchMajor.valid) {
toolMajor = touchMajor = in.touchMajor;
toolMinor = touchMinor = mRawPointerAxes.touchMinor.valid
? in.touchMinor : in.touchMajor;
size = mRawPointerAxes.touchMinor.valid
? avg(in.touchMajor, in.touchMinor) : in.touchMajor;
} else if (mRawPointerAxes.toolMajor.valid) {
touchMajor = toolMajor = in.toolMajor;
touchMinor = toolMinor = mRawPointerAxes.toolMinor.valid
? in.toolMinor : in.toolMajor;
size = mRawPointerAxes.toolMinor.valid
? avg(in.toolMajor, in.toolMinor) : in.toolMajor;
} else {
LOG_ASSERT(false, "No touch or tool axes. "
"Size calibration should have been resolved to NONE.");
touchMajor = 0;
touchMinor = 0;
toolMajor = 0;
toolMinor = 0;
size = 0;
}
if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) {
uint32_t touchingCount = mCurrentRawPointerData.touchingIdBits.count();
if (touchingCount > 1) {
touchMajor /= touchingCount;
touchMinor /= touchingCount;
toolMajor /= touchingCount;
toolMinor /= touchingCount;
size /= touchingCount;
}
}
if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) {
touchMajor *= mGeometricScale;
touchMinor *= mGeometricScale;
toolMajor *= mGeometricScale;
toolMinor *= mGeometricScale;
} else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) {
touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0;
touchMinor = touchMajor;
toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0;
toolMinor = toolMajor;
} else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) {
touchMinor = touchMajor;
toolMinor = toolMajor;
}
mCalibration.applySizeScaleAndBias(&touchMajor);
mCalibration.applySizeScaleAndBias(&touchMinor);
mCalibration.applySizeScaleAndBias(&toolMajor);
mCalibration.applySizeScaleAndBias(&toolMinor);
size *= mSizeScale;
break;
default:
touchMajor = 0;
touchMinor = 0;
toolMajor = 0;
toolMinor = 0;
size = 0;
break;
}
// Pressure
float pressure;
switch (mCalibration.pressureCalibration) {
case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
pressure = in.pressure * mPressureScale;
break;
default:
pressure = in.isHovering ? 0 : 1;
break;
}
// Tilt and Orientation
float tilt;
float orientation;
if (mHaveTilt) {
float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale;
float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale;
orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle));
tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle));
} else {
tilt = 0;
switch (mCalibration.orientationCalibration) {
case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
orientation = (in.orientation - mOrientationCenter) * mOrientationScale;
break;
case Calibration::ORIENTATION_CALIBRATION_VECTOR: {
int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
int32_t c2 = signExtendNybble(in.orientation & 0x0f);
if (c1 != 0 || c2 != 0) {
orientation = atan2f(c1, c2) * 0.5f;
float confidence = hypotf(c1, c2);
float scale = 1.0f + confidence / 16.0f;
touchMajor *= scale;
touchMinor /= scale;
toolMajor *= scale;
toolMinor /= scale;
} else {
orientation = 0;
}
break;
}
default:
orientation = 0;
}
}
// Distance
float distance;
switch (mCalibration.distanceCalibration) {
case Calibration::DISTANCE_CALIBRATION_SCALED:
distance = in.distance * mDistanceScale;
break;
default:
distance = 0;
}
// X and Y
// Adjust coords for surface orientation.
float x, y;
switch (mSurfaceOrientation) {
case DISPLAY_ORIENTATION_90:
x = float(in.y - mRawPointerAxes.y.minValue) * mYScale;
y = float(mRawPointerAxes.x.maxValue - in.x) * mXScale;
orientation -= M_PI_2;
if (orientation < - M_PI_2) {
orientation += M_PI;
}
break;
case DISPLAY_ORIENTATION_180:
x = float(mRawPointerAxes.x.maxValue - in.x) * mXScale;
y = float(mRawPointerAxes.y.maxValue - in.y) * mYScale;
break;
case DISPLAY_ORIENTATION_270:
x = float(mRawPointerAxes.y.maxValue - in.y) * mYScale;
y = float(in.x - mRawPointerAxes.x.minValue) * mXScale;
orientation += M_PI_2;
if (orientation > M_PI_2) {
orientation -= M_PI;
}
break;
default:
x = float(in.x - mRawPointerAxes.x.minValue) * mXScale;
y = float(in.y - mRawPointerAxes.y.minValue) * mYScale;
break;
}
// Write output coords.
PointerCoords& out = mCurrentCookedPointerData.pointerCoords[i];
out.clear();
out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);
out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);
out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor);
out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor);
out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt);
out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);
// Write output properties.
PointerProperties& properties = mCurrentCookedPointerData.pointerProperties[i];
uint32_t id = in.id;
properties.clear();
properties.id = id;
properties.toolType = in.toolType;
// Write id index.
mCurrentCookedPointerData.idToIndex[id] = i;
}
}
這資料處理的,cookPointerData,哈哈哈,原來都在這裡搞定的啊。TouchMajor and TouchMinor表示了觸控時接觸面積的大小範圍。這個函式會把驅動上傳上來的資料,也就是X、Y座標轉換為android設定的解析度的一個對映。差不多就這樣了。具體,碰到問題了在解決了。
在此,有時候再講講自己除錯的時候碰到的一個問題,那就是android電源管理中會把背光給關掉,所以點死了觸控式螢幕還是發現沒用。所以除錯的時候可以把其中的一個
policyFlags設定下,不要drop掉,也就是即使背光關掉了也可以有效。
frameworks/base/services/input/InputDispatcher.cpp
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now();
// Reset the key repeat timer whenever we disallow key events, even if the next event
// is not a key. This is to ensure that we abort a key repeat if the device is just coming
// out of sleep.
if (!mPolicy->isKeyRepeatEnabled()) {
resetKeyRepeatLocked();
}
// If dispatching is frozen, do not process timeouts or try to deliver any new events.
if (mDispatchFrozen) {
#if DEBUG_FOCUS
LOGD("Dispatch frozen. Waiting some more.");
#endif
return;
}
// Optimize latency of app switches.
// Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
// been pressed. When it expires, we preempt dispatch and drop all other pending events.
bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
if (mAppSwitchDueTime < *nextWakeupTime) {
*nextWakeupTime = mAppSwitchDueTime;
}
// Ready to start a new event.
// If we don't already have a pending event, go grab one.
if (! mPendingEvent) {
if (mInboundQueue.isEmpty()) {
if (isAppSwitchDue) {
// The inbound queue is empty so the app switch key we were waiting
// for will never arrive. Stop waiting for it.
resetPendingAppSwitchLocked(false);
isAppSwitchDue = false;
}
// Synthesize a key repeat if appropriate.
if (mKeyRepeatState.lastKeyEntry) {
if (currentTime >= mKeyRepeatState.nextRepeatTime) {
mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
} else {
if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
*nextWakeupTime = mKeyRepeatState.nextRepeatTime;
}
}
}
// Nothing to do if there is no pending event.
if (! mPendingEvent) {
if (mActiveConnections.isEmpty()) {
dispatchIdleLocked();
}
return;
}
} else {
// Inbound queue has at least one entry.
EventEntry* entry = mInboundQueue.head;
// Throttle the entry if it is a move event and there are no
// other events behind it in the queue. Due to movement batching, additional
// samples may be appended to this event by the time the throttling timeout
// expires.
// TODO Make this smarter and consider throttling per device independently.
if (entry->type == EventEntry::TYPE_MOTION
&& !isAppSwitchDue
&& mDispatchEnabled
&& (entry->policyFlags & POLICY_FLAG_PASS_TO_USER)
&& !entry->isInjected()) {
MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
int32_t deviceId = motionEntry->deviceId;
uint32_t source = motionEntry->source;
if (! isAppSwitchDue
&& !motionEntry->next // exactly one event, no successors
&& (motionEntry->action == AMOTION_EVENT_ACTION_MOVE
|| motionEntry->action == AMOTION_EVENT_ACTION_HOVER_MOVE)
&& deviceId == mThrottleState.lastDeviceId
&& source == mThrottleState.lastSource) {
nsecs_t nextTime = mThrottleState.lastEventTime
+ mThrottleState.minTimeBetweenEvents;
if (currentTime < nextTime) {
// Throttle it!
#if DEBUG_THROTTLING
LOGD("Throttling - Delaying motion event for "
"device %d, source 0x%08x by up to %0.3fms.",
deviceId, source, (nextTime - currentTime) * 0.000001);
#endif
if (nextTime < *nextWakeupTime) {
*nextWakeupTime = nextTime;
}
if (mThrottleState.originalSampleCount == 0) {
mThrottleState.originalSampleCount =
motionEntry->countSamples();
}
return;
}
}
#if DEBUG_THROTTLING
if (mThrottleState.originalSampleCount != 0) {
uint32_t count = motionEntry->countSamples();
LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
count - mThrottleState.originalSampleCount,
mThrottleState.originalSampleCount, count);
mThrottleState.originalSampleCount = 0;
}
#endif
mThrottleState.lastEventTime = currentTime;
mThrottleState.lastDeviceId = deviceId;
mThrottleState.lastSource = source;
}
mInboundQueue.dequeue(entry);
mPendingEvent = entry;
}
// Poke user activity for this event.
if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
pokeUserActivityLocked(mPendingEvent);
}
}
// Now we have an event to dispatch.
// All events are eventually dequeued and processed this way, even if we intend to drop them.
LOG_ASSERT(mPendingEvent != NULL);
bool done = false;
DropReason dropReason = DROP_REASON_NOT_DROPPED;
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
dropReason = DROP_REASON_POLICY;
} else if (!mDispatchEnabled) {
dropReason = DROP_REASON_DISABLED;
}
if (mNextUnblockedEvent == mPendingEvent) {
mNextUnblockedEvent = NULL;
}
switch (mPendingEvent->type) {
case EventEntry::TYPE_CONFIGURATION_CHANGED: {
ConfigurationChangedEntry* typedEntry =
static_cast<ConfigurationChangedEntry*>(mPendingEvent);
done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped
break;
}
case EventEntry::TYPE_DEVICE_RESET: {
DeviceResetEntry* typedEntry =
static_cast<DeviceResetEntry*>(mPendingEvent);
done = dispatchDeviceResetLocked(currentTime, typedEntry);
dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped
break;
}
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
if (isAppSwitchDue) {
if (isAppSwitchKeyEventLocked(typedEntry)) {
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DROP_REASON_NOT_DROPPED) {
dropReason = DROP_REASON_APP_SWITCH;
}
}
if (dropReason == DROP_REASON_NOT_DROPPED
&& isStaleEventLocked(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DROP_REASON_BLOCKED;
}
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
case EventEntry::TYPE_MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
dropReason = DROP_REASON_APP_SWITCH;
}
if (dropReason == DROP_REASON_NOT_DROPPED
&& isStaleEventLocked(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DROP_REASON_BLOCKED;
}
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
break;
}
default:
LOG_ASSERT(false);
break;
}
if (done) {
if (dropReason != DROP_REASON_NOT_DROPPED) {
dropInboundEventLocked(mPendingEvent, dropReason);
}
releasePendingEventLocked();
*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}
只要把下面的東西給註釋掉就好了,那具體的policyflags是哪裡賦值的呢?真心找了我好久的。
frameworks/base/services/input/InputDispatcher.cpp
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
dropReason = DROP_REASON_POLICY;
} else if (!mDispatchEnabled) {
dropReason = DROP_REASON_DISABLED;
}
frameworks/base/services/input/InputDispatcher.cpp
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
LOGD("notifyMotion - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
"action=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, edgeFlags=0x%x, "
"xPrecision=%f, yPrecision=%f, downTime=%lld",
args->eventTime, args->deviceId, args->source, args->policyFlags,
args->action, args->flags, args->metaState, args->buttonState,
args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime);
for (uint32_t i = 0; i < args->pointerCount; i++) {
LOGD(" Pointer %d: id=%d, toolType=%d, "
"x=%f, y=%f, pressure=%f, size=%f, "
"touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
"orientation=%f",
i, args->pointerProperties[i].id,
args->pointerProperties[i].toolType,
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
}
#endif
if (!validateMotionEvent(args->action, args->pointerCount, args->pointerProperties)) {
return;
}
uint32_t policyFlags = args->policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);
bool needWake;
{ // acquire lock
mLock.lock();
if (mInputFilterEnabled) {
mLock.unlock();
MotionEvent event;
event.initialize(args->deviceId, args->source, args->action, args->flags,
args->edgeFlags, args->metaState, args->buttonState, 0, 0,
args->xPrecision, args->yPrecision,
args->downTime, args->eventTime,
args->pointerCount, args->pointerProperties, args->pointerCoords);
policyFlags |= POLICY_FLAG_FILTERED;
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
return; // event was consumed by the filter
}
mLock.lock();
}
// Attempt batching and streaming of move events.
if (args->action == AMOTION_EVENT_ACTION_MOVE
|| args->action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
// BATCHING CASE
//
// Try to append a move sample to the tail of the inbound queue for this device.
// Give up if we encounter a non-move motion event for this device since that
// means we cannot append any new samples until a new motion event has started.
for (EventEntry* entry = mInboundQueue.tail; entry; entry = entry->prev) {
if (entry->type != EventEntry::TYPE_MOTION) {
// Keep looking for motion events.
continue;
}
MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
if (motionEntry->deviceId != args->deviceId
|| motionEntry->source != args->source) {
// Keep looking for this device and source.
continue;
}
if (!motionEntry->canAppendSamples(args->action,
args->pointerCount, args->pointerProperties)) {
// Last motion event in the queue for this device and source is
// not compatible for appending new samples. Stop here.
goto NoBatchingOrStreaming;
}
// Do the batching magic.
batchMotionLocked(motionEntry, args->eventTime,
args->metaState, args->pointerCoords,
"most recent motion event for this device and source in the inbound queue");
mLock.unlock();
return; // done!
}
// BATCHING ONTO PENDING EVENT CASE
//
// Try to append a move sample to the currently pending event, if there is one.
// We can do this as long as we are still waiting to find the targets for the
// event. Once the targets are locked-in we can only do streaming.
if (mPendingEvent
&& (!mPendingEvent->dispatchInProgress || !mCurrentInputTargetsValid)
&& mPendingEvent->type == EventEntry::TYPE_MOTION) {
MotionEntry* motionEntry = static_cast<MotionEntry*>(mPendingEvent);
if (motionEntry->deviceId == args->deviceId
&& motionEntry->source == args->source) {
if (!motionEntry->canAppendSamples(args->action,
args->pointerCount, args->pointerProperties)) {
// Pending motion event is for this device and source but it is
// not compatible for appending new samples. Stop here.
goto NoBatchingOrStreaming;
}
// Do the batching magic.
batchMotionLocked(motionEntry, args->eventTime,
args->metaState, args->pointerCoords,
"pending motion event");
mLock.unlock();
return; // done!
}
}
// STREAMING CASE
//
// There is no pending motion event (of any kind) for this device in the inbound queue.
// Search the outbound queue for the current foreground targets to find a dispatched
// motion event that is still in progress. If found, then, appen the new sample to
// that event and push it out to all current targets. The logic in
// prepareDispatchCycleLocked takes care of the case where some targets may
// already have consumed the motion event by starting a new dispatch cycle if needed.
if (mCurrentInputTargetsValid) {
for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
const InputTarget& inputTarget = mCurrentInputTargets[i];
if ((inputTarget.flags & InputTarget::FLAG_FOREGROUND) == 0) {
// Skip non-foreground targets. We only want to stream if there is at
// least one foreground target whose dispatch is still in progress.
continue;
}
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex < 0) {
// Connection must no longer be valid.
continue;
}
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
if (connection->outboundQueue.isEmpty()) {
// This foreground target has an empty outbound queue.
continue;
}
DispatchEntry* dispatchEntry = connection->outboundQueue.head;
if (! dispatchEntry->inProgress
|| dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION
|| dispatchEntry->isSplit()) {
// No motion event is being dispatched, or it is being split across
// windows in which case we cannot stream.
continue;
}
MotionEntry* motionEntry = static_cast<MotionEntry*>(
dispatchEntry->eventEntry);
if (motionEntry->action != args->action
|| motionEntry->deviceId != args->deviceId
|| motionEntry->source != args->source
|| motionEntry->pointerCount != args->pointerCount
|| motionEntry->isInjected()) {
// The motion event is not compatible with this move.
continue;
}
if (args->action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
if (mLastHoverWindowHandle == NULL) {
#if DEBUG_BATCHING
LOGD("Not streaming hover move because there is no "
"last hovered window.");
#endif
goto NoBatchingOrStreaming;
}
sp<InputWindowHandle> hoverWindowHandle = findTouchedWindowAtLocked(
args->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X),
args->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
if (mLastHoverWindowHandle != hoverWindowHandle) {
#if DEBUG_BATCHING
LOGD("Not streaming hover move because the last hovered window "
"is '%s' but the currently hovered window is '%s'.",
mLastHoverWindowHandle->getName().string(),
hoverWindowHandle != NULL
? hoverWindowHandle->getName().string() : "<null>");
#endif
goto NoBatchingOrStreaming;
}
}
// Hurray! This foreground target is currently dispatching a move event
// that we can stream onto. Append the motion sample and resume dispatch.
motionEntry->appendSample(args->eventTime, args->pointerCoords);
#if DEBUG_BATCHING
LOGD("Appended motion sample onto batch for most recently dispatched "
"motion event for this device and source in the outbound queues. "
"Attempting to stream the motion sample.");
#endif
nsecs_t currentTime = now();
dispatchEventToCurrentInputTargetsLocked(currentTime, motionEntry,
true /*resumeWithAppendedMotionSample*/);
runCommandsLockedInterruptible();
mLock.unlock();
return; // done!
}
}
NoBatchingOrStreaming:;
}
// Just enqueue a new motion event.
MotionEntry* newEntry = new MotionEntry(args->eventTime,
args->deviceId, args->source, policyFlags,
args->action, args->flags, args->metaState, args->buttonState,
args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
args->pointerCount, args->pointerProperties, args->pointerCoords);
needWake = enqueueInboundEventLocked(newEntry);
mLock.unlock();
} // release lock
if (needWake) {
mLooper->wake();
}
}
看到了嗎?就是這裡了,interceptMotionBeforeQueueing()函式,
uint32_t policyFlags = args->policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);
void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
// Policy:
// - Ignore untrusted events and pass them along.
// - No special filtering for injected events required at this time.
// - Filter normal events based on screen state.
// - For normal events brighten (but do not wake) the screen if currently dim.
if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) {
if (isScreenOn()) {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
if (!isScreenBright()) {
policyFlags |= POLICY_FLAG_BRIGHT_HERE;
}
} else {
JNIEnv* env = jniEnv();
jint wmActions = env->CallIntMethod(mCallbacksObj,
gCallbacksClassInfo.interceptMotionBeforeQueueingWhenScreenOff,
policyFlags);
if (checkAndClearExceptionFromCallback(env,
"interceptMotionBeforeQueueingWhenScreenOff")) {
wmActions = 0;
}
policyFlags |= POLICY_FLAG_WOKE_HERE | POLICY_FLAG_BRIGHT_HERE;
handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
}
} else {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
}
}
原來如此呢, if (isScreenOn()) 。搞定,收工,回去睡覺了,哈哈。。。