Android Input系统之 InputMonitor 更新流程

分析源码为 android 12

一、InputMonitor层面分析

在 WMS 添加,更新和删除Window 的时候都会调用 InputMonitor 的 updateInputWindowsLw 更新 window 信息。如下是删除window 时候的调用函数:

WMS.java

void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit,int displayId) {synchronized (mGlobalLock) {final DisplayContent dc = mRoot.getDisplayContent(displayId);if (dc == null) {ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"+ " for non-exiting displayId=%d", binder, displayId);return;}final WindowToken token = dc.removeWindowToken(binder, animateExit);if (token == null) {ProtoLog.w(WM_ERROR,"removeWindowToken: Attempted to remove non-existing token: %s",binder);return;}if (removeWindows) {token.removeAllWindowsIfPossible();}// 调用 updateInputWindowsLwdc.getInputMonitor().updateInputWindowsLw(true /* force */);}}

updateInputWindowsLw 属于经常被外部调用的方法,它会最后触发到UpdateInputWindows 的 run 方法执行。

private class UpdateInputWindows implements Runnable {@Overridepublic void run() {synchronized (mService.mGlobalLock) {mUpdateInputWindowsPending = false;mUpdateInputWindowsNeeded = false;if (mDisplayRemoved) {return;}// Populate the input window list with information about all of the windows that// could potentially receive input.// As an optimization, we could try to prune the list of windows but this turns// out to be difficult because only the native code knows for sure which window// currently has touch focus.// If there's a drag in flight, provide a pseudo-window to catch drag inputfinal boolean inDrag = mService.mDragDropController.dragDropActiveLocked();// Add all windows on the default display.mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);}}}

最后会调用到mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);这个关键方法:

private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {InputConsumerImpl mPipInputConsumer;InputConsumerImpl mWallpaperInputConsumer;InputConsumerImpl mRecentsAnimationInputConsumer;private boolean mAddPipInputConsumerHandle;private boolean mAddWallpaperInputConsumerHandle;private boolean mAddRecentsAnimationInputConsumerHandle;boolean mInDrag;private void updateInputWindows(boolean inDrag) {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");......mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);updateInputFocusRequest(mRecentsAnimationInputConsumer);......}Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}......
}

这里有调用了 mDisplayContent.forAllWindows方法来遍历每一个windowstate 执行自己的 accept 方法:

public void accept(WindowState w) {......
// 这里会对每个WindowState转换成inputWindowHandle信息,
// 不过要求得有surface才可以,意味处于NO_SURFACE是不会进入if (w.mWinAnimator.hasSurface()) {populateInputWindowHandle(inputWindowHandle, w);setInputWindowInfoIfNeeded(mInputTransaction,w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);}}

把 WindowState 转换成 inputWindowHandle 后接下来会把这个inputWindowHandle 信息设置给 mSurfaceControl

static void setInputWindowInfoIfNeeded(SurfaceControl.Transaction t, SurfaceControl sc,InputWindowHandleWrapper inputWindowHandle) {if (DEBUG_INPUT) {Slog.d(TAG_WM, "Update InputWindowHandle: " + inputWindowHandle);}if (inputWindowHandle.isChanged()) {inputWindowHandle.applyChangesToSurface(t, sc);}}

接下来来到 SurfaceControl.java 的

public Transaction setInputWindowInfo(SurfaceControl sc, InputWindowHandle handle) {checkPreconditions(sc);nativeSetInputWindowInfo(mNativeObject, sc.mNativeObject, handle);return this;}

这里一看就是调用是 nativeSetInputWindowInfo 方法

static void nativeSetInputWindowInfo(JNIEnv* env, jclass clazz, jlong transactionObj,jlong nativeObject, jobject inputWindow) {auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);sp<NativeInputWindowHandle> handle = android_view_InputWindowHandle_getHandle(env, inputWindow);handle->updateInfo();auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);transaction->setInputWindowInfo(ctrl, *handle->getInfo());
}sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle(JNIEnv* env, jobject inputWindowHandleObj) {if (!inputWindowHandleObj) {return NULL;}AutoMutex _l(gHandleMutex);jlong ptr = env->GetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr);NativeInputWindowHandle* handle;if (ptr) {//这个java端的ptr变量,主要功能就是为了保存对应native层面指针地址handle = reinterpret_cast<NativeInputWindowHandle*>(ptr);} else {jweak objWeak = env->NewWeakGlobalRef(inputWindowHandleObj);handle = new NativeInputWindowHandle(objWeak);//java层面对象作为了参数,方便后面调用java对象相关属性handle->incStrong((void*)android_view_InputWindowHandle_getHandle);env->SetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr,reinterpret_cast<jlong>(handle));}return handle;
}

重点看看 handle->updateInfo(),这个只截取部分:


bool NativeInputWindowHandle::updateInfo() {JNIEnv* env = AndroidRuntime::getJNIEnv();jobject obj = env->NewLocalRef(mObjWeak);if (!obj) {releaseChannel();return false;}mInfo.touchableRegion.clear();jobject tokenObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.token);if (tokenObj) {mInfo.token = ibinderForJavaObject(env, tokenObj);env->DeleteLocalRef(tokenObj);} else {mInfo.token.clear();}mInfo.name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>");mInfo.dispatchingTimeout = std::chrono::milliseconds(env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutMillis));//这里可以看出主要就是对java的数据拷贝到这个mInfomInfo.frameLeft = env->GetIntField(obj,gInputWindowHandleClassInfo.frameLeft);env->DeleteLocalRef(obj);return true;
}

接下来看看 transaction->setInputWindowInfo(ctrl, *handle->getInfo()); 这里较为简单,知识把对应的WindowInfo数据传递给对应的layer_state_t,其实对应就是Layer

SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(const sp<SurfaceControl>& sc, const WindowInfo& info) {layer_state_t* s = getLayerState(sc);if (!s) {mStatus = BAD_INDEX;return *this;}s->windowInfoHandle = new WindowInfoHandle(info);s->what |= layer_state_t::eInputInfoChanged;return *this;
}

那么到此总结一下:
1、InputMonitior遍历每个WindowState相关数据,当然得有surface的,毕竟和surfaceflinger打交道
2、然后把数据java 的InputWindow数据转化native的InputWindow数据,通过一步步传递到transition中,即保存到了layer_state_t中
调用transition的apply方法:

status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay) {//省略遍历目前mComposerStates对象,这里面包好目前的所有inputwindowfor (auto const& kv : mComposerStates){composerStates.add(kv.second);}
//跨进程调用setTransactionState把composerStates作为参数sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,{} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,hasListenerCallbacks, listenerCallbacks, mId);//省略mStatus = NO_ERROR;return NO_ERROR;
}

二、到达SurfaceFlinger层面分析

跨进程调用到了SurfaceFlinger中了:

status_t SurfaceFlinger::setTransactionState(const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {TransactionState state{frameTimelineInfo,  states,displays,           flags,applyToken,         inputWindowCommands,desiredPresentTime, isAutoTimestamp,uncacheBuffer,      postTime,permissions,        hasListenerCallbacks,listenerCallbacks,  originPid,originUid,          transactionId};//把TransactionState放入队列,且会启动对应的scheduleCommitqueueTransaction(state);// Check the pending state to make sure the transaction is synchronous.if (state.transactionCommittedSignal) {waitForSynchronousTransaction(*state.transactionCommittedSignal);}return NO_ERROR;
}

然后执行SurfaceFlinger对应的commit会调用applyTransactionState再通过
setClientStateLocked把传递过来的layer_state_t进行获取更新到SurfaceFlinger的Layer
依次往下调用updateInputFlinger,会调用buildWindowInfos把当前所有的layer中获取数据转化到WindowInfo

uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo,ComposerState& composerState,int64_t desiredPresentTime, bool isAutoTimestamp,int64_t postTime, uint32_t permissions) {
//省略if (what & layer_state_t::eInputInfoChanged) {layer->setInputInfo(*s.windowInfoHandle->getInfo());flags |= eTraversalNeeded;}
//省略
}

这样就更新到了Layer中 接下来再调用updateInputFlinger

void SurfaceFlinger::updateInputFlinger() {//省略if (mVisibleRegionsDirty || mInputInfoChanged) {mInputInfoChanged = false;updateWindowInfo = true;//把WindowInfos搞出buildWindowInfos(windowInfos, displayInfos);}BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo,windowInfos = std::move(windowInfos),displayInfos = std::move(displayInfos),inputWindowCommands =std::move(mInputWindowCommands),inputFlinger = mInputFlinger, this]() {if (updateWindowInfo) {//这里会触发通知mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos,inputWindowCommands.syncInputWindows);} else if (inputWindowCommands.syncInputWindows) {// If the caller requested to sync input windows, but there are no// changes to input windows, notify immediately.windowInfosReported();}for (const auto& focusRequest : inputWindowCommands.focusRequests) {inputFlinger->setFocusedWindow(focusRequest);}}});mInputWindowCommands.clear();
}

这里看看buildWindowInfos

void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,std::vector<DisplayInfo>& outDisplayInfos) {//省略mDrawingState.traverseInReverseZOrder([&](Layer* layer) {if (!layer->needsInputInfo()) return;// Do not create WindowInfos for windows on displays that cannot receive input.if (const auto opt = displayInputInfos.get(layer->getLayerStack())) {const auto& info = opt->get();outWindowInfos.push_back(layer->fillInputInfo(info.transform, info.isSecure));//根据layer填满outWindowInfos信息}});sNumWindowInfos = outWindowInfos.size();outDisplayInfos.reserve(displayInputInfos.size());for (const auto& [_, info] : displayInputInfos) {outDisplayInfos.push_back(info.info);}
}

再接下来看看WindowInfo怎么传递通知出去给InputDispatch

void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos,const std::vector<DisplayInfo>& displayInfos,bool shouldSync) {ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners;{std::scoped_lock lock(mListenersMutex);for (const auto& [_, listener] : mWindowInfosListeners) {windowInfosListeners.push_back(listener);}}mCallbacksPending = windowInfosListeners.size();for (const auto& listener : windowInfosListeners) {//这里listener其实就是InputDispatherlistener->onWindowInfosChanged(windowInfos, displayInfos,shouldSync ? mWindowInfosReportedListener : nullptr);}
}

而后调用到InputDispatcher中

void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos,const std::vector<DisplayInfo>& displayInfos) {mDispatcher.onWindowInfosChanged(windowInfos, displayInfos);
}

完毕。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/665424.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32 UART/USART的低功耗模式和节能优化策略

在STM32微控制器中&#xff0c;UART/USART模块的低功耗模式和节能优化策略可以帮助减少系统功耗&#xff0c;并延长电池寿命。下面我将介绍一些常用的低功耗功能和相应的节能优化策略&#xff0c;并提供一个示例代码来演示如何实现它们。 ✅作者简介&#xff1a;热爱科研的嵌入…

华为鸿蒙系统第三方桌面手机管家版本号12.0.1.330

起因 放假在家&#xff0c;准备把自己的旧手机给家里老人看小视频&#xff0c;但是老人不懂智能手机的复杂操作&#xff0c;就想换成《极简桌面》这个软件&#xff0c;在“设置->默认应用->桌面”&#xff0c;但是华为为了“保护用户的隐私以及合法权益”禁用了第三方启…

202417读书笔记|《画•大师 阿尔丰斯•穆夏》——在明亮大胆的色调中熠熠生辉

202417读书笔记|《画•大师 阿尔丰斯•穆夏》——在明亮大胆的色调中熠熠生辉 这是读完的第二本或者第三本穆夏的画集。很赞&#xff0c;他的绘画风格&#xff0c;构图&#xff0c;元素&#xff0c;取大自然的月桂树&#xff0c;常青藤&#xff0c;&#x1f319;的不同形态&…

链表与二叉树-数据结构

链表与二叉树-数据结构 创建叶子node节点建立二叉树三元组&#xff1a;只考虑稀疏矩阵中非0的元素&#xff0c;并且存储到一个类&#xff08;三元组&#xff09;的数组中。 创建叶子node节点 class Node{int no;Node next;public Node(int no){this.nono;} } public class Lb…

Rsnapshot备份服务器

1、资源服务器设置免登录 资源服务器生成公钥和私钥 ssh-keygen 资源服务器将公钥复制到备份服务器 ssh-copy-id root192.168.1.129 备份服务器测试免密登录 ssh root192.168.1.128 2、顶顶顶顶 3、第三方士大夫 4、似懂非懂 5、是的范德萨发 6、第三方士大夫

Java实现对接微信支付功能接口详解(三)

本系列文章简介&#xff1a; 微信支付是一种在线支付解决方案&#xff0c;允许用户通过微信内的支付功能进行付款。它提供了多种支付方式&#xff0c;包括扫码支付、公众号支付、小程序支付等。在Java应用程序中实现微信支付功能&#xff0c;可以为用户提供方便快捷的支付体验。…

Fink CDC数据同步(三)Flink集成Hive

1 目的 持久化元数据 Flink利用Hive的MetaStore作为持久化的Catalog&#xff0c;我们可通过HiveCatalog将不同会话中的 Flink元数据存储到Hive Metastore 中。 利用 Flink 来读写 Hive 的表 Flink打通了与Hive的集成&#xff0c;如同使用SparkSQL或者Impala操作Hive中的数据…

Android-service

1. 预知识&#xff08;安卓版本&#xff09; public static final int BASE 1; //Android 1.0public static final int BASE_1_1 2; //Android 1.1public static final int CUPCAKE 3; //Android 1.5public static final…

Java 正则表达式总结

Java 正则表达式总结 大家好&#xff0c;我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编。今天&#xff0c;我们将深入探讨Java正则表达式&#xff0c;这是一种强大的文本匹配和处理工具&#xff0c;用于处理字符串的模式匹配。 1. 正则表达式简介 正则表达式是…

ABC339 A-G

Japan Registry Services (JPRS) Programming Contest 2024 (AtCoder Beginner Contest 339) - AtCoder 离AK ABC最近的一集&#xff0c;写完F还剩十分钟聊天去了&#xff0c;赛后一看题一眼主席树贴板子改一改十分钟过了...前几题感觉真的阅读理解... A - TLD 题意&#xf…

流畅的Python(七)-函数装饰器和闭包

一、核心要义 主要解释函数装饰器的工作原理&#xff0c;包括最简单的注册装饰器和较复杂的参数化装饰器。同时&#xff0c;因为装饰器的实现依赖于闭包&#xff0c;因此会首先介绍闭包存在的原因和工作原理。 二、代码示例 1、变量作用域规则 #!/usr/bin/env python # -*-…

前端学习笔记 | HTML5+CSS3静态网页制作的技巧(持续更新)

注&#xff1a;本文的css样式均使用less写法 1、字体居中 &#xff08;1&#xff09;先text-align:center;行内元素水平居中 &#xff08;2&#xff09;再line-heigh:(盒子高度);行内元素垂直居中 text-align: center;line-height: ( 30 / vw ); 2、盒子居中 情景1&#…

【课程作业_01】国科大2023模式识别与机器学习实践作业

国科大2023模式识别与机器学习实践作业 作业内容 从四类方法中选三类方法&#xff0c;从选定的每类方法中 &#xff0c;各选一种具体的方法&#xff0c;从给定的数据集中选一 个数据集&#xff08;MNIST&#xff0c;CIFAR-10&#xff0c;电信用户流失数据集 &#xff09;对这…

【大数据技术攻关专题】「Apache-Flink零基础入门」手把手+零基础带你玩转大数据流式处理引擎Flink(基础加强+运行原理)

手把手零基础带你玩转大数据流式处理引擎Flink&#xff08;运行机制原理加深&#xff09; 前提介绍运行Flink应用运行机制Flink的两大核心组件JobManagerTaskManagerTaskSlot Flink分层架构Stateful Stream ProcessingDataStream和DataSetDataStream&#xff08;数据流&#xf…

GPIO中断

1.EXTI简介 EXTI是External Interrupt的缩写&#xff0c;指外部中断。在嵌入式系统中&#xff0c;外部中断是一种用于处理外部事件的机制。当外部事件发生时&#xff08;比如按下按钮、传感器信号变化等&#xff09;&#xff0c;外部中断可以立即打断正在执行的程序&#xff0…

Qt 数据库操作V1.0

1、pro文件 QT sql2、h文件 #ifndef DATABASEOPERATION_H #define DATABASEOPERATION_H#include <QSqlDatabase> #include <QSqlQuery> #include <QSqlError> #include <QSqlRecord> #include <QDebug> #include <QVariant>clas…

大红喜庆版UI猜灯谜小程序源码/猜字谜微信小程序源码

今天给大家带来一款UI比较喜庆的猜灯谜小程序&#xff0c;大家看演示图的时候当然也是可以看得到那界面是多么的喜庆&#xff0c;而且新的一年也很快就来了,所以种种的界面可能都比较往喜庆方面去变吧。 这款小程序搭建是免服务器和域名的&#xff0c;只需要使用微信开发者工具…

Linux一键部署telegraf 实现Grafana Linux 图形展示

influxd2前言 influxd2 是 InfluxDB 2.x 版本的后台进程,是一个开源的时序数据库平台,用于存储、查询和可视化时间序列数据。它提供了一个强大的查询语言和 API,可以快速而轻松地处理大量的高性能时序数据。 telegraf 是一个开源的代理程序,它可以收集、处理和传输各种不…

Linux开发工具

前言&#xff1a;哈喽小伙伴们&#xff0c;经过前边的学习我们已经掌握了Linux的基本指令和权限&#xff0c;相信大家学完这些之后都会对Linux有一个更加深入的认识&#xff0c;但是Linux的学习可以说是从现在才刚刚开始。 这篇文章&#xff0c;我们将讲解若干个Linux的开发工…

Java基础数据结构之Map和Set

Map和Set接口 1.Set集合&#xff1a;独特性与无序性 Set是Java集合框架中的一种&#xff0c;它代表着一组无序且独特的元素。这意味着Set中的元素不会重复&#xff0c;且没有特定的顺序。Set接口有多个实现类&#xff0c;如HashSet、LinkedHashSet和TreeSet。 2.Map集合&…