Android 11 触摸小圆点显示流程

在开发者选项中,打开 “显示点按操作反馈” 开关,当我们在触摸屏幕时,会显示一个小圆点,来分析下小圆点的显示流程。
操作这个开关时,其实就是操作Settings数据库中的 SHOW_TOUCHES

//packages\apps\Settings\src\com\android\settings\development\ShowTapsPreferenceController.java
@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;
    }

而在IMS中,会对 SHOW_TOUCHES 这个key进行监听

//frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
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);
    }

当SHOW_TOUCHES 这个key的值有改变时,调用updateShowTouchesFromSettings方法,在updateShowTouchesFromSettings方法中,是调用nativeSetShowTouches这个native方法,直接来看下这个方法

//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);//调用NativeInputManager的setShowTouches方法
}

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;//1
    } // release lock

    mInputManager->getReader()->requestRefreshConfiguration(
            InputReaderConfiguration::CHANGE_SHOW_TOUCHES);//2
}

注释1处mLocked.showTouches就为传进来的enabled(打开为true,关闭为false),注释2处调用InputReader的requestRefreshConfiguration继续处理

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::requestRefreshConfiguration(uint32_t changes) {
    AutoMutex _l(mLock);

    if (changes) {
        bool needWake = !mConfigurationChangesToRefresh;
        mConfigurationChangesToRefresh |= changes;//changes为InputReaderConfiguration::CHANGE_SHOW_TOUCHES

        if (needWake) {
            mEventHub->wake();//唤醒InputReader线程
        }
    }
}

InputReader线程被唤醒,loopOnce继续执行

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::loopOnce() {
    //省略
    { // acquire lock
        AutoMutex _l(mLock);
        
        uint32_t changes = mConfigurationChangesToRefresh;
        if (changes) {
            mConfigurationChangesToRefresh = 0;
            timeoutMillis = 0;
            refreshConfigurationLocked(changes);//1
        } else if (mNextTimeout != LLONG_MAX) {
           //省略
        }
    } // release lock

	//省略

注释1处,调用refreshConfigurationLocked继续处理

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::refreshConfigurationLocked(uint32_t changes) {
    mPolicy->getReaderConfiguration(&mConfig);//1
    mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);

    if (changes) {
       //省略
        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);//2
            }
        }
    }
}

注释1处主要是将前面的mLocked.showTouches又赋值给mConfig的showTouches,注释2处调用InputDevice的configure继续处理。
1,getReaderConfiguration

//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) {
	//省略

	{ // acquire lock
        AutoMutex _l(mLock);
		//省略

        outConfig->showTouches = mLocked.showTouches;//赋值
		
		//省略
    } //  release lock
}

2,InputDevice:configure

//frameworks\native\services\inputflinger\reader\InputDevice.cpp
void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
                            uint32_t changes) {
	//省略
	for_each_mapper([this, when, config, changes](InputMapper& mapper) {
            mapper.configure(when, config, changes);
            mSources |= mapper.getSources();
        });
	//省略

}

对于InputReaderConfiguration::CHANGE_SHOW_TOUCHES,又调用对应mapper的configure处理。多指触摸的话mapper为MultiTouchInputMapper,MultiTouchInputMapper没有实现configure方法,在其父类TouchInputMapper中实现

//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
                                 uint32_t changes) {
    //省略
    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);
    }
	//省略

调用configureSurface继续处理

//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
	//省略
	// 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();
    }
	//省略
}

getPointerController方法最终调用InputReader的getPointerControllerLocked方法

//frameworks\native\services\inputflinger\reader\InputReader.cpp
sp<PointerControllerInterface> InputReader::getPointerControllerLocked(int32_t deviceId) {
    sp<PointerControllerInterface> controller = mPointerController.promote();
    if (controller == nullptr) {
        controller = mPolicy->obtainPointerController(deviceId);//1
        mPointerController = controller;
        updatePointerDisplayLocked();//2
    }
    return controller;
}

注释1处创建PointerController对象,注释2处调用updatePointerDisplayLocked继续处理
1,obtainPointerController

//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32_t /* deviceId */) {
    ATRACE_CALL();
    AutoMutex _l(mLock);

    sp<PointerController> controller = mLocked.pointerController.promote();
    if (controller == nullptr) {
        ensureSpriteControllerLocked();//创建SpriteController对象

        controller = new PointerController(this, mLooper, mLocked.spriteController);//创建PointerController对象
        mLocked.pointerController = controller;
        updateInactivityTimeoutLocked();
    }

    return controller;
}

2,updatePointerDisplayLocked

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::updatePointerDisplayLocked() {
    sp<PointerControllerInterface> controller = mPointerController.promote();//取出前面创建的PointerController对象
    if (controller == nullptr) {
        return;
    }

   //省略

    controller->setDisplayViewport(*viewport);
}

来看一下PointerController的setDisplayViewport方法

frameworks\base\libs\input\PointerController.cpp
void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
	//省略
		loadResourcesLocked();//加载小圆点图片资源
	//省略

	updatePointerLocked();//保存资源信息到SpriteController中,其中包含了小圆点图片的bitmap

}

loadResourcesLocked加载图片信息,主要是通过JNI调用,调用到PointerIcon中的getSystemIcon方法中

//frameworks\base\core\java\android\view\PointerIcon.java
public static PointerIcon getSystemIcon(@NonNull Context context, int type) {
       //省略
        int typeIndex = getSystemIconTypeIndex(type);
        if (typeIndex == 0) {
            typeIndex = getSystemIconTypeIndex(TYPE_DEFAULT);
        }

        int defStyle = sUseLargeIcons ?
                com.android.internal.R.style.LargePointer : com.android.internal.R.style.Pointer;
        TypedArray a = context.obtainStyledAttributes(null,
                com.android.internal.R.styleable.Pointer,
                0, defStyle);
        int resourceId = a.getResourceId(typeIndex, -1);
        a.recycle();
        
        icon = new PointerIcon(type);
        if ((resourceId & 0xff000000) == 0x01000000) {
            icon.mSystemIconResourceId = resourceId;//设置ID
        } else {
            icon.loadResource(context, context.getResources(), resourceId);
        }
        systemIcons.append(type, icon);
        return icon;
    }

private static int getSystemIconTypeIndex(int type) {
        switch (type) {
            case TYPE_ARROW:
                return com.android.internal.R.styleable.Pointer_pointerIconArrow;
            case TYPE_SPOT_HOVER:
                return com.android.internal.R.styleable.Pointer_pointerIconSpotHover;
            case TYPE_SPOT_TOUCH:
                return com.android.internal.R.styleable.Pointer_pointerIconSpotTouch;
	//省略
}

可以看出,加载的资源ID为pointer_spot_touch_icon

//frameworks\base\core\res\res\drawable\pointer_spot_touch_icon.xml
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
    android:bitmap="@drawable/pointer_spot_touch"
    android:hotSpotX="16dp"
    android:hotSpotY="16dp" />

小圆点的图片资源已经被加载,并将其保存在相应的变量中了,接下来就需要将其显示出来了。当有触摸数据产生时,InputReader在读取到数据,然后会调用到cookAndDispatch来处理原始数据

//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::cookAndDispatch(nsecs_t when) {
	//省略
	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);
        }
	//省略
}

主要是通过调用setSpots去处理,在setSpots函数中,主要是先创建一个Spot对象,然后调用其updateSprite方法去请求更新小圆点

//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);
        }
    }
}

接下来的流程,主要是通过发送发送MSG_UPDATE_SPRITES消息,然后调用doUpdateSprites来更新绘制小圆点

//frameworks\base\libs\input\SpriteController.cpp
void SpriteController::doUpdateSprites() {
	//省略
	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();

		//省略
}

总结
触摸圆点的显示逻辑都是在InputReader线程中完成。以下为显示的流程图
在这里插入图片描述

相关推荐

  1. Android 11系统启动流程

    2024-05-26 03:32:20       19 阅读
  2. Android 11 AudioPolicyService 启动流程

    2024-05-26 03:32:20       7 阅读
  3. Android12 关机流程

    2024-05-26 03:32:20       40 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-26 03:32:20       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-26 03:32:20       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-26 03:32:20       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-26 03:32:20       18 阅读

热门阅读

  1. gateway基本配置

    2024-05-26 03:32:20       10 阅读
  2. 时政|杂粮产业

    2024-05-26 03:32:20       13 阅读
  3. MYSQL--多表查询

    2024-05-26 03:32:20       9 阅读
  4. Gopeed的高级用法

    2024-05-26 03:32:20       13 阅读
  5. GitLab的原理及应用详解(四)

    2024-05-26 03:32:20       11 阅读
  6. 揭秘软件测试工程师:事业前景和成功秘诀

    2024-05-26 03:32:20       10 阅读
  7. 前端面试题日常练-day33 【面试题】

    2024-05-26 03:32:20       13 阅读
  8. Android.mk简单介绍、规则与基本格式

    2024-05-26 03:32:20       7 阅读
  9. ANDROID OLLVM 混淆配置

    2024-05-26 03:32:20       10 阅读
  10. android NetworkMonitor记录

    2024-05-26 03:32:20       8 阅读