1.描述
点按操作反馈可以在开发者模式中选择开启,开启后,当手指触摸屏幕 时,会出现一个小原点。
2.总结
先做个总结,以防有人没耐心看下去。。。
- 当在开发者模式界面打开显示点按操作反馈开关时,会通过ShowTapsPreferenceController改变Settings.System.SHOW_TOUCHES的值,而这个值在开机的时候就被注册了监听,会去调用nativeSetShowTouches,进入c层
- 然后通过输入事件的监听线程调用refreshConfigurationLocked,更新配置并新建了PointerControl.
- 然后通过input的事件分发线程通知去重置设备并清空之前的keyevent和motionevent。
- 重置设备使用了hal和驱动交互,重置设备后消息被InputHub捕获,接着开始处理这条消息。通过层层调用,最后调到了PointerControl去处理绘画逻辑,最后交给surface去显示
3. 分析
以下分析下它是如何实现的。
3.1界面处理
首先全局搜索显示点按操作反馈可以找到
//frameworks/base/packages/SettingsLib/res/values-zh-rCN/strings.xml
<string name="show_touches" msgid="8437666942161289025">"显示点按操作反馈"</string>
<string name="show_touches_summary" msgid="3692861665994502193">"显示点按操作的视觉反馈"</string>
接下来搜索一下show_touches_summary
可以适当的屏蔽一下strings.xml和R文件,因为多语言版本还有R中肯定存在很多show_touches_summary,而这不是我们要的。
Tv和car也不是我们要分析的,可以暂时去掉。
//packages/apps/Settings/res/xml/development_settings.xml
<PreferenceCategory
android:key="debug_input_category"
android:title="@string/debug_input_category"
android:order="500">
<SwitchPreference
android:key="show_touches"
android:title="@string/show_touches"
android:summary="@string/show_touches_summary" />
<SwitchPreference
android:key="pointer_location"
android:title="@string/pointer_location"
android:summary="@string/pointer_location_summary" />
</PreferenceCategory>
这时候应该搜一下development_settings,看看是哪里在用
一番过滤,显然这个比较像,继续看看
//packages/apps/Settings/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@Override
protected int getPreferenceScreenResId() {
return Utils.isMonkeyRunning()? R.xml.placeholder_prefs : R.xml.development_settings;
}
//这些底下有用
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
if (Utils.isMonkeyRunning()) {
mPreferenceControllers = new ArrayList<>();
return null;
}
mPreferenceControllers = buildPreferenceControllers(context, getActivity(),
getSettingsLifecycle(), this /* devOptionsDashboardFragment */,
new BluetoothA2dpConfigStore());
return mPreferenceControllers;
}
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment,
BluetoothA2dpConfigStore bluetoothA2dpConfigStore) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new MemoryUsagePreferenceController(context));
controllers.add(new BugReportPreferenceController(context));
controllers.add(new BugReportHandlerPreferenceController(context));
controllers.add(new SystemServerHeapDumpPreferenceController(context));
controllers.add(new LocalBackupPasswordPreferenceController(context));
controllers.add(new StayAwakePreferenceController(context, lifecycle));
controllers.add(new HdcpCheckingPreferenceController(context));
controllers.add(new BluetoothSnoopLogPreferenceController(context));
controllers.add(new OemUnlockPreferenceController(context, activity, fragment));
controllers.add(new FileEncryptionPreferenceController(context));
controllers.add(new PictureColorModePreferenceController(context, lifecycle));
controllers.add(new WebViewAppPreferenceController(context));
controllers.add(new CoolColorTemperaturePreferenceController(context));
controllers.add(new DisableAutomaticUpdatesPreferenceController(context));
controllers.add(new SelectDSUPreferenceController(context));
controllers.add(new AdbPreferenceController(context, fragment));
controllers.add(new ClearAdbKeysPreferenceController(context, fragment));
controllers.add(new WirelessDebuggingPreferenceController(context, lifecycle));
controllers.add(new AdbAuthorizationTimeoutPreferenceController(context));
controllers.add(new LocalTerminalPreferenceController(context));
controllers.add(new BugReportInPowerPreferenceController(context));
controllers.add(new AutomaticSystemServerHeapDumpPreferenceController(context));
controllers.add(new MockLocationAppPreferenceController(context, fragment));
controllers.add(new DebugViewAttributesPreferenceController(context));
controllers.add(new SelectDebugAppPreferenceController(context, fragment));
controllers.add(new WaitForDebuggerPreferenceController(context));
controllers.add(new EnableGpuDebugLayersPreferenceController(context));
controllers.add(new ForcePeakRefreshRatePreferenceController(context));
controllers.add(new EnableVerboseVendorLoggingPreferenceController(context));
controllers.add(new VerifyAppsOverUsbPreferenceController(context));
controllers.add(new ArtVerifierPreferenceController(context));
controllers.add(new LogdSizePreferenceController(context));
controllers.add(new LogPersistPreferenceController(context, fragment, lifecycle));
controllers.add(new CameraLaserSensorPreferenceController(context));
controllers.add(new WifiDisplayCertificationPreferenceController(context));
controllers.add(new WifiVerboseLoggingPreferenceController(context));
controllers.add(new WifiScanThrottlingPreferenceController(context));
controllers.add(new WifiEnhancedMacRandomizationPreferenceController(context));
controllers.add(new MobileDataAlwaysOnPreferenceController(context));
controllers.add(new TetheringHardwareAccelPreferenceController(context));
controllers.add(new BluetoothDeviceNoNamePreferenceController(context));
controllers.add(new BluetoothAbsoluteVolumePreferenceController(context));
controllers.add(new BluetoothGabeldorschePreferenceController(context));
controllers.add(new BluetoothAvrcpVersionPreferenceController(context));
controllers.add(new BluetoothMapVersionPreferenceController(context));
controllers.add(new BluetoothA2dpHwOffloadPreferenceController(context, fragment));
controllers.add(new BluetoothMaxConnectedAudioDevicesPreferenceController(context));
controllers.add(new ShowTapsPreferenceController(context));
controllers.add(new PointerLocationPreferenceController(context));
controllers.add(new ShowSurfaceUpdatesPreferenceController(context));
controllers.add(new ShowLayoutBoundsPreferenceController(context));
controllers.add(new ShowRefreshRatePreferenceController(context));
controllers.add(new RtlLayoutPreferenceController(context));
controllers.add(new WindowAnimationScalePreferenceController(context));
controllers.add(new EmulateDisplayCutoutPreferenceController(context));
controllers.add(new TransitionAnimationScalePreferenceController(context));
controllers.add(new AnimatorDurationScalePreferenceController(context));
controllers.add(new SecondaryDisplayPreferenceController(context));
controllers.add(new GpuViewUpdatesPreferenceController(context));
controllers.add(new HardwareLayersUpdatesPreferenceController(context));
controllers.add(new DebugGpuOverdrawPreferenceController(context));
controllers.add(new DebugNonRectClipOperationsPreferenceController(context));
controllers.add(new ForceDarkPreferenceController(context));
controllers.add(new EnableBlursPreferenceController(context));
controllers.add(new ForceMSAAPreferenceController(context));
controllers.add(new HardwareOverlaysPreferenceController(context));
controllers.add(new SimulateColorSpacePreferenceController(context));
controllers.add(new UsbAudioRoutingPreferenceController(context));
controllers.add(new StrictModePreferenceController(context));
controllers.add(new ProfileGpuRenderingPreferenceController(context));
controllers.add(new KeepActivitiesPreferenceController(context));
controllers.add(new BackgroundProcessLimitPreferenceController(context));
controllers.add(new CachedAppsFreezerPreferenceController(context));
controllers.add(new ShowFirstCrashDialogPreferenceController(context));
controllers.add(new AppsNotRespondingPreferenceController(context));
controllers.add(new NotificationChannelWarningsPreferenceController(context));
controllers.add(new AllowAppsOnExternalPreferenceController(context));
controllers.add(new ResizableActivityPreferenceController(context));
controllers.add(new FreeformWindowsPreferenceController(context));
controllers.add(new DesktopModePreferenceController(context));
controllers.add(new SizeCompatFreeformPreferenceController(context));
controllers.add(new ShortcutManagerThrottlingPreferenceController(context));
controllers.add(new EnableGnssRawMeasFullTrackingPreferenceController(context));
controllers.add(new DefaultLaunchPreferenceController(context, "running_apps"));
controllers.add(new DefaultLaunchPreferenceController(context, "demo_mode"));
controllers.add(new DefaultLaunchPreferenceController(context, "quick_settings_tiles"));
controllers.add(new DefaultLaunchPreferenceController(context, "feature_flags_dashboard"));
controllers.add(
new DefaultLaunchPreferenceController(context, "default_usb_configuration"));
controllers.add(new DefaultLaunchPreferenceController(context, "density"));
controllers.add(new DefaultLaunchPreferenceController(context, "background_check"));
controllers.add(new DefaultLaunchPreferenceController(context, "inactive_apps"));
controllers.add(new AutofillLoggingLevelPreferenceController(context, lifecycle));
controllers.add(new AutofillResetOptionsPreferenceController(context));
controllers.add(new BluetoothCodecDialogPreferenceController(context, lifecycle,
bluetoothA2dpConfigStore, fragment));
controllers.add(new BluetoothSampleRateDialogPreferenceController(context, lifecycle,
bluetoothA2dpConfigStore));
controllers.add(new BluetoothBitPerSampleDialogPreferenceController(context, lifecycle,
bluetoothA2dpConfigStore));
controllers.add(new BluetoothQualityDialogPreferenceController(context, lifecycle,
bluetoothA2dpConfigStore));
controllers.add(new BluetoothChannelModeDialogPreferenceController(context, lifecycle,
bluetoothA2dpConfigStore));
controllers.add(new BluetoothHDAudioPreferenceController(context, lifecycle,
bluetoothA2dpConfigStore, fragment));
controllers.add(new SharedDataPreferenceController(context));
controllers.add(new OverlaySettingsPreferenceController(context));
return controllers;
}
找找它的父类,看哪里调用了getPreferenceScreenResId
//packages/apps/Settings/src/com/android/settings/dashboard/DashboardFragment.java
// Load preference controllers from code
final List<AbstractPreferenceController> controllersFromCode =
createPreferenceControllers(context);
// Load preference controllers from xml definition
final List<BasePreferenceController> controllersFromXml = PreferenceControllerListHelper
.getPreferenceControllersFromXml(context, getPreferenceScreenResId());
// Filter xml-based controllers in case a similar controller is created from code already.
final List<BasePreferenceController> uniqueControllerFromXml =
PreferenceControllerListHelper.filterControllers(
controllersFromXml, controllersFromCode);
先看下getPreferenceControllersFromXml
//packages/apps/Settings/src/com/android/settings/core/PreferenceControllerListHelper.java
/**
* Instantiates a list of controller based on xml definition.
*/
@NonNull
public static List<BasePreferenceController> getPreferenceControllersFromXml(Context context,
@XmlRes int xmlResId) {
final List<BasePreferenceController> controllers = new ArrayList<>();
List<Bundle> preferenceMetadata;
try {
preferenceMetadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId,
MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_PREF_CONTROLLER
| MetadataFlag.FLAG_INCLUDE_PREF_SCREEN | MetadataFlag.FLAG_FOR_WORK);
} catch (IOException | XmlPullParserException e) {
Log.e(TAG, "Failed to parse preference xml for getting controllers", e);
return controllers;
}
for (Bundle metadata : preferenceMetadata) {
final String controllerName = metadata.getString(METADATA_CONTROLLER);
if (TextUtils.isEmpty(controllerName)) {
continue;
}
BasePreferenceController controller;
try {
controller = BasePreferenceController.createInstance(context, controllerName);
} catch (IllegalStateException e) {
Log.d(TAG, "Could not find Context-only controller for pref: " + controllerName);
final String key = metadata.getString(METADATA_KEY);
final boolean isWorkProfile = metadata.getBoolean(METADATA_FOR_WORK, false);
if (TextUtils.isEmpty(key)) {
Log.w(TAG, "Controller requires key but it's not defined in xml: "
+ controllerName);
continue;
}
try {
controller = BasePreferenceController.createInstance(context, controllerName,
key, isWorkProfile);
} catch (IllegalStateException e2) {
Log.w(TAG, "Cannot instantiate controller from reflection: " + controllerName);
continue;
}
}
controllers.add(controller);
}
return controllers;
}
这里是根据xml中配置好的classname去实例化control。显然show_touches没有配置control的classname。
看下PreferenceControllerListHelper.filterControllers
//packages/apps/Settings/src/com/android/settings/core/PreferenceControllerListHelper.java
@NonNull
public static List<BasePreferenceController> filterControllers(
@NonNull List<BasePreferenceController> input,
List<AbstractPreferenceController> filter) {
if (input == null || filter == null) {
return input;
}
final Set<String> keys = new TreeSet<>();
final List<BasePreferenceController> filteredList = new ArrayList<>();
for (AbstractPreferenceController controller : filter) {
final String key = controller.getPreferenceKey();
if (key != null) {
keys.add(key);
}
}
for (BasePreferenceController controller : input) {
if (keys.contains(controller.getPreferenceKey())) {
Log.w(TAG, controller.getPreferenceKey() + " already has a controller");
continue;
}
filteredList.add(controller);
}
return filteredList;
}
这里是过滤掉了已经在代码中添加过的control。因此control会是唯一的。
这样就需要在代码添加的controls中去找下preferenceKey是show_touches的了。
盲猜一下是ShowTapsPreferenceController
进去一看,还真是!
//packages/apps/Settings/src/com/android/settings/development/ShowTapsPreferenceController.java
private static final String SHOW_TOUCHES_KEY = "show_touches";
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean isEnabled = (Boolean) newValue;
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.SHOW_TOUCHES, isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
return true;
}
@Override
public void updateState(Preference preference) {
int showTapsMode = Settings.System.getInt(mContext.getContentResolver(),
Settings.System.SHOW_TOUCHES, SETTING_VALUE_OFF);
((SwitchPreference) mPreference).setChecked(showTapsMode != SETTING_VALUE_OFF);
}
@Override
protected void onDeveloperOptionsSwitchDisabled() {
super.onDeveloperOptionsSwitchDisabled();
Settings.System.putInt(mContext.getContentResolver(), Settings.System.SHOW_TOUCHES,
SETTING_VALUE_OFF);
((SwitchPreference) mPreference).setChecked(false);
}
这里可以看出就是修改了下Settings.System.SHOW_TOUCHES这个值,全局搜一下
//frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
private void updateShowTouchesFromSettings() {
int setting = getShowTouchesSetting(0);
nativeSetShowTouches(mPtr, setting != 0);
}
private void registerShowTouchesSettingObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.SHOW_TOUCHES), true,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateShowTouchesFromSettings();
}
}, UserHandle.USER_ALL);
}
可以看出开机时在这里注册了监听,当Settings.System.SHOW_TOUCHES值改变时调用updateShowTouchesFromSettings,接着调用
nativeSetShowTouches。这里开始进入c层逻辑了
3.2 c层处理
更新配置
很容易就可以找到
//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeSetShowTouches(JNIEnv* /* env */,
jclass /* clazz */, jlong ptr, jboolean enabled) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
im->setShowTouches(enabled);
}
void NativeInputManager::setShowTouches(bool enabled) {
{ // acquire lock
AutoMutex _l(mLock);
if (mLocked.showTouches == enabled) {
return;
}
ALOGI("Setting show touches feature to %s.", enabled ? "enabled" : "disabled");
mLocked.showTouches = enabled;
} // release lock
mInputManager->getReader()->requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
}
这里把enabled复制给了mLocked.showTouches(后面要考)。一看就知道要找一下requestRefreshConfiguration。
随便一搜
//frameworks/native/services/inputflinger/reader/InputReader.cpp
void InputReader::requestRefreshConfiguration(uint32_t changes) {
AutoMutex _l(mLock);
if (changes) {
bool needWake = !mConfigurationChangesToRefresh;
mConfigurationChangesToRefresh |= changes;
if (needWake) {
mEventHub->wake();
}
}
}
经过一番艰苦的看代码。
//frameworks/native/services/inputflinger/reader/InputReader.cpp
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
std::vector<InputDeviceInfo> inputDevices;
{ // acquire lock
AutoMutex _l(mLock);
oldGeneration = mGeneration;
timeoutMillis = -1;
uint32_t changes = mConfigurationChangesToRefresh;
if (changes) {
mConfigurationChangesToRefresh = 0;
timeoutMillis = 0;
refreshConfigurationLocked(changes);
} else if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
}
} // release lock
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) {
processEventsLocked(mEventBuffer, count);
}
if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTS
ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
mNextTimeout = LLONG_MAX;
timeoutExpiredLocked(now);
}
}
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
getInputDevicesLocked(inputDevices);
}
} // release lock
// Send out a message that the describes the changed input devices.
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
// on another thread similarly waiting to acquire the InputReader lock thereby
// resulting in a deadlock. This situation is actually quite plausible because the
// listener is actually the input dispatcher, which calls into the window manager,
// which occasionally calls into the input reader.
mQueuedListener->flush();
}
void InputReader::refreshConfigurationLocked(uint32_t changes) {
mPolicy->getReaderConfiguration(&mConfig);
mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
if (changes) {
ALOGI("Reconfiguring input devices, changes=%s",
InputReaderConfiguration::changesToString(changes).c_str());
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) {
updatePointerDisplayLocked();
}
if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
mEventHub->requestReopenDevices();
} else {
for (auto& devicePair : mDevices) {
std::shared_ptr<InputDevice>& device = devicePair.second;
device->configure(now, &mConfig, changes);
}
}
}
}
这里函数名虽是loopOnce,但实际是个循环,因为InputThread实现了threadLoop,会一直循环调用loopOnce。
改变了mConfigurationChangesToRefresh的值后唤醒了线程,线程处理完事件后,继续循环调用loopOnce。
接着就会调用refreshConfigurationLocked!
mPolicy->getReaderConfiguration(&mConfig);这个实际会调到
//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
jint virtualKeyQuietTime = env->CallIntMethod(mServiceObj,
gServiceClassInfo.getVirtualKeyQuietTimeMillis);
if (!checkAndClearExceptionFromCallback(env, "getVirtualKeyQuietTimeMillis")) {
outConfig->virtualKeyQuietTime = milliseconds_to_nanoseconds(virtualKeyQuietTime);
}
outConfig->excludedDeviceNames.clear();
jobjectArray excludedDeviceNames = jobjectArray(env->CallStaticObjectMethod(
gServiceClassInfo.clazz, gServiceClassInfo.getExcludedDeviceNames));
if (!checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && excludedDeviceNames) {
jsize length = env->GetArrayLength(excludedDeviceNames);
for (jsize i = 0; i < length; i++) {
std::string deviceName = getStringElementFromJavaArray(env, excludedDeviceNames, i);
outConfig->excludedDeviceNames.push_back(deviceName);
}
env->DeleteLocalRef(excludedDeviceNames);
}
// Associations between input ports and display ports
// The java method packs the information in the following manner:
// Original data: [{'inputPort1': '1'}, {'inputPort2': '2'}]
// Received data: ['inputPort1', '1', 'inputPort2', '2']
// So we unpack accordingly here.
outConfig->portAssociations.clear();
jobjectArray portAssociations = jobjectArray(env->CallObjectMethod(mServiceObj,
gServiceClassInfo.getInputPortAssociations));
if (!checkAndClearExceptionFromCallback(env, "getInputPortAssociations") && portAssociations) {
jsize length = env->GetArrayLength(portAssociations);
for (jsize i = 0; i < length / 2; i++) {
std::string inputPort = getStringElementFromJavaArray(env, portAssociations, 2 * i);
std::string displayPortStr =
getStringElementFromJavaArray(env, portAssociations, 2 * i + 1);
uint8_t displayPort;
// Should already have been validated earlier, but do it here for safety.
bool success = ParseUint(displayPortStr, &displayPort);
if (!success) {
ALOGE("Could not parse entry in port configuration file, received: %s",
displayPortStr.c_str());
continue;
}
outConfig->portAssociations.insert({inputPort, displayPort});
}
env->DeleteLocalRef(portAssociations);
}
jint hoverTapTimeout = env->CallIntMethod(mServiceObj,
gServiceClassInfo.getHoverTapTimeout);
if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) {
jint doubleTapTimeout = env->CallIntMethod(mServiceObj,
gServiceClassInfo.getDoubleTapTimeout);
if (!checkAndClearExceptionFromCallback(env, "getDoubleTapTimeout")) {
jint longPressTimeout = env->CallIntMethod(mServiceObj,
gServiceClassInfo.getLongPressTimeout);
if (!checkAndClearExceptionFromCallback(env, "getLongPressTimeout")) {
outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(hoverTapTimeout);
// We must ensure that the tap-drag interval is significantly shorter than
// the long-press timeout because the tap is held down for the entire duration
// of the double-tap timeout.
jint tapDragInterval = max(min(longPressTimeout - 100,
doubleTapTimeout), hoverTapTimeout);
outConfig->pointerGestureTapDragInterval =
milliseconds_to_nanoseconds(tapDragInterval);
}
}
}
jint hoverTapSlop = env->CallIntMethod(mServiceObj,
gServiceClassInfo.getHoverTapSlop);
if (!checkAndClearExceptionFromCallback(env, "getHoverTapSlop")) {
outConfig->pointerGestureTapSlop = hoverTapSlop;
}
{ // acquire lock
AutoMutex _l(mLock);
outConfig->pointerVelocityControlParameters.scale = exp2f(mLocked.pointerSpeed
* POINTER_SPEED_EXPONENT);
outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled;
outConfig->showTouches = mLocked.showTouches;
outConfig->pointerCapture = mLocked.pointerCapture;
outConfig->setDisplayViewports(mLocked.viewports);
outConfig->defaultPointerDisplayId = mLocked.pointerDisplayId;
outConfig->disabledDevices = mLocked.disabledInputDevices;
} // release lock
}
这里关键是outConfig->showTouches = mLocked.showTouches;,把刚才暂存的mLocked.showTouches赋值给了outConfig->showTouches.
继续上面的refreshConfigurationLocked分析,根据之前传的值InputReaderConfiguration::CHANGE_SHOW_TOUCHES,这里会走else分支,每个设备去执行configure。
//frameworks/native/services/inputflinger/reader/InputDevice.cpp
void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
uint32_t changes) {
mSources = 0;
mClasses = 0;
mControllerNumber = 0;
for_each_subdevice([this](InputDeviceContext& context) {
mClasses |= context.getDeviceClasses();
int32_t controllerNumber = context.getDeviceControllerNumber();
if (controllerNumber > 0) {
if (mControllerNumber && mControllerNumber != controllerNumber) {
ALOGW("InputDevice::configure(): composite device contains multiple unique "
"controller numbers");
}
mControllerNumber = controllerNumber;
}
});
mIsExternal = !!(mClasses & INPUT_DEVICE_CLASS_EXTERNAL);
mHasMic = !!(mClasses & INPUT_DEVICE_CLASS_MIC);
if (!isIgnored()) {
if (!changes) { // first time only
mConfiguration.clear();
for_each_subdevice([this](InputDeviceContext& context) {
PropertyMap configuration;
context.getConfiguration(&configuration);
mConfiguration.addAll(&configuration);
});
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
sp<KeyCharacterMap> keyboardLayout =
mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
bool shouldBumpGeneration = false;
for_each_subdevice(
[&keyboardLayout, &shouldBumpGeneration](InputDeviceContext& context) {
if (context.setKeyboardLayoutOverlay(keyboardLayout)) {
shouldBumpGeneration = true;
}
});
if (shouldBumpGeneration) {
bumpGeneration();
}
}
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
if (mAlias != alias) {
mAlias = alias;
bumpGeneration();
}
}
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) {
auto it = config->disabledDevices.find(mId);
bool enabled = it == config->disabledDevices.end();
setEnabled(enabled, when);
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
// In most situations, no port will be specified.
mAssociatedDisplayPort = std::nullopt;
mAssociatedViewport = std::nullopt;
// Find the display port that corresponds to the current input port.
const std::string& inputPort = mIdentifier.location;
if (!inputPort.empty()) {
const std::unordered_map<std::string, uint8_t>& ports = config->portAssociations;
const auto& displayPort = ports.find(inputPort);
if (displayPort != ports.end()) {
mAssociatedDisplayPort = std::make_optional(displayPort->second);
}
}
// If the device was explicitly disabled by the user, it would be present in the
// "disabledDevices" list. If it is associated with a specific display, and it was not
// explicitly disabled, then enable/disable the device based on whether we can find the
// corresponding viewport.
bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end());
if (mAssociatedDisplayPort) {
mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort);
if (!mAssociatedViewport) {
ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
"but the corresponding viewport is not found.",
getName().c_str(), *mAssociatedDisplayPort);
enabled = false;
}
}
if (changes) {
// For first-time configuration, only allow device to be disabled after mappers have
// finished configuring. This is because we need to read some of the properties from
// the device's open fd.
setEnabled(enabled, when);
}
}
for_each_mapper([this, when, config, changes](InputMapper& mapper) {
mapper.configure(when, config, changes);
mSources |= mapper.getSources();
});
// If a device is just plugged but it might be disabled, we need to update some info like
// axis range of touch from each InputMapper first, then disable it.
if (!changes) {
setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when);
}
}
}
这里主要调用了mapper.configure.把修改后的config传了进去
全局搜一下InputReaderConfiguration::CHANGE_SHOW_TOUCHES可以找到
//frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
uint32_t changes) {
InputMapper::configure(when, config, changes);
mConfig = *config;
if (!changes) { // first time only
// Configure basic parameters.
configureParameters();
// Configure common accumulators.
mCursorScrollAccumulator.configure(getDeviceContext());
mTouchButtonAccumulator.configure(getDeviceContext());
// Configure absolute axis information.
configureRawPointerAxes();
// Prepare input device calibration.
parseCalibration();
resolveCalibration();
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) {
// Update location calibration to reflect current settings
updateAffineTransformation();
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
// Update pointer speed.
mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
}
bool resetNeeded = false;
if (!changes ||
(changes &
(InputReaderConfiguration::CHANGE_DISPLAY_INFO |
InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
// Configure device sources, surface dimensions, orientation and
// scaling factors.
configureSurface(when, &resetNeeded);
}
if (changes && resetNeeded) {
// Send reset, unless this is the first time the device has been configured,
// in which case the reader will call reset itself after all mappers are ready.
NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
getListener()->notifyDeviceReset(&args);
}
}
这里调用了configureSurface.
configureSurface代码很长,这里贴一下关键的
// Create pointer controller if needed.
if (mDeviceMode == DEVICE_MODE_POINTER ||
(mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
if (mPointerController == nullptr) {
mPointerController = getContext()->getPointerController(getDeviceId());
}
} else {
mPointerController.clear();
}
这里用到了mConfig.showTouches,然后新建了点的控制对象。
重置设备
回到上面configure,最后调用了getListener()->notifyDeviceReset(&args);发出了设备重置的通知。
这个通知实际上是
//frameworks/native/services/inputflinger/InputListener.cpp
void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
traceEvent(__func__, args->id);
mArgsQueue.push_back(new NotifyDeviceResetArgs(*args));
}
void QueuedInputListener::flush() {
size_t count = mArgsQueue.size();
for (size_t i = 0; i < count; i++) {
NotifyArgs* args = mArgsQueue[i];
args->notify(mInnerListener);
delete args;
}
mArgsQueue.clear();
}
压入处理队列,等inputreader循环线程最后去flush。
之后调用到
//frameworks/native/services/inputflinger/InputClassifier.cpp
void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
std::scoped_lock lock(mLock);
if (mMotionClassifier) {
mMotionClassifier->reset(*args);
}
// continue to next stage
mListener->notifyDeviceReset(args);
}
可以看到这里用mMotionClassifier->reset(*args);重置了设备,然后调用mListener->notifyDeviceReset(args);
这里的mListener即InputDispatcher.先简单看下做了什么,再看如何重置设备
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
args->deviceId);
#endif
bool needWake;
{ // acquire lock
std::scoped_lock _l(mLock);
DeviceResetEntry* newEntry =
new DeviceResetEntry(args->id, args->eventTime, args->deviceId);
needWake = enqueueInboundEventLocked(newEntry);
} // release lock
if (needWake) {
mLooper->wake();
}
}
这里调用了enqueueInboundEventLocked,将消息压入队列
然后在dispatchOnceInnerLocked中取出使用
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now();
// Reset the key repeat timer whenever normal dispatch is suspended while the
// device is in a non-interactive state. This is to ensure that we abort a key
// repeat if the device is just coming out of sleep.
if (!mDispatchEnabled) {
resetKeyRepeatLocked();
}
// If dispatching is frozen, do not process timeouts or try to deliver any new events.
if (mDispatchFrozen) {
if (DEBUG_FOCUS) {
ALOGD("Dispatch frozen. Waiting some more.");
}
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.empty()) {
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) {
return;
}
} else {
// Inbound queue has at least one entry.
mPendingEvent = mInboundQueue.front();
mInboundQueue.pop_front();
traceInboundQueueLengthLocked();
}
// 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.
ALOG_ASSERT(mPendingEvent != nullptr);
bool done = false;
DropReason dropReason = DropReason::NOT_DROPPED;
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
dropReason = DropReason::POLICY;
} else if (!mDispatchEnabled) {
dropReason = DropReason::DISABLED;
}
if (mNextUnblockedEvent == mPendingEvent) {
mNextUnblockedEvent = nullptr;
}
switch (mPendingEvent->type) {
case EventEntry::Type::CONFIGURATION_CHANGED: {
ConfigurationChangedEntry* typedEntry =
static_cast<ConfigurationChangedEntry*>(mPendingEvent);
done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped
break;
}
case EventEntry::Type::DEVICE_RESET: {
DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent);
done = dispatchDeviceResetLocked(currentTime, typedEntry);
dropReason = DropReason::NOT_DROPPED; // device resets are never dropped
break;
}
case EventEntry::Type::FOCUS: {
FocusEntry* typedEntry = static_cast<FocusEntry*>(mPendingEvent);
dispatchFocusLocked(currentTime, typedEntry);
done = true;
dropReason = DropReason::NOT_DROPPED; // focus events are never dropped
break;
}
case EventEntry::Type::KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
if (isAppSwitchDue) {
if (isAppSwitchKeyEvent(*typedEntry)) {
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DropReason::NOT_DROPPED) {
dropReason = DropReason::APP_SWITCH;
}
}
if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
dropReason = DropReason::STALE;
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DropReason::BLOCKED;
}
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
case EventEntry::Type::MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
dropReason = DropReason::APP_SWITCH;
}
if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
dropReason = DropReason::STALE;
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DropReason::BLOCKED;
}
done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
}
if (done) {
if (dropReason != DropReason::NOT_DROPPED) {
dropInboundEventLocked(*mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
releasePendingEventLocked();
*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}
根据DeviceResetEntry的type,这里主要是调用dispatchDeviceResetLocked
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry->eventTime,
entry->deviceId);
#endif
CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset");
options.deviceId = entry->deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
return true;
}
接着调用synthesizeCancelationEventsForAllConnectionsLocked
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
const CancelationOptions& options) {
for (const auto& pair : mConnectionsByFd) {
synthesizeCancelationEventsForConnectionLocked(pair.second, options);
}
}
接着调用synthesizeCancelationEventsForConnectionLocked.
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
const sp<Connection>& connection, const CancelationOptions& options) {
if (connection->status == Connection::STATUS_BROKEN) {
return;
}
nsecs_t currentTime = now();
std::vector<EventEntry*> cancelationEvents =
connection->inputState.synthesizeCancelationEvents(currentTime, options);
if (cancelationEvents.empty()) {
return;
}
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
"with reality: %s, mode=%d.",
connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
options.mode);
#endif
InputTarget target;
sp<InputWindowHandle> windowHandle =
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
windowInfo->windowXScale, windowInfo->windowYScale);
target.globalScaleFactor = windowInfo->globalScaleFactor;
}
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
for (size_t i = 0; i < cancelationEvents.size(); i++) {
EventEntry* cancelationEventEntry = cancelationEvents[i];
switch (cancelationEventEntry->type) {
case EventEntry::Type::KEY: {
logOutboundKeyDetails("cancel - ",
static_cast<const KeyEntry&>(*cancelationEventEntry));
break;
}
case EventEntry::Type::MOTION: {
logOutboundMotionDetails("cancel - ",
static_cast<const MotionEntry&>(*cancelationEventEntry));
break;
}
case EventEntry::Type::FOCUS: {
LOG_ALWAYS_FATAL("Canceling focus events is not supported");
break;
}
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
EventEntry::typeToString(cancelationEventEntry->type));
break;
}
}
enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
target, InputTarget::FLAG_DISPATCH_AS_IS);
cancelationEventEntry->release();
}
startDispatchCycleLocked(currentTime, connection);
}
其中synthesizeCancelationEvents为
//frameworks/native/services/inputflinger/dispatcher/InputState.cpp
std::vector<EventEntry*> InputState::synthesizeCancelationEvents(
nsecs_t currentTime, const CancelationOptions& options) {
std::vector<EventEntry*> events;
for (KeyMemento& memento : mKeyMementos) {
if (shouldCancelKey(memento, options)) {
events.push_back(new KeyEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
memento.source, memento.displayId, memento.policyFlags,
AKEY_EVENT_ACTION_UP,
memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode,
memento.scanCode, memento.metaState, 0 /*repeatCount*/,
memento.downTime));
}
}
for (const MotionMemento& memento : mMotionMementos) {
if (shouldCancelMotion(memento, options)) {
const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT
: AMOTION_EVENT_ACTION_CANCEL;
events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
memento.source, memento.displayId, memento.policyFlags,
action, 0 /*actionButton*/, memento.flags, AMETA_NONE,
0 /*buttonState*/, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
memento.yPrecision, memento.xCursorPosition,
memento.yCursorPosition, memento.downTime,
memento.pointerCount, memento.pointerProperties,
memento.pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/));
}
}
return events;
}
bool InputState::shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options) {
if (options.keyCode && memento.keyCode != options.keyCode.value()) {
return false;
}
if (options.deviceId && memento.deviceId != options.deviceId.value()) {
return false;
}
if (options.displayId && memento.displayId != options.displayId.value()) {
return false;
}
switch (options.mode) {
case CancelationOptions::CANCEL_ALL_EVENTS:
case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
return true;
case CancelationOptions::CANCEL_FALLBACK_EVENTS:
return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
default:
return false;
}
}
bool InputState::shouldCancelMotion(const MotionMemento& memento,
const CancelationOptions& options) {
if (options.deviceId && memento.deviceId != options.deviceId.value()) {
return false;
}
if (options.displayId && memento.displayId != options.displayId.value()) {
return false;
}
switch (options.mode) {
case CancelationOptions::CANCEL_ALL_EVENTS:
return true;
case CancelationOptions::CANCEL_POINTER_EVENTS:
return memento.source & AINPUT_SOURCE_CLASS_POINTER;
case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
default:
return false;
}
}
这里根据deviceid和上面传入的CANCEL_ALL_EVENTS的options,这里会返回两个事件,取消keyevent事件和取消motionevent事件。
即重置设备后,需要取消设备已经发出的keyevent和motionevent。因为设备重置了,这些事件也就无用了,取消掉放置资源浪费。
重点看下如何重置设备
//frameworks/native/services/inputflinger/InputClassifier.cpp
/**
* Per-device reset. Clear the outstanding events that are going to be sent to HAL.
* Request InputClassifier thread to call resetDevice for this particular device.
*/
void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
int32_t deviceId = args.deviceId;
// Clear the pending events right away, to avoid unnecessary work done by the HAL.
mEvents.erase([deviceId](const ClassifierEvent& event) {
std::optional<int32_t> eventDeviceId = event.getDeviceId();
return eventDeviceId && (*eventDeviceId == deviceId);
});
enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
}
这里先清掉没必要的事件后,来执行重置事件
//frameworks/native/services/inputflinger/InputClassifier.cpp
void MotionClassifier::enqueueEvent(ClassifierEvent&& event) {
bool eventAdded = mEvents.push(std::move(event));
if (!eventAdded) {
// If the queue is full, suspect the HAL is slow in processing the events.
ALOGE("Could not add the event to the queue. Resetting");
reset();
}
}
将事件压入mEvents。然后在processEvents中 取出处理
//frameworks/native/services/inputflinger/InputClassifier.cpp
void MotionClassifier::processEvents() {
while (true) {
ClassifierEvent event = mEvents.pop();
bool halResponseOk = true;
switch (event.type) {
case ClassifierEventType::MOTION: {
NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
common::V1_0::MotionEvent motionEvent =
notifyMotionArgsToHalMotionEvent(*motionArgs);
Return<common::V1_0::Classification> response = mService->classify(motionEvent);
halResponseOk = response.isOk();
if (halResponseOk) {
common::V1_0::Classification halClassification = response;
updateClassification(motionArgs->deviceId, motionArgs->eventTime,
getMotionClassification(halClassification));
}
break;
}
case ClassifierEventType::DEVICE_RESET: {
const int32_t deviceId = *(event.getDeviceId());
halResponseOk = mService->resetDevice(deviceId).isOk();
clearDeviceState(deviceId);
break;
}
case ClassifierEventType::HAL_RESET: {
halResponseOk = mService->reset().isOk();
clearClassifications();
break;
}
case ClassifierEventType::EXIT: {
clearClassifications();
return;
}
}
if (!halResponseOk) {
ALOGE("Error communicating with InputClassifier HAL. "
"Exiting MotionClassifier HAL thread");
clearClassifications();
return;
}
}
}
这里主要调用到了mService->resetDevice(deviceId).isOk();这个service通过hal通信调用驱动去重置设备。
重置设备后处理
resetDevice后,有产生一个消息被InputHub获取到
//hardware/libhardware/modules/input/evdev/InputHub.cpp
status_t InputHub::poll() {
bool deviceChange = false;
if (manageWakeLocks()) {
// Mind the wake lock dance!
// If we're relying on wake locks, we hold a wake lock at all times
// except during epoll_wait(). This works due to some subtle
// choreography. When a device driver has pending (unread) events, it
// acquires a kernel wake lock. However, once the last pending event
// has been read, the device driver will release the kernel wake lock.
// To prevent the system from going to sleep when this happens, the
// InputHub holds onto its own user wake lock while the client is
// processing events. Thus the system can only sleep if there are no
// events pending or currently being processed.
release_wake_lock(WAKE_LOCK_ID);
}
struct epoll_event pendingEventItems[EPOLL_MAX_EVENTS];
int pollResult = epoll_wait(mEpollFd, pendingEventItems, EPOLL_MAX_EVENTS, NO_TIMEOUT);
if (manageWakeLocks()) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
}
if (pollResult == 0) {
ALOGW("epoll_wait should not return 0 with no timeout");
return UNKNOWN_ERROR;
}
if (pollResult < 0) {
// An error occurred. Return even if it's EINTR, and let the caller
// restart the poll.
ALOGE("epoll_wait returned with errno=%d", errno);
return -errno;
}
// pollResult > 0: there are events to process
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
std::vector<int> removedDeviceFds;
int inputFd = -1;
std::shared_ptr<InputDeviceNode> deviceNode;
for (int i = 0; i < pollResult; ++i) {
const struct epoll_event& eventItem = pendingEventItems[i];
int dataFd = static_cast<int>(eventItem.data.u32);
if (dataFd == mINotifyFd) {
if (eventItem.events & EPOLLIN) {
deviceChange = true;
} else {
ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
}
continue;
}
if (dataFd == mWakeEventFd) {
if (eventItem.events & EPOLLIN) {
ALOGV("awoken after wake()");
uint64_t u;
ssize_t nRead = TEMP_FAILURE_RETRY(read(mWakeEventFd, &u, sizeof(uint64_t)));
if (nRead != sizeof(uint64_t)) {
ALOGW("Could not read event fd; waking anyway.");
}
} else {
ALOGW("Received unexpected epoll event 0x%08x for wake event.",
eventItem.events);
}
continue;
}
// Update the fd and device node when the fd changes. When several
// events are read back-to-back with the same fd, this saves many reads
// from the hash table.
if (inputFd != dataFd) {
inputFd = dataFd;
deviceNode = mDeviceNodes[inputFd];
}
if (deviceNode == nullptr) {
ALOGE("could not find device node for fd %d", inputFd);
continue;
}
if (eventItem.events & EPOLLIN) {
struct input_event ievs[INPUT_MAX_EVENTS];
for (;;) {
ssize_t readSize = TEMP_FAILURE_RETRY(read(inputFd, ievs, sizeof(ievs)));
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
ALOGW("could not get event, removed? (fd: %d, size: %zd errno: %d)",
inputFd, readSize, errno);
removedDeviceFds.push_back(inputFd);
break;
} else if (readSize < 0) {
if (errno != EAGAIN && errno != EINTR) {
ALOGW("could not get event. errno=%d", errno);
}
break;
} else if (readSize % sizeof(input_event) != 0) {
ALOGE("could not get event. wrong size=%zd", readSize);
break;
} else {
size_t count = static_cast<size_t>(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; ++i) {
auto& iev = ievs[i];
auto when = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
InputEvent inputEvent = { when, iev.type, iev.code, iev.value };
mInputCallback->onInputEvent(deviceNode, inputEvent, now);
}
}
}
} else if (eventItem.events & EPOLLHUP) {
ALOGI("Removing device fd %d due to epoll hangup event.", inputFd);
removedDeviceFds.push_back(inputFd);
} else {
ALOGW("Received unexpected epoll event 0x%08x for device fd %d",
eventItem.events, inputFd);
}
}
if (removedDeviceFds.size()) {
for (auto deviceFd : removedDeviceFds) {
auto deviceNode = mDeviceNodes[deviceFd];
if (deviceNode != nullptr) {
status_t ret = closeNodeByFd(deviceFd);
if (ret != OK) {
ALOGW("Could not close device with fd %d. errno=%d", deviceFd, ret);
} else {
mInputCallback->onDeviceRemoved(deviceNode);
}
}
}
}
if (deviceChange) {
readNotify();
}
return OK;
}
然后这里会调用mInputCallback->onInputEvent(deviceNode, inputEvent, now);
这个mInputCallback即InputDeviceManager.
在
//hardware/libhardware/modules/input/evdev/EvdevModule.cpp
EvdevModule::EvdevModule(InputHostInterface* inputHost) :
mInputHost(inputHost),
mDeviceManager(std::make_shared<InputDeviceManager>(mInputHost.get())),
mInputHub(std::make_unique<InputHub>(mDeviceManager)) {}
这里创建.
//hardware/libhardware/modules/input/evdev/InputDeviceManager.cpp
void InputDeviceManager::onInputEvent(const std::shared_ptr<InputDeviceNode>& node, InputEvent& event,
nsecs_t event_time) {
if (mDevices[node] == nullptr) {
ALOGE("got input event for unknown node %s", node->getPath().c_str());
return;
}
mDevices[node]->processInput(event, event_time);
}
这里调用了device的processInput。
然后
//hardware/libhardware/modules/input/evdev/InputDevice.cpp
void EvdevDevice::processInput(InputEvent& event, nsecs_t currentTime) {
#if DEBUG_INPUT_EVENTS
std::string log;
log.append("---InputEvent for device %s---\n");
log.append(" when: %" PRId64 "\n");
log.append(" type: %d\n");
log.append(" code: %d\n");
log.append(" value: %d\n");
ALOGD(log.c_str(), mDeviceNode->getPath().c_str(), event.when, event.type, event.code,
event.value);
#endif
// Bug 7291243: Add a guard in case the kernel generates timestamps
// that appear to be far into the future because they were generated
// using the wrong clock source.
//
// This can happen because when the input device is initially opened
// it has a default clock source of CLOCK_REALTIME. Any input events
// enqueued right after the device is opened will have timestamps
// generated using CLOCK_REALTIME. We later set the clock source
// to CLOCK_MONOTONIC but it is already too late.
//
// Invalid input event timestamps can result in ANRs, crashes and
// and other issues that are hard to track down. We must not let them
// propagate through the system.
//
// Log a warning so that we notice the problem and recover gracefully.
if (event.when >= currentTime + s2ns(10)) {
// Double-check. Time may have moved on.
auto time = systemTime(SYSTEM_TIME_MONOTONIC);
if (event.when > time) {
ALOGW("An input event from %s has a timestamp that appears to have "
"been generated using the wrong clock source (expected "
"CLOCK_MONOTONIC): event time %" PRId64 ", current time %" PRId64
", call time %" PRId64 ". Using current time instead.",
mDeviceNode->getPath().c_str(), event.when, time, currentTime);
event.when = time;
} else {
ALOGV("Event time is ok but failed the fast path and required an extra "
"call to systemTime: event time %" PRId64 ", current time %" PRId64
", call time %" PRId64 ".", event.when, time, currentTime);
}
}
for (size_t i = 0; i < mMappers.size(); ++i) {
mMappers[i]->process(event);
}
}
调用mapper的process。
//frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
void TouchInputMapper::process(const RawEvent* rawEvent) {
mCursorButtonAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
mTouchButtonAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
sync(rawEvent->when);
}
}
调用sync。
//frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
void TouchInputMapper::sync(nsecs_t when) {
const RawState* last =
mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back();
// Push a new state.
mRawStatesPending.emplace_back();
RawState* next = &mRawStatesPending.back();
next->clear();
next->when = when;
// Sync button state.
next->buttonState =
mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState();
// Sync scroll
next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
mCursorScrollAccumulator.finishSync();
// Sync touch
syncTouch(when, next);
// Assign pointer ids.
if (!mHavePointerIds) {
assignPointerIds(last, next);
}
#if DEBUG_RAW_EVENTS
ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
"hovering ids 0x%08x -> 0x%08x",
last->rawPointerData.pointerCount, next->rawPointerData.pointerCount,
last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value,
last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value);
#endif
processRawTouches(false /*timeout*/);
}
调用processRawTouches
//frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
void TouchInputMapper::processRawTouches(bool timeout) {
if (mDeviceMode == DEVICE_MODE_DISABLED) {
// Drop all input if the device is disabled.
mCurrentRawState.clear();
mRawStatesPending.clear();
return;
}
// Drain any pending touch states. The invariant here is that the mCurrentRawState is always
// valid and must go through the full cook and dispatch cycle. This ensures that anything
// touching the current state will only observe the events that have been dispatched to the
// rest of the pipeline.
const size_t N = mRawStatesPending.size();
size_t count;
for (count = 0; count < N; count++) {
const RawState& next = mRawStatesPending[count];
// A failure to assign the stylus id means that we're waiting on stylus data
// and so should defer the rest of the pipeline.
if (assignExternalStylusId(next, timeout)) {
break;
}
// All ready to go.
clearStylusDataPendingFlags();
mCurrentRawState.copyFrom(next);
if (mCurrentRawState.when < mLastRawState.when) {
mCurrentRawState.when = mLastRawState.when;
}
cookAndDispatch(mCurrentRawState.when);
}
if (count != 0) {
mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
}
if (mExternalStylusDataPending) {
if (timeout) {
nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
clearStylusDataPendingFlags();
mCurrentRawState.copyFrom(mLastRawState);
#if DEBUG_STYLUS_FUSION
ALOGD("Timeout expired, synthesizing event with new stylus data");
#endif
cookAndDispatch(when);
} else if (mExternalStylusFusionTimeout == LLONG_MAX) {
mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
}
}
}
调用cookAndDispatch
//frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
void TouchInputMapper::cookAndDispatch(nsecs_t when) {
// Always start with a clean state.
mCurrentCookedState.clear();
// Apply stylus buttons to current raw state.
applyExternalStylusButtonState(when);
// Handle policy on initial down or hover events.
bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 &&
mCurrentRawState.rawPointerData.pointerCount != 0;
uint32_t policyFlags = 0;
bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;
if (initialDown || buttonsPressed) {
// If this is a touch screen, hide the pointer on an initial down.
if (mDeviceMode == DEVICE_MODE_DIRECT) {
getContext()->fadePointer();
}
if (mParameters.wake) {
policyFlags |= POLICY_FLAG_WAKE;
}
}
// Consume raw off-screen touches before cooking pointer data.
// If touches are consumed, subsequent code will not receive any pointer data.
if (consumeRawTouches(when, policyFlags)) {
mCurrentRawState.rawPointerData.clear();
}
// Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure
// with cooked pointer data that has the same ids and indices as the raw data.
// The following code can use either the raw or cooked data, as needed.
cookPointerData();
// Apply stylus pressure to current cooked state.
applyExternalStylusTouchState(when);
// Synthesize key down from raw buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
mViewport.displayId, policyFlags, mLastCookedState.buttonState,
mCurrentCookedState.buttonState);
// Dispatch the touches either directly or by translation through a pointer on screen.
if (mDeviceMode == DEVICE_MODE_POINTER) {
for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
mCurrentCookedState.stylusIdBits.markBit(id);
} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER ||
pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
mCurrentCookedState.fingerIdBits.markBit(id);
} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {
mCurrentCookedState.mouseIdBits.markBit(id);
}
}
for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits); !idBits.isEmpty();) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
mCurrentCookedState.stylusIdBits.markBit(id);
}
}
// Stylus takes precedence over all tools, then mouse, then finger.
PointerUsage pointerUsage = mPointerUsage;
if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
mCurrentCookedState.mouseIdBits.clear();
mCurrentCookedState.fingerIdBits.clear();
pointerUsage = POINTER_USAGE_STYLUS;
} else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
mCurrentCookedState.fingerIdBits.clear();
pointerUsage = POINTER_USAGE_MOUSE;
} else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||
isPointerDown(mCurrentRawState.buttonState)) {
pointerUsage = POINTER_USAGE_GESTURES;
}
dispatchPointerUsage(when, policyFlags, pointerUsage);
} else {
if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches &&
mPointerController != nullptr) {
mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
mPointerController->setButtonState(mCurrentRawState.buttonState);
mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex,
mCurrentCookedState.cookedPointerData.touchingIdBits,
mViewport.displayId);
}
if (!mCurrentMotionAborted) {
dispatchButtonRelease(when, policyFlags);
dispatchHoverExit(when, policyFlags);
dispatchTouches(when, policyFlags);
dispatchHoverEnterAndMove(when, policyFlags);
dispatchButtonPress(when, policyFlags);
}
if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
mCurrentMotionAborted = false;
}
}
// Synthesize key up from raw buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
mViewport.displayId, policyFlags, mLastCookedState.buttonState,
mCurrentCookedState.buttonState);
// Clear some transient state.
mCurrentRawState.rawVScroll = 0;
mCurrentRawState.rawHScroll = 0;
// Copy current touch to last touch in preparation for the next cycle.
mLastRawState.copyFrom(mCurrentRawState);
mLastCookedState.copyFrom(mCurrentCookedState);
}
原点绘画逻辑
一层层下来,终于是使用到了mPointerController,重点是mPointerController–>setSpots
//frameworks/base/libs/input/PointerController.cpp
void PointerController::setSpots(const PointerCoords* spotCoords,
const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId) {
#if DEBUG_POINTER_UPDATES
ALOGD("setSpots: idBits=%08x", spotIdBits.value);
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
uint32_t id = idBits.firstMarkedBit();
idBits.clearBit(id);
const PointerCoords& c = spotCoords[spotIdToIndex[id]];
ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id,
c.getAxisValue(AMOTION_EVENT_AXIS_X),
c.getAxisValue(AMOTION_EVENT_AXIS_Y),
c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
displayId);
}
#endif
AutoMutex _l(mLock);
if (!mLocked.viewport.isValid()) {
return;
}
std::vector<Spot*> newSpots;
std::map<int32_t, std::vector<Spot*>>::const_iterator iter =
mLocked.spotsByDisplay.find(displayId);
if (iter != mLocked.spotsByDisplay.end()) {
newSpots = iter->second;
}
mSpriteController->openTransaction();
// Add or move spots for fingers that are down.
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
uint32_t id = idBits.clearFirstMarkedBit();
const PointerCoords& c = spotCoords[spotIdToIndex[id]];
const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
? mResources.spotTouch : mResources.spotHover;
float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
Spot* spot = getSpot(id, newSpots);
if (!spot) {
spot = createAndAddSpotLocked(id, newSpots);
}
spot->updateSprite(&icon, x, y, displayId);
}
// Remove spots for fingers that went up.
for (size_t i = 0; i < newSpots.size(); i++) {
Spot* spot = newSpots[i];
if (spot->id != Spot::INVALID_ID
&& !spotIdBits.hasBit(spot->id)) {
fadeOutAndReleaseSpotLocked(spot);
}
}
mSpriteController->closeTransaction();
mLocked.spotsByDisplay[displayId] = newSpots;
}
这里主要是spot->updateSprite(&icon, x, y, displayId);
//frameworks/base/libs/input/PointerController.cpp
void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
int32_t displayId) {
sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
sprite->setAlpha(alpha);
sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
sprite->setPosition(x, y);
sprite->setDisplayId(displayId);
this->x = x;
this->y = y;
if (icon != lastIcon) {
lastIcon = icon;
if (icon) {
sprite->setIcon(*icon);
sprite->setVisible(true);
} else {
sprite->setVisible(false);
}
}
}
这里主要是sprite->setVisible
//frameworks/base/libs/input/SpriteController.cpp
void SpriteController::SpriteImpl::setVisible(bool visible) {
AutoMutex _l(mController->mLock);
if (mLocked.state.visible != visible) {
mLocked.state.visible = visible;
invalidateLocked(DIRTY_VISIBILITY);
}
}
调用invalidateLocked
//frameworks/base/libs/input/SpriteController.cpp
void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
bool wasDirty = mLocked.state.dirty;
mLocked.state.dirty |= dirty;
if (!wasDirty) {
mController->invalidateSpriteLocked(this);
}
}
调用mController->invalidateSpriteLocked(this);
//frameworks/base/libs/input/SpriteController.cpp
void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) {
bool wasEmpty = mLocked.invalidatedSprites.isEmpty();
mLocked.invalidatedSprites.push(sprite);
if (wasEmpty) {
if (mLocked.transactionNestingCount != 0) {
mLocked.deferredSpriteUpdate = true;
} else {
mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
}
}
}
这里发了个消息,实际调用是
//frameworks/base/libs/input/SpriteController.cpp
void SpriteController::handleMessage(const Message& message) {
switch (message.what) {
case MSG_UPDATE_SPRITES:
doUpdateSprites();
break;
case MSG_DISPOSE_SURFACES:
doDisposeSurfaces();
break;
}
}
调用doUpdateSprites
void SpriteController::doUpdateSprites() {
// Collect information about sprite updates.
// Each sprite update record includes a reference to its associated sprite so we can
// be certain the sprites will not be deleted while this function runs. Sprites
// may invalidate themselves again during this time but we will handle those changes
// in the next iteration.
Vector<SpriteUpdate> updates;
size_t numSprites;
{ // acquire lock
AutoMutex _l(mLock);
numSprites = mLocked.invalidatedSprites.size();
for (size_t i = 0; i < numSprites; i++) {
const sp<SpriteImpl>& sprite = mLocked.invalidatedSprites.itemAt(i);
updates.push(SpriteUpdate(sprite, sprite->getStateLocked()));
sprite->resetDirtyLocked();
}
mLocked.invalidatedSprites.clear();
} // release lock
// Create missing surfaces.
bool surfaceChanged = false;
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) {
update.state.surfaceWidth = update.state.icon.bitmap.getInfo().width;
update.state.surfaceHeight = update.state.icon.bitmap.getInfo().height;
update.state.surfaceDrawn = false;
update.state.surfaceVisible = false;
update.state.surfaceControl = obtainSurface(
update.state.surfaceWidth, update.state.surfaceHeight);
if (update.state.surfaceControl != NULL) {
update.surfaceChanged = surfaceChanged = true;
}
}
}
// Resize and/or reparent sprites if needed.
SurfaceComposerClient::Transaction t;
bool needApplyTransaction = false;
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
if (update.state.surfaceControl == nullptr) {
continue;
}
if (update.state.wantSurfaceVisible()) {
int32_t desiredWidth = update.state.icon.bitmap.getInfo().width;
int32_t desiredHeight = update.state.icon.bitmap.getInfo().height;
if (update.state.surfaceWidth < desiredWidth
|| update.state.surfaceHeight < desiredHeight) {
needApplyTransaction = true;
t.setSize(update.state.surfaceControl,
desiredWidth, desiredHeight);
update.state.surfaceWidth = desiredWidth;
update.state.surfaceHeight = desiredHeight;
update.state.surfaceDrawn = false;
update.surfaceChanged = surfaceChanged = true;
if (update.state.surfaceVisible) {
t.hide(update.state.surfaceControl);
update.state.surfaceVisible = false;
}
}
}
// If surface is a new one, we have to set right layer stack.
if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) {
t.setLayerStack(update.state.surfaceControl, update.state.displayId);
needApplyTransaction = true;
}
}
if (needApplyTransaction) {
t.apply();
}
// Redraw sprites if needed.
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
if ((update.state.dirty & DIRTY_BITMAP) && update.state.surfaceDrawn) {
update.state.surfaceDrawn = false;
update.surfaceChanged = surfaceChanged = true;
}
if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn
&& update.state.wantSurfaceVisible()) {
sp<Surface> surface = update.state.surfaceControl->getSurface();
ANativeWindow_Buffer outBuffer;
status_t status = surface->lock(&outBuffer, NULL);
if (status) {
ALOGE("Error %d locking sprite surface before drawing.", status);
} else {
graphics::Paint paint;
paint.setBlendMode(ABLEND_MODE_SRC);
graphics::Canvas canvas(outBuffer, (int32_t) surface->getBuffersDataSpace());
canvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
const int iconWidth = update.state.icon.bitmap.getInfo().width;
const int iconHeight = update.state.icon.bitmap.getInfo().height;
if (outBuffer.width > iconWidth) {
paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent
canvas.drawRect({iconWidth, 0, outBuffer.width, iconHeight}, paint);
}
if (outBuffer.height > iconHeight) {
paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent
canvas.drawRect({0, iconHeight, outBuffer.width, outBuffer.height}, paint);
}
status = surface->unlockAndPost();
if (status) {
ALOGE("Error %d unlocking and posting sprite surface after drawing.", status);
} else {
update.state.surfaceDrawn = true;
update.surfaceChanged = surfaceChanged = true;
}
}
}
}
needApplyTransaction = false;
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
bool wantSurfaceVisibleAndDrawn = update.state.wantSurfaceVisible()
&& update.state.surfaceDrawn;
bool becomingVisible = wantSurfaceVisibleAndDrawn && !update.state.surfaceVisible;
bool becomingHidden = !wantSurfaceVisibleAndDrawn && update.state.surfaceVisible;
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
| DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID
| DIRTY_ICON_STYLE))))) {
needApplyTransaction = true;
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible || (update.state.dirty & DIRTY_ALPHA))) {
t.setAlpha(update.state.surfaceControl,
update.state.alpha);
}
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible || (update.state.dirty & (DIRTY_POSITION
| DIRTY_HOTSPOT)))) {
t.setPosition(
update.state.surfaceControl,
update.state.positionX - update.state.icon.hotSpotX,
update.state.positionY - update.state.icon.hotSpotY);
}
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible
|| (update.state.dirty & DIRTY_TRANSFORMATION_MATRIX))) {
t.setMatrix(
update.state.surfaceControl,
update.state.transformationMatrix.dsdx,
update.state.transformationMatrix.dtdx,
update.state.transformationMatrix.dsdy,
update.state.transformationMatrix.dtdy);
}
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible
|| (update.state.dirty & (DIRTY_HOTSPOT | DIRTY_ICON_STYLE)))) {
Parcel p;
p.writeInt32(update.state.icon.style);
p.writeFloat(update.state.icon.hotSpotX);
p.writeFloat(update.state.icon.hotSpotY);
// Pass cursor metadata in the sprite surface so that when Android is running as a
// client OS (e.g. ARC++) the host OS can get the requested cursor metadata and
// update mouse cursor in the host OS.
t.setMetadata(
update.state.surfaceControl, METADATA_MOUSE_CURSOR, p);
}
int32_t surfaceLayer = mOverlayLayer + update.state.layer;
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
t.setLayer(update.state.surfaceControl, surfaceLayer);
}
if (becomingVisible) {
t.show(update.state.surfaceControl);
update.state.surfaceVisible = true;
update.surfaceChanged = surfaceChanged = true;
} else if (becomingHidden) {
t.hide(update.state.surfaceControl);
update.state.surfaceVisible = false;
update.surfaceChanged = surfaceChanged = true;
}
}
}
if (needApplyTransaction) {
status_t status = t.apply();
if (status) {
ALOGE("Error applying Surface transaction");
}
}
// If any surfaces were changed, write back the new surface properties to the sprites.
if (surfaceChanged) { // acquire lock
AutoMutex _l(mLock);
for (size_t i = 0; i < numSprites; i++) {
const SpriteUpdate& update = updates.itemAt(i);
if (update.surfaceChanged) {
update.sprite->setSurfaceLocked(update.state.surfaceControl,
update.state.surfaceWidth, update.state.surfaceHeight,
update.state.surfaceDrawn, update.state.surfaceVisible);
}
}
} // release lock
// Clear the sprite update vector outside the lock. It is very important that
// we do not clear sprite references inside the lock since we could be releasing
// the last remaining reference to the sprite here which would result in the
// sprite being deleted and the lock being reacquired by the sprite destructor
// while already held.
updates.clear();
}
终于,在这里用canvas画出了圆点并交给surface显示!
没想到一个简单的触摸原点的开关竟然有这么复杂的逻辑!!!