有一天在群里聊天的时候, 有人提出一个问题, 怎样才能做到 HAL 层访问 JAVA 层的接口? 刚好我不会, 所以做了一点研究.
之前的文章末尾部分说过了 service call 可以用来调试系统的 binder 服务. 传送门: Android native 进程间通信实例 - binder 篇之 -- 简单的单工通信
这次可以用到这个命令了!
1. 随机选取一个 java 层的服务.
adb shell 中输入命令 service list, 选取一个服务来做研究, 这次看中的是 textservices, 注意第一个服务 bysysui 后面的 "[ ]" 里面没有内容, 不能选取这样的服务来做这次的研究.
2. 搜寻这个服务相关的源码.
- frameworks/base/services/core/java/com/Android/server/TextServicesManagerService.java
- frameworks/base/core/java/com/Android/internal/textservice/ITextServicesManager.aidl
- out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/Android/internal/textservice/ITextServicesManager.java
3. 选择一个接口用于被 HAL 层的代码访问
可以知道 TextServicesManagerService.java 中 有一行 public class TextServicesManagerService extends ITextServicesManager.Stub,
所以我从 ITextServicesManager.aidl 来选择要访问的接口, 这次选的就是 boolean isSpellCheckerEnabled(); 这个函数应该就是返回一个 bool 变量而已, 越简单越好.
4. 搜寻 binder 中 transact 需要输入的 code
因为吧啦吧啦的原因 (可以自行去别的博文搜索原理, 本系列博文侧重实际操作), 所以在 out 目录下可以获取到每个服务中各个接口访问锁需要传入的 code.
在 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/Android/internal/textservice/ITextServicesManager.java 中可以发现熟悉的 onTransact 接口,
同时发现调用 sSpellCheckerEnabled 的 code 为 TRANSACTION_isSpellCheckerEnabled,
它的定义是: static final int TRANSACTION_isSpellCheckerEnabled = (Android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
因为 Android.os.IBinder.FIRST_CALL_TRANSACTION 的值是 1, 所以可知 code 为 8
5. 使用 service call 来撩一下 isSpellCheckerEnabled
可以看到 Parcel 有两个值, 第一个是 00000000, 第二个是 00000001.
再看看 ITextServicesManager.java 中 TRANSACTION_isSpellCheckerEnabled 这个 code 的处理, 果然 write 了两次, 而第二次 writeInt 的值就是我们需要获取的 bool 值了!
ITextServicesManager.java
- case TRANSACTION_isSpellCheckerEnabled:
- {
- data.enforceInterface(DESCRIPTOR);
- boolean _result = this.isSpellCheckerEnabled();
- reply.writeNoException();
- reply.writeInt(((_result)?(1):(0)));
- return true;
- }
按照之前分析的方法, 传送门: Android native 进程间通信实例 - binder 篇之 -- 用 parcel 传输数组
1. 首先 data.enforceInterface 传进去了一个组字符串 private static final java.lang.String DESCRIPTOR = "com.android.internal.textservice.ITextServicesManager";
感觉这组字符串是和校验有关了, 查看了 Parcel.cpp 源码, 发现 enforceInterface 果然是对比字符串用的, 在这个接口的上面有个接口名字叫做 writeInterfaceToken,
所以到时候要用 writeInterfaceToken 来写这组字符串用于比对校验.
2. writeNoException 和 writeInt 最终调用的是 Parcel.cpp 里面 writeInt32, 所以 reply 部分要 readInt32 两次.
6. HAL 层代码怎么写
就直接在之前写的 mybinderclient.cpp 上面贴源码吧 transct 前写校验字符串, 然后传入 code 值为 8, 最后 readInt32 两次, 第二次就是要读取的 JAVA 层服务 isSpellCheckerEnabled 的值啦!
- #include <binder/IServiceManager.h>
- #include <binder/IPCThreadState.h>
- #include <binder/Parcel.h>
- #include <binder/IInterface.h>
- #include<stdio.h>
- #define LOG_TAG "binderclient"
- using namespace Android;
- int main(int argc, char** argv)
- {
- static int TRANSACTION_isSpellCheckerEnabled = (/*Android.os.IBinder.FIRST_CALL_TRANSACTION*/1 + 7);
- sp<IBinder> ITextServicesBinder = defaultServiceManager()->getService(String16("textservices"));
- Parcel ITextServicesData, ITextServicesReply;
- ITextServicesData.writeInterfaceToken(String16("com.android.internal.textservice.ITextServicesManager"));
- ITextServicesBinder->transact(TRANSACTION_isSpellCheckerEnabled, ITextServicesData, &ITextServicesReply);
- int ret = ITextServicesReply.readInt32();
- int ret2 = ITextServicesReply.readInt32();
- printf("ret = %d, isSpellCheckerEnabled = %d\n", ret, ret2);
- return 0;
- }
执行结果:
这次在 HAL 层通过 binder 访问 JAVA 层服务的简单例子就讲解到这里, 希望大家看完以后能够触类旁通, 在这个例子上面得到启发.
希望大家多多吐槽, 大家一起共同进步!!
posted on 2019-06-28 10:15 啊源股 阅读 (...) 评论 (...) 编辑 收藏
来源: https://www.cnblogs.com/songsongman/p/11100760.html