专栏首页编程入门之C语言Android native进程间通信实例-binder篇之——解决实际问题inputreader内建类清楚缓存

Android native进程间通信实例-binder篇之——解决实际问题inputreader内建类清楚缓存

我在实际开发中,遇到一个问题,在电容屏驱动中没有发送input_sync 给上层,导致电容屏有的数据缓存在inputreader 中,会导致系统一系列奇怪问题发生,

至于为什么驱动不发送input_sync ,是因为项目某个功能的框架没有搭好导致的,总之这次不能从驱动中解决这个问题,这次为了弥补这个过失,

就需要在特定的情况下强制把电容屏在inputreader 的缓存清除,好了,这次binder 又要闪亮登场了!

1. 熟悉Inputreader 源码获取清除缓存接口

说实话,没有具体跟踪调试过这部分源码,直接从0开始生硬的看代码确实费力,我尽量简洁地说这块源码,以及我是如何找到清除缓存的接口的。

a. 首先把 frameworks\native\services\inputflinger 这部分代码添加到sourceinsight 中。

b. 只需要大致明白,EventHub.cpp 是直接获取驱动报上来的原始数据,然后InputReader.cpp 对这份数据进行处理保存在一段缓存队列中,InputDispatcher.cpp

从队列中取数据,再发送给上层窗口等等。

所以,只需要阅读InputReader.cpp 代码即可,因为要清空的缓存就在其中。

怎么办,这个cpp 有7千多行代码,不同android 版本说不定有8千多行呢?

别慌!粗略的看一下,发现有个类叫做 MultiTouchInputMapper ,电容屏不就是多点触控么,直接添加相关调试log, 可以清楚这块调用流程。总之,MultiTouchInputMapper

里面有个重要的实现叫做 void MultiTouchInputMapper::reset(nsecs_t when) ,就是它会清空缓存。

c. 熟悉代码后发现 MultiTouchInputMapper 与 InputMapper 有密切的关系,如果实在觉得看代码嫌烦,直接搜出所有的reset ,可以发现void InputDevice::reset(nsecs_t when) 最终会掉进

MultiTouchInputMapper 里面,感觉这就是唯一的通路了,虽然会误伤到SingleTouchInputMapper ,但是对项目没有影响就无所谓了,毕竟SingleTouchInputMapper 中也没有什么数据好清空,如果对我的设计思想有异议请大胆说出来吧!

误伤SingleTouchInputMapper 的InputDevice::reset 代码如下,如果不想误伤可以把下面实现进行修改,或者只调用MultiTouchInputMapper 的reset 接口也行,我这么做主要是害怕只清空一部分不能解决问题,所以后续调试决定统一清空。

void InputDevice::reset(nsecs_t when) {
    size_t numMappers = mMappers.size();
    for (size_t i = 0; i < numMappers; i++) {
        InputMapper* mapper = mMappers[i];
        mapper->reset(when);
    }

    mContext->updateGlobalMetaState();

    notifyReset(when);
}

d. 下一步就是搜寻InputDevice 这个类了,可以很快找到调用的方式。

首先声明获取device: InputDevice* device = mDevices.valueAt(deviceIndex);

然后清空数据:device->reset(when);

e. 添加处理代码

void InputReader::clearCTPData(nsecs_t when, int32_t deviceId) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex < 0) {
        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
        return;
    }

    InputDevice* device = mDevices.valueAt(deviceIndex);
    if (device->isIgnored()) {
        //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }
    
    device->reset(when);
}

发现里面有个参数比较陌生,deviceId, 调试过input 设备的朋友应该清楚,在adb shell 下输入getevent 就会冒出好多挂载的input 设备信息,其中就包括了deviceId,当然要用代码获取也是可以的,这部分下一节讨论。

2. 添加binder 服务

由上面添加的clearCTPData 这个接口可知,这个处理是在InputReader 类里面新加的一个方法。调用它就需要有一个指针指向当前的InputReader ,好的,有了这个想法就开始写代码吧。

首先在InputReader.cpp 中InputReader::InputReader 的构造函数中添加咱们的binder 指针,binder 调用ontransct的类服务也需要重写一下,就命名为MyInputreaderService 吧。

分三步走,

第一部:

把clearCTPData的代码添加到InputReader.cpp 和 InputReader.h 中,代码刚才有贴过,声明直接放在class InputReader 里面即可。

第二部 :

InputReader.cpp 中在构造函数里面添加binder 的服务,代码如下:

InputReader::InputReader(const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) :
        mContext(this), mEventHub(eventHub), mPolicy(policy),
        mGlobalMetaState(0), mGeneration(1),
        mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    mQueuedListener = new QueuedInputListener(listener);

    { // acquire lock
        AutoMutex _l(mLock);

        sp<IBinder> sendBinder = new ByInputreaderService(this);

        defaultServiceManager()->addService(String16("my.inputreader"), sendBinder);


        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
}

第三部:

完成ByInputreaderService 的功能,我主要借鉴getevent的源码做了一个简单的获取deviceID的功能,同时用InputReader 构造函数中传入的this 来搞事情(调用clearCTPData)

