显示点按操作反馈分析

1.描述

点按操作反馈可以在开发者模式中选择开启,开启后,当手指触摸屏幕 时,会出现一个小原点。

2.总结

先做个总结,以防有人没耐心看下去。。。

  1. 当在开发者模式界面打开显示点按操作反馈开关时,会通过ShowTapsPreferenceController改变Settings.System.SHOW_TOUCHES的值,而这个值在开机的时候就被注册了监听,会去调用nativeSetShowTouches,进入c层
  2. 然后通过输入事件的监听线程调用refreshConfigurationLocked,更新配置并新建了PointerControl.
  3. 然后通过input的事件分发线程通知去重置设备并清空之前的keyevent和motionevent。
  4. 重置设备使用了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显示!
没想到一个简单的触摸原点的开关竟然有这么复杂的逻辑!!!

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
周四客户关系软件分四大主要区域 : 1、操作区:为用户提供编辑数据时所需要的功能,如:增加、修改、删除,查询等。 2、增强区:增强区即数据显示条件区,主要为用户提供显示数据的分类、时间等条件。 3、功能区:是用户进入各功能模块的便捷途径,只要鼠标简单击。 4、数据显示区:这区域是主要的目标区域,显示用户需要的数据。 周四客户关系软件(CRM)架构于市场营销管理理论及客户资源管理的基础上,充分考虑到国内企业的实际情况和具体条件进行开发的。我们的功能模块直接体现在销售过程中的各个环节中,突出“ 以客户为中心 ”的管理思想。企业通过周四客户关系软件实现专业化集中管理和控制,使公司能及时了解销 售网络情况、客户动态、客户人员结构、竞争对手、竞争产品、产品价格、客户服务等信息。 周四客户关系软件更加适合那些以意向订单(项目)管理和客户关系集中 管理为目标的公司。形成了高度集中的信息共享体系。合理地处理了一线业务人员、公司销售指挥中心等工作衔接问题。下面我们将介绍周四客户关系软件的各大功能模块: 一、团队管理 销售行动中不可缺少的基础设定,集中建立销售相关人员的基础信息,根据公司的需求设置销售区域及各区域的人员结构等。体现出销售部门人员的层次性,同时根据各销售区域的不同,合理分布销售人员。 二、客户资源管理 客户是公司赢利的直接对象,所以对客户资料、客户关系等信息的维护非常重要,加强客户信息的管理和销售过程的严格控制、以及良好的客户关系,已是保持公司盈利的重要手段。怎么留住老客户、分析潜在赢利客户、发现新客 户,周四客户关系系统为您提供了非常方便实用的执行功能。同时也提供了强大的客户分类功能与客户跟进功能,全面整合了跟进客户信息、客户的人员结构、竞争对手信息、意向订单(项目)结构、计划任务及销售过程中出现的问题。 三、工作记录及工作任务管理 销售人员向公司反馈业务进展的主要入口,记录销售人员跟进客户情况、工作方式及业务进展情况。计划任务在销售过程中是不可缺少的环节,周四客户关系软件提供两个工作计划指针与一个智能工作总结分析模块 1 、销售人员进行下一步工作安排,明确自己的工作重,使工作更具条理性。同时销售管理中心作为 销售总后台,也可根据具体情况为销售人员安排工作任务。 2 、销售管理中心为销售人员制定合理的工作指标与工作计划量,目的是提高销售人员工作的主动性与 积极性。 3 、工作总结功能模板,方便销售人员进行周、月工作总结及管理人员进行区域(办事处)周、月工作 总结。 四、项目管理 周四客户关系软件将项目管理合理地引入销售工作环节中,我们可以将客户需求的一个产品、一个意向订单、某一工程或某一个计划看成一个项目。周四客户关系软件项目管理模块将清楚展现客户的人员结构、产品需求结构、客户的信誉度、客户的基本要求及公司的销售策略。项目管理引入的目的是使公司能更有效的分析客户的真实需求、将那些更有价值与潜在力的客户纳入公司客户群体中,减少销售人员的业务行为的盲目性。与客户分类一样,项目按区域(办事处)、业务员、等级进行分类。 五、产品结构管理 定义我们的销售产品及服务项目,将销售进行严格的细分,使业务人员更加明确公司的赢利方式。使销售人员站在公司最高利益的基础上提高业绩。 六、备忘录 业务过程中难免有些杂事,但与业务之间关系密切,为了提醒自己时时关注,备忘录是一个相当灵活的工具。备忘录中能记录临时要交代的或要办理的事情。让业务人员在业务过程中更具主动性。 七、工作请示与工作批示 销售工作过程中,遇到困难业务员可以直接作工作请示,销售管理中心能根据实际情况对业务员的工作进行有效的批示指导。 八、费用管理及文档资料管理中心 销售过程中产生费用是难免的,我们应该通过数据依据对费用进行有效的管控。 文档资料的集中管理也是非常必要的,将文档资料分类管理,更有利于文档资料的查询。 详细功能:www.z4-soft.com http://www.z4-soft.com/depict/周四客户关系软件(商务版试用).rar
14.1 推荐系统应用场景  亚马逊商城、淘宝、京东等等电商网站的(您可能感兴趣的图书、音像、服装、电子设备…)物品投放  QQ(您可能认识的)联系人推荐  新闻站相关题材内容资讯展示 我们每天都面临着大量事件的抉择,在没有有效辅助决策信息之前,从其他用户反馈信息中了解某事物的特性,可以加快我们抉择的过程。如:我们到没有去过的外地旅游之前,选择哪家餐馆就餐、哪些景观光、哪些酒店入住这些问题都可以通过参考推荐系统提供的信息得到很好的解答。 企业通过使用推荐系统,可增强用户体验、实现更好的交叉销售、提高营业额度。 14.2 学习目标 在学习完本节课后,您应能够:  解释什么是推荐系统、如何使用推荐系统。  识别推荐系统挖掘可能采取的数据格式,以便进行推荐系统构建。  在 RapidMiner 中开发推荐系统。 14.3 安装推荐系统插件  为了完成推荐系统操作,您需要确认已经安装了推荐系统插件,您可用以下步骤确认是否已经安装推荐系统插件: —打开RapidMiner,在主菜单击 Help > Manage Extensions —确认Recommender Extension已经显示在列表中,被选中,我们将使用5.1.1版本的推荐系统插件。  如果您还没有安装推荐系统插件,您可以按以下步骤完成安装: —如果您使用windows系统,确认您以Administrator权限的用户登录 —在主菜单上击 Help>Update and Extensions (Marketplace)… —在 search 标签中输入搜索关键字 Recommender,在结果列表中 击选择Recommender
关键词:声卡 数据采集 MATLAB 信号处理   论文摘要:利用数据采集卡构建的数据采集系统一般价格昂贵且难以与实际需求完 全匹配。声卡作为数据采集卡具有价格低廉、开发容易和系统灵活等优。本文详细介 绍了系统的开发背景,软件结构和特,系统地分析了数据采集硬件和软件设计技术,在此 基础上以声卡为数据采集卡,以MATLAB为开发平台设计了数据采集与分析系统。 本文介绍了MATLAB及其数据采集工具箱, 利用声卡的A/ D、D/ A 技术和MATLAB 的方便 编程及可视化功能,提出了一种基于声卡的数据采集与分析方案,该方案具有实现简单、 性价比和灵活度高的优。用MATLAB 语言编制了相应软件,实现了该系统。该软件有着 简洁的人机交互工作界面,操作方便,并且可以根据用户的需求进行功能扩充。最后给出 了应用该系统采集数据的应用实例。   1绪论   1.1 课题背景   数据也称观测值,是实验、测量、观察、调查等的结果,常以数量的形式给出。数 据采集,又称数据获取,就是将系统需要管理的所有对象的原始数据收集、归类、整理 、录入到系统当中去。数据采集是机管理系统使用前的一个数据初始化过程。数据采集 技术广泛引用在各个领域。比如摄像头,麦克风,都是数据采集工具。   数据采集(Data Acquisition)是将被测对象(外部世界、现场)的各种参量(可以是 物理量,也可以是化学量、生物量等)通过各种传感元件作适当转换后,再经信号调理、 采样、量化、编码、传输等步骤,最后送到控制器进行数据处理或存储记录的过程。   被采集数据是已被转换为电讯号的各种物理量,如温度、水位、风速、压力等,可 以是模拟量,也可以是数字量。采集一般是采样方式,即隔一定时间(称采样周期)对 同一数据重复采集。采集的数据大多是瞬时值,也可是某段时间内的一个特征值。准 确的数据测量是数据采集的基础。数据测量方法有接触式和非接触式,检测元件多种多 样。不论哪种方法和元件,都以不影响被测对象状态和测量环境为前提,以保证数据的 正确性。数据采集含义很广,包括对连续物理量的采集。在计算机辅助制图、测图、设 计中,对图形或图像数字化过程也可称为数据采集,此时被采集的是几何量数据。   在智能仪器、信号处理以及自动控制等领域,都存在着数据的测量与控制问题,常 常需要对外部的温度、压力、流量、位移等模拟量进行采集。数据采集技术是一种流行 且实用的技术。它广泛应用于信号检测、信号处理、仪器仪表等领域。近年来,随着数 字化技术的不断,数据采集技术也呈现出速度更高、通道更多、数据量更大的发展态势 。   数据采集系统是一种应用极为广泛的模拟量测量设备,其基本任务是把信号送入计 算机或相应的信号处理系统,根据不同的需要进行相应的计算和处理。它将模拟量采集 、转换成数字量后,再经过计算机处理得出所需的数据。同时,还可以用计算机将得到 的数据进行储存、显示和打印,以实现对某些物理量的监视,其中一部分数据还将被用 作生产过程中的反馈控制量。   数据采集系统是计算机测控系统中非常重要的环节,目前,有各种数据采集卡或采 集系统可供选择,以满足生产和科研试验等各方面的不同需要,但由于数据源以及用户 需求的多样性,有时并不能满足要求。特别是在某些应用中,需要同时高速采集多个通 道的数据,而且为了分析比较各通道信号间的相互关系,常常要求所有通道的采集必须 同步。现有的数据采集系统能够满足上述要求的比较少,且价格十分昂贵,体积较大, 分量较重,使用十分不方便。   一般模拟量是通过各种数据采集卡进行数据采集。目前常用的是具有 ISA 总线、P CI 总线等接口形式的 A/D 采集卡,虽然数据传输率很高,但是还存在整个系统笨重, 缺乏灵活性,不能实现即插即用,不适合小型、便携设备采用等缺。另外这些类型的 采集卡在计算机上安装比较麻烦,而且由于受计算机插槽数量、地址、中断资源的限制 不可能挂接很多设备。因此,工程师们往往需要花费大量的时间和资源用于系统搭建。   随着工业技术的迅猛发展,生产规模的不断壮大,生产过程和制作工艺的日趋复杂 ,对自动测试和各种信息集成的要求也就越来越高。数据采集系统的好坏将直接影响自 动测试系统的可靠性和稳定性,为了满足不同的测试需求,以及减少对资源的浪费,在 系统的设计上应该尽量满足通用性和可扩展性。在高度发展的当今社会中,技术的突飞 猛进和生产过程的高度自动化已成为人所共知的必然趋势,而它们的共同要求是必须建 立在有着不断发展与提高的信息工业基础上。人们只有从外界获取大量准确、可靠的信 息经过一系列的科学分析、处理、加工与判断,进而认识和掌握界与科学技术中的各种 现象与其相关的变化,并通过相应的系统和方法实现科学实验研究与生产过程的高度自 动化。换言之,生产过程的

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值