开发者

从"Show tabs"了解Android Input系统

开发者 https://www.devze.com 2023-01-09 10:20 出处:网络 作者: Aloys_Code
目录Input源码解读——从"Show tabs"开始Settings 写入设置InputManagerService监听设置通过 InputReader 请求刷新配置EventHub 唤醒 InputReader 线程InputReader线程刷新配置InputDevice配置变
目录
  • Input源码解读——从"Show tabs"开始
  • Settings 写入设置
  • InputManagerService监听设置
  • 通过 InputReader 请求刷新配置
  • EventHub 唤醒 InputReader 线程
  • InputReader线程刷新配置
    • InputDevice配置变化
    • TouchInputMapper 进一步处理
  • 创建和初始化 PointerController
    • 初始化 PointerController
    • 加载 Pointer 相关资源
  • 显示Tap
    • 总体流程
      • 涉及的Input核心逻辑框图

        Input源码解读——从"Show tabs"开始

        本文基于android T版本源码,梳理当用户在开发者选项中开启Show tabs功能后显示第点按操作的视觉反馈的原理,来进一步了解Android Input系统

        从"Show tabs"了解Android Input系统

        Settings 写入设置

        首先是设置应用(Settings)提供的开发者选项画面响应点击,将Show taps选项对应的设置Key SHOW_TOUCHES的 ON 值通过android.provder.Settings接口写入到保存系统设置数据的SettingsProvier中。

        // packages/apps/Settings/src/com/android/settings/development/ShowTapsPreferenceController.Java
        public class ShowTapsPreferenceController extends DeveloperOptionsPreferenceController ... {
            ...
            @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;
            }
            ...
        }
        

        InputManagerService监听设置

        负责管理输入的系统服务InputManagerService在启动之际,会监听设置中的 SHOW_TOUCHES字段的变化,在设置产生变化的时候调用native侧的代码进行处理。

        // frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
        public class InputManagerService extends IInputManager.Stub... {
            ...
            public void start() {
                ...
                registerShowTouchesSettingObserver();
                ...
            }
            
            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);
            }
            
            private void updateShowTouchesFromSettings() {
                int setting = getShowTouchesSetting(0);
                mNative.suCPMeetShowTouches(setting != 0);
            }
            ...
        
        // frameworks/base/services/core/java/com/android/server/input/NativeInputManagerService.java
        public interface NativeInputManagerService {
            ...
            void setShowTouches(boolean enabled);
            ...
        }
        
        // frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
        class NativeInputManager : public virtual RefBase, ...{
            ...
            void setShowTouches(bool 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);
        }
        

        这里的mInputManagerInputManagerInterface对象实例,InputManagerInputManagerInterface和子类,所以通过mInputManager可以连接NativeInputManagerInputReader

        这里向负责读取事件的InputReader发出更新配置的请求,配置变更的Type为 CHANGE_SHOW_TOUCHES

        通过 InputReader 请求刷新配置

        InputReader接收到配置变化的Type之后,会根据记录待刷新配置的变量 mConfigurationChangesToRefresh判断当前是否已经在刷新过程中。

        如果尚未处于刷新中,则更新mConfigurationChangesToRefresh的值,并唤醒EventHub进行配置刷新。

        // frameworks/native/services/inputflinger/reader/InputReader.cpp
        void InputReader::requestRefreshConfiguration(uint32_t changes) {
            std::scoped_lock _l(mLock);
        
            if (changes) {
                bool needwake = !mConfigurationChangesToRefresh;
                mConfigurationChangesToRefresh |= changes;
        
                if (needWake) {
                    mEventHub->wake();
                }
            }
        }
        

        EventHub 唤醒 InputReader 线程

        InputManagerService过来的刷新请求最终需要InputReader线程来处理。

        可是 InputReader 线程处在从EventHub中读取事件和没有事件时便调用epoll_wait进入等待状态的循环当中。

        所以为了让其立即处理配置变化,需要EventHub的手动唤醒。

        // frameworks/native/services/inputflinger/reader/EventHub.cpp
        void EventHub::wake() {
            ALOGV("wake() called");
        
            ssize_t nWrite;
            do {
                nWrite = write(mWakeWritePipeFd, "W", 1);
            } while (nWrite == -1 && errno == EINTR);
        
            if (nWrite != 1 && errno != EAGAIN) {
                ALOGW("Could not write wake signal: %s", strerror(errno));
            }
        }
        
        size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
            ...
            for (;;) {
                ...
                int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
                ...
            }
            ...
        }
        

        InputReader线程刷新配置

        EventHub唤醒后处于等待状态的getEvents会结束,之后InputReader线程会进入下次循环即loopOnce

        其首先将检查是否存在待刷新的配置变化changes,存在的话调用refreshConfigurationLockedInputDevice去重新适配变化。

        // frameworks/native/services/inputflinger/reader/InputReader.cpp
        void InputReader::loopOnce() {
            ...
            std::vector<InputDeviceInfo> inputDevices;
            { // acquire lock
                ...
                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);
            ...
        }
        

        需要留意,refreshConfigurationLocked在调用InputDevice进一步处理之前需要先获取配置的变化放入mConfig中。

        // frameworks/native/services/inputflinger/reader/InputReader.cpp
        void InputReader::refreshConfigurationLocked(uint32_t changes) {
            mPolicy->getReaderConfiguration(&mConfig);
            ...
        
            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);
                }
            }
            ...
        }
        

        InputDevice配置变化

        InputDeviceconfigure需要处理很多配置变化,比如键盘布局、麦克风等。对于Show taps的变化关注调用 InputMappercongfigure即可。

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

        TouchInputMapper 进一步处理

        众多输入事件的物理数据需要对应的InputMapper来转化为上层能识别的事件类型。比如识别键盘输入的 KeyboardInputMapper、识别震动的VibratorInputMapper等等。

        现在的触摸屏都支持多点触控,所以是MultiTouchInputMapper来处理的。可MultiTouchInputMapper没有复写 configure(),而是沿用由父类TouchInputMapper的共通处理。

        // frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
        void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
                                         uint32_t changes) {
            ...
            bool resetNeeded = false;
            if (!changes ||
                (changes &
                 (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
                  InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
                  InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
                  InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
                  InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
                // Configure device sources, display dimensions, orientation and
                // scaling factors.
                configureInputDevice(when, &resetNeeded);
            }
            ...
        }
        

        TouchInputMapper会依据changes的类型进行对应处理,对于SHOW_TOUCHES的变化需要调用configureInputDevice进一步处理。

        创建和初始化 PointerController

        configureInputDevice进行多个参数的测量和配置,其中和Show taps相关的是PointerController的创建,该类是 Mouse、Taps、Pointer location 等系统 Touch 显示的专用类。

        // frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
        void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
            ...
            // Create pointer controller if needed, and keep it around if Pointer Capture is enabled to
            // preserve the cursor position.
            if (mDeviceMode == DeviceMode::POINTER ||
                (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
                (mParameters.deviceType == Parameters::DeviceType::POINTER &&
                 mConfig.pointerCaptureRequest.enable)) {
                if (mPointerController == nullptr) {
                    mPointerController = getContext()->getPointerController(getDeviceId());
                }
                if (mConfig.pointerCaptureRequest.enable) {
                    mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
                }
            } else {
                mPointerController.reset();
            }
            ...
        }
        

        这里调用InputReaderContext#getPointerControllerInputReader::ContextImplInputReaderContext的子类,所以会回调到InputReader开启PointerController的创建和初始化。

        // frameworks/native/services/inputflinger/reader/InputReader.cpp
        std::shared_ptr<PointerControllerInterface> InputReader::ContextImpl::getPointerController(
                int32_t deviceId) {
            // lock is already held by the input loop
            return mReader->getPointerControllerLocked(deviceId);
        }
        
        std::shared_ptr<PointerControllerInterface> InputReader::getPointerControllerLocked(
                int32_t deviceId) {
            std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
            if (controller == nullptr) {
                controller = mPolicy->obtainPointerController(deviceId);
                mPointerController = controller;
                updatePointerDisplayLocked();
            }
            return controller;
        }
        

        这里调用InputReaderPolicyInterface#obtainPointerController,而NativeInputManagerInputReaderPolicyInterface的子类。

        // frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
        std::shared_ptr<PointerControllerInterface> NativeInputManager::obtainPointerController(
                int32_t /* deviceId */) {
            ...
            std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
            if (controller == nullptr) {
                ensureSpriteControllerLocked();
        
                controller = PointerController::create(this, mLooper, mLocked.spriteController);
                mLocked.pointerController = controller;
                updateInactivityTimeoutLocked();
            }
        
            return controlljavascripter;
        }
        

        PointerController 构建的同时需要构建持有的 MouseCursorController。

        // frameworks/base/libs/input/PointerController.cpp
        std::shared_ptr<PointerController> PointerController::create( ... ) {
            std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
                    new PointerController(policy, looper, spriteController));
            ...
            return controller;
        }
        
        PointerController::PointerController( ... )
              : mContext(policy, looper, spriteController, *this), mCursorController(mContext) {
            std::scoped_lock lock(mLock);
            mLocked.presenta编程客栈tion = Presentation::SPOT;
            ...
        }
        

        obtainPointerController执行完之后调用updatePointerDisplayLocked执行PointerController的初始化。

        初始化 PointerController

        调用PointerControllersetDisplayViewport传入显示用的DisplayViewPort

        // frameworks/native/services/inputflinger/reader/InputReader.cpp
        void InputReader::updatePointerDisplayLocked() {
            ...
            std::optional<DisplayViewport> viewport =
                    mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
            if (!viewport) {
                ...
                viewport = mConfig.getDisplayViewportById(ADISPLAY_ID_DEFAULT);
            }
            ...
            controller->setDisplayViewport(*viewport);
        }
        

        setDisplayViewport需要持有的MouseCursorController进一步初始化。

        // frameworks/base/libs/input/PointerController.cpp
        void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
            ...
            mCursorController.setDisplayViewport(viewport, getAdditionalMouseResources);
        }
        

        MouseCursorController需要获取Display相关的参数,并执行两个重要步骤:loadResourcesLocked/updatePointerLocked

        // frameworks/base/libs/input/MouseCursorController.cpp
        void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport,
                                                       bool getAdditionalMouseResources) {
            ...
            // Reset cursor position to center if size or display changed.
            if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth ||
                oldDisplayHeight != newDisplayHeight) {
                float minX, minY, maxX, maxY;
                if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
                    mLocked.pointerX = (minX + maxX) * 0.5f;
                    mLocked.pointerY = (minY + maxY) * 0.5f;
                    // Reload icon resources for density may be changed.
                    loadResourcesLocked(getAdditionalMouseResources);
                ...
                }
            } else if (oldViewport.orientation != viewport.orientation) {
                ...
            }
        
            updatePointerLocked();
        }
        

        加载 Pointer 相关资源

        // frameworks/base/libs/input/MouseCursorController.cpp
        void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) {
            ...
            policy->loadPointerResources(&mResources, mLocked.viewport.displayId);
            policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
            ...
        }
        

        省略诸多细节,loadPointerResources将通过InputManagerServiceJNI端以及PointerIconJNI端创建PointerIcon实例,并读取显示的资源。

        getSystemIcon则是负责的函数,其将读取系统资源里名为PointerStyle,并读取指针对应的资源 ID。

        // 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);
                ...
                icon = new PointerIcon(type);
                if ((resourceId & 0xff000000) == 0x01000000) {
                    icon.mSystemIconResourceId = resourceId;
                } else {
                    icon.loadResource(context, context.getResources(), resourceId);
                }
                systemIcons.append(type, icon);
                return icon;
            }
        
            private static int getSystemIconTypeIndex(int type) {
                switch (type) {
                    ...
                    case TYPE_SPOT_TOUCH:
                        return com.android.internal.R.styleable.Pointer_pointerIconSpotTouch;
                    ...
                    default:
                        return 0;
                }
            }
        

        资源 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" />
        

        其指向的图片就是如下熟悉的 Spot png:pointer_spot_touch.png。之后的loadPointerIcon阶段会将该图片解析成 Bitmap 并被管理在SpriteIcon中。

        SpriteIconupdatePointerLocked阶段会被存放到SpriteController中,等待显示的调度。

        // frameworks/base/libs/input/MouseCursorController.cpp
        void MouseCursorController::updatePointerLocked() REQUIRES(mLock) {
            if (!mLocked.viewport.isValid()) {
                return;
            }
            sp<SpriteController> spriteController = mContext.getSpriteController();
            spriteController->openTransaction();
        
            ...
            if (mLocked.updatePointerIcon) {
                if (mLocked.requestedPointerType == mContext.getPolicy()->getDefaultPointerIconId()) {
                    mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
                ...
                }
                mLocked.updatePointerIcon = false;
            }
        
            spriteController->closeTransaction();
        }
        

        显示Tap

        点击的时候EventHub#getEvents会产生事件,InputReader#loopOnce会调用processEventsLocked处理事件。

        // frameworks/native/services/inputflinger/reader/InputReader.cpp
        void InputReader::loopOnce() {
            ...
            size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
        
            { // acquire lock
                ...
                if (count) {
                    processEventsLocked(mEventBuffer, count);
                }
                ....
            } // release lock
            ...
        }
        

        之后调用InputMapper开始加工事件,并在TouchInputMapper#cookAndDispatch的时候调用updateTouchSpots更新 PointerController的一些参数。

        // frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
        void TouchInputMapper::updateTouchSpots() {
            ...
            mPointerController->setPresentation(PointerControllerInterface::Presentation::SPOT);
            mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
        
            mPointerController->setButtonState(mCurrentRawState.buttonState);
            setTouchSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
                          mCurrentCookedState.cookedPointerData.idToIndex,
                          mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId);
        }
        

        其中比较关键的setTouchSpots是显示Taps的关键步骤,准备 x、y 坐标和压力值。

        在 Reader 而不是 Dispatch、更不是 ViewRootImpl 的时候处理的原因在于:Read 到事件即显示可以更早地响,同时不用占用 App 进程。

        // frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
        void TouchInputMapper::setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
                                             BitSet32 spotIdBits, int32_t displayId) {
            std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
        
            for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
                const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()];
                float x = spotCoords[index].getX();
                float y = spotCoords[index].getY();
                float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
                ...
            }
        
            mPointerController->setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits, displayId);
        }
        

        其后PointerController会通过TouchSpotController创建Spot实例向其发送updateSprite请求。最后回调 SpriteController调用setIcon处理。

        // frameworks/base/libs/input/TouchSpotController.cpp
        void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
                                           php          int32_t displayId) {
            sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
            ...
            if (icon != mLastIcon) {
                mLastIcon = icon;
                if (icon) {
                    sprite->setIcon(*icon);
                    sprite->setVisible(true);
                } else {
                    sprite->setVisible(false);
                }
            }
        }
        
        // frameworks/base/libs/input/SpriteController.cpp
        void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
            AutoMutex _l(mController->mLock);
            ...
            invalidateLocked(dirty);
        }
        
        void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
            ...
            if (!wasDirty) {
                mController->invalidateSpriteLocked(this);
            }
        }
        
        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));
                }
            }
        }
        

        MSG_UPDATE_SPRITES经过 Handler 回调doUpdateSprites,将取出封装在SpriteUpdate中的SpriteIcon并执行 draw。

        // frameworks/base/libs/input/SpriteController.cpp
        void SpriteController::doUpdateSprites() {
            ...
            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
                开发者_Python入门        && update.state.wantSurfaceVisible()) {
                    sp<Surface> surface = update.state.surfaceControl->getSurface();
                    if (update.state.icon.draw(surface)) {
                        update.state.surfaceDrawn = true;
                        update.surfaceChanged = surfaceChanged = true;
                    }
                }
            }
            ...
            updates.clear();
        }
        

        最后,SpriteIcon将取出Bitmap描画到SurfaceCanvas上去。

        // frameworks/base/libs/input/SpriteIcon.cpp
        bool SpriteIcon::draw(sp<Surface> surface) const {
            ...
            graphics::Paint paint;
            paint.setBlendMode(ABLEND_MODE_SRC);
        
            graphics::Canvas canvas(outBuffer, (int32_t)surface->getBuffersDatASPace());
            canvas.drawBitmap(bitmap, 0, 0, &paint);
            ...
            status 编程= surface->unlockAndPost();
            if (status) {
                ALOGE("Error %d unlocking and posting sprite surface after drawing.", status);
            }
            return !status;
        }

        总体流程

        通过一个框图简单回顾一下整个流程。

        从"Show tabs"了解Android Input系统

        可以看到,简简单单的 Show taps 功能,从设置、配置、刷新再到显示,经历了多个进程、多个模块的协力。

        涉及的Input核心逻辑框图

        从"Show tabs"了解Android Input系统

        到此这篇关于从"Show tabs"了解Android Input系统的文章就介绍到这了,更多相关Android Input系统 Show tabs内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

        0

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        关注公众号