class ByInputreaderService : public BBinder {
public:
	InputReader *parent;
	int mCTPDeviceId;
	nsecs_t mWhen;
	
	ByInputreaderService(InputReader *p) : parent(p)
	{
		mCTPDeviceId = 1;
		getCtpFd();
	}

	~ByInputreaderService()
	{

	}

	int scan_input_device(int fd)
	{
		char name[80];
		name[sizeof(name) - 1] = '\0';

		if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
			ALOGI("could not get device name for %s\n", strerror(errno));
			name[0] = '\0';
		}

		ALOGI("getCtpFd device name is %s\n", name);
		if(strcmp("cyttsp5_mt", name) == 0)
		{
			ALOGI("getCtpFd cyttsp5_mt !!!\n");
			return 0;
		}

		return -1;
	}

	int getCtpFd(void)
	{
		int fd = 0;
		int device_type = 0;
		char devname[50];
		char *filename;
		DIR *dir;
		struct dirent *de;
		dir = opendir("/dev/input");
		if(dir == NULL)
		return -1;

		strcpy(devname, "/dev/input");
		filename = devname + strlen(devname);
		*filename++ = '/';
		while((de = readdir(dir))) 
		{
			if(de->d_name[0] == '.' && (de->d_name[1] == '\0' ||(de->d_name[1] == '.' && de->d_name[2] == '\0')))
				continue;

			strcpy(filename, de->d_name);

			fd = open(devname, O_RDWR);
			if (fd < 0) 
			{
				ALOGI("[getCtpFd] open device failed! path: %s\n",devname);
				continue;
			}
			device_type = scan_input_device(fd);
			if(device_type != 0)
			{
				mCTPDeviceId ++;
				close(fd);
				ALOGI("[getCtpFd] scan device failed! path: %s -- %d\n",devname, device_type);
				continue;
			}
			else
			{
				ALOGI("[getCtpFd] scan device success! path: %s -- %d\n",devname, device_type);
				break;
			}
		}
		closedir(dir);

		close(fd);
		return fd;    
	}


	status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
	{
		ALOGI("ByInputreaderService %d \n", code);

		if(CLEARINPUTDATA == code)
		{
			mWhen = systemTime(SYSTEM_TIME_MONOTONIC);
			parent->clearCTPData(mWhen, mCTPDeviceId);	        
		}
		return NO_ERROR;
	}


};

CLEARINPUTDATA 这个宏随便定义,总之客户端要和这个code 值保持一致即可。

如果觉得代码或者实现的方式有什么不妥的地方请多多指教,谢谢。

希望大家多多吐槽,大家一起共同进步!!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C语言编程入门之--第四章C语言基本数据类型

      导读:C语言程序中经常涉及一些数学计算,所以要熟悉其基本的数据类型。数据类型学习起来比较枯燥,不过结合之前的内存概念,以及本节的字节概念,相信数据类型也就不...

    啊源股
  • 解决多类开机弹框问题

    生活使用电脑中,经常由于安装某些程序的失误导致电脑开机的时候会弹出一些对话框,对话框的内容大多都是“某某dll安装不正常”、“xx 内存writen错误”等等,...

    啊源股
  • C语言编程入门之--第五章C语言基本运算和表达式-part1

      导读:程序要完成高级功能,首先要能够做到基本的加减乘除。本章从程序中变量的概念开始,结合之前学的输出函数和新介绍的输入函数制作简单人机交互程序,然后讲解最基...

    啊源股
  • Oozie分布式任务的工作流——Spark篇

    Spark是现在应用最广泛的分布式计算框架,oozie支持在它的调度中执行spark。在我的日常工作中,一部分工作就是基于oozie维护好每天的spark离线任...

    用户1154259
  • 使用Tensorflow Lite在Android上构建自定义机器学习模型

    机器学习有许多用处,并提供了一个充满未知性的世界。然而,有些人可能会退缩,认为它太难了,其实并不是这样的。使用TensorFlow Lite并不一定都是机器学习...

    AiTechYun
  • tensorflow学习笔记(一):命令行参数

    版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/u01...

    ke1th
  • 基础知识 | 每日一面(13)

    读者:我遇到这样声明结构的代码: struct name { int namelen; charnamestr[1];}; 然后又使用一些内存分配技巧使 nam...

    闫小林
  • 线性回归的求解:矩阵方程和梯度下降、数学推导及NumPy实现

    我的网站公式显示效果更好:https://lulaoshi.info/machine-learning/linear-model/minimise-loss-f...

    PP鲁
  • Python中如何统计文本词汇出现的次数?

    有时在遇到一个文本需要统计文本内词汇的次数的时候,可以用一个简单的python程序来实现。

    小小科
  • Python|统计文本词汇出现次数

    有时在遇到一个文本需要统计文本内词汇的次数的时候,可以用一个简单的python程序来实现。

    算法与编程之美

扫码关注云+社区

领取腾讯云代金券