Android NDK开发之震动服务客户端编写程序(C++)

一、背景

最近有个小伙伴问我可不可以写一个可执行程序(C/C++) 来实现Android设备的震动的功能。 作为一个多年的Android开发者,我觉得这是可以实现的。 但是由于过去我一直做App开发,也就把这个实现过程想简单了。 经过了几天的折腾,终于算是实现了这个程序。 本文就是记录一下 整个“折腾”的经历。

环境描述: Android12 (由于AOSP针对于 "震动服务"的架构 在Android12上有调整)
开发工具: Android Studio
开发形式: NDK

二、实现思路

  • 思路一: 获取到VibratorManagerService对相应的BpBinder对象,然后进行IPC通信。
  • 思路二: 模拟VibratorManagerService,直接“访问硬件”,实震动效果。
  • 思路三: 写在最后(苦笑~~)。

当我被问到这个问题的时候,我首先想到了前面两种实现方式,但是又考虑到 “思路二” 直接“访问硬件设备” 不太符合Android的架构理念, 故想尝试一下“方式一”的实现。

2.1 可行性分析

熟悉binder架构的同学,可能都知道:android系统中的binder ipc 就是通过native层调用驱动层来实现的。即便在应用层开发的时候,我们常采用的AIDL,其本质上也是通过native的bpbinder和bbinder实现。 具体的原理,可以看一下面的这张图:

在这里插入图片描述

同上图中看出 Bpbinder 与 Bbinder 是成对儿且对等的;同理在上层Binder.Stub 与 BinderProxy是成对儿且对等的。

我们是可以实现通过native层的bpbinder调用到java层的BinderServer对象(例如:IPackageManger.Stub)。

2.2 VibratorManagerService 结构图

在这里插入图片描述

从上图中VibratorManagerService(后面简称‘VMS’)的结构中,我们可以看出:

要想实现编写一个C++可执行程序实现控制振动器的功能,需要两个:a. 获取VibratorManagerService的代理对象 b. 遵顼IVbratorManagerService AIDL协议

三、功能实现 VibratorManagerService 结构图

  • 获取VMS代理对象
    • 获取SM代理对象
    • 获取VMS代理对象
  • AIDL协议生成
  • 编译构建
  • 测试IPC调用

3.1 获取VMS代理对象

熟悉AOSP源码的小伙伴,可能都会知道:获取一个BinderProxy代理对象,是通过ServiceManager的代理对象来完成的。 因此咱们要做的第一步就是拿到SM代理对象,然后再通过SM代理对象去“getService”拿到VMS代理对象。

3.1.1 导入libbinder.so并获取SM代理对象

AOSP中,Native层获取SM代理对象的方式是:

sp<IServiceManager> sm = defaultServiceManager();

defaultServiceManager() 函数是定义在IServiceManager.h中,但是在NDK中,binder包下并没有IServiceManager.h
因此我们只能将 libbinder.so 以第三方库的形式导入到项目中,并将相关的 头文件拷贝进来。

3.1.1.1 导入 libbinder.so

AOSP中,构建libinder.so 时, 依赖了 liblog.so , libcutils.so , libutils.so ,因此需要依次导入:

// frameworks/native/libs/binder/Android.bp
cc_library_shared {name: "libbinder",......shared_libs: ["libbase","liblog","libcutils","libutils","libbinderthreadstate",]...
}

在这里插入图片描述

接下来就是每个头文件的导入:

这里笔者是根据so对应的android.bp文件,找到对应“导出头文件”

在这里插入图片描述

头文件目录如下

库名称头文件路径备注
libbinder.soframeworks/native/include/binder/*
frameworks/native/include/binder/*
libutils.sosystem/core/libutils/include/utils/*
liblog.sosystem/logging/liblog/include/log/*
其它system/core/libsystem/include/system/*

详情可以见下面的demo

3.1.1.2 获取SM代理对象
sp<IServiceManager> sm = defaultServiceManager();

3.1.2 获取VMS代理对象

sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("vibrator_manager"));
if (binder != nullptr) {LOGD("Sharknade Binder info: %p", binder.get());
}

编译成功之后, 测试结果如下:
在这里插入图片描述

获得VMS代理成功了 ,接下是就是如何使用该代理对象进行RPC通信了。

3.2 AIDL协议生成

Android SDK中 提供了AIDL工具,可以实现AIDL文件转C++、Java、NDK等形式转换。 我们可以通过该工具 快速获得VMS 相关的头文件。
VMS相关的AIDL文件有5个:

  • IVibratorManagerService.aidl
  • CombinedVibration.aidl
  • IVibratorStateListener.aidl
  • VibrationAttributes.aidl
  • VibratorInfo.aidl

执行下面的shell,获得对应的头文件:

<SDK_PATH>/build-tools/31.0.0/aidl -h ./ -o ./ --lang=cpp -I ./aidl  aidl/android/os/IVibratorManagerService.aidl
<SDK_PATH>/build-tools/31.0.0/aidl -h ./ -o ./ --lang=cpp -I ./aidl  aidl/android/os/CombinedVibration.aidl
<SDK_PATH>/build-tools/31.0.0/aidl -h ./ -o ./ --lang=cpp -I ./aidl  aidl/android/os/IVibratorStateListener.aidl
<SDK_PATH>/build-tools/31.0.0/aidl -h ./ -o ./ --lang=cpp -I ./aidl  aidl/android/os/VibrationAttributes.aidl
<SDK_PATH>/build-tools/31.0.0/aidl -h ./ -o ./ --lang=cpp -I ./aidl  aidl/android/os/VibratorInfo.aidl
参数含义
-h生成的头文件路径
-o生成的源文件路径
-I导入的头文件路径

3.3 编译构建项目

3.3.1 项目整体结构

在这里插入图片描述

目录功能
aidlVMS(VibratorManagerService) 相关的AIDL文件
so项目依赖的so目录
include项目依赖的so对应的导出头文件
vibrator_main.cpp主程序入口
CmakeLists.txtCmake构建脚本
*.hAIDL生成的头文件

*.h 头文件的间的关系:

在这里插入图片描述

3.3.2 CmakeLists内容及注意事项

cmake_minimum_required(VERSION 3.22.1)project("vibrator_sample")# 设置C++标准为C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)# -fno-rtti 这个标记很重要,否则编译不通过。
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")set(VIBRATOR vibrator_main)
include_directories(include)
add_executable(${VIBRATOR}vibrator_main.cppIVibratorManagerService.hBpVibratorManagerService.hCombinedVibration.hIVibratorStateListener.hVibrationAttributes.hVibratorInfo.hIVibratorManagerService.cpp)set_target_properties(${VIBRATOR} PROPERTIESRUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/out)# 指定NDK的STL库
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++")set(SHARED_LIB_DIR ${CMAKE_SOURCE_DIR}/so)
link_directories(${SHARED_LIB_DIR})add_library(binder SHARED IMPORTED)
set_target_properties(binder PROPERTIES IMPORTED_LOCATION ${SHARED_LIB_DIR}/libbinder.so)add_library(cutils SHARED IMPORTED)
set_target_properties(cutils PROPERTIES IMPORTED_LOCATION ${SHARED_LIB_DIR}/libcutils.so)add_library(utils SHARED IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION ${SHARED_LIB_DIR}/libutils.so)add_library(log SHARED IMPORTED)
set_target_properties(log PROPERTIES IMPORTED_LOCATION ${SHARED_LIB_DIR}/liblog.so)# 链接目标库
target_link_libraries(${VIBRATOR}binderutilscutilslog
)

3.4 测试结果

右侧执行 vibrator_main 可执行程序,左侧通过adb 能够看到RPC调用日志。
在这里插入图片描述

四、实现思路三(补充)

该功能可以通过shell实现

我在查看VibratorManagerService源码时,发现该执行shell命令操作:

class VibratorManagerService {@Overridepublic void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,String[] args, ShellCallback cb, ResultReceiver resultReceiver) {new VibratorManagerShellCommand(this).exec(this, in, out, err, args, cb, resultReceiver);}private final class VibratorManagerShellCommand extends ShellCommand {public static final String SHELL_PACKAGE_NAME = "com.android.shell";......}
}

因此,用下面的命令,也可以实现震动效果。

adb shell
cmd vibrator_manager synced oneshot 100 #执行震动操作

那么,我们可以写一个执行shell的C++程序来完成此功能。

五、写在最后

Demo功能地址

ADB 相关命令



最后,欢迎小伙伴关注我的公众号(主要内容Android、鸿蒙、FW、C/C++等),一起成长进步。

在这里插入图片描述

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

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

相关文章

港股指数实时行情API接口

港股 指数 实时 行情 API接口 # Restful API https://tsanghi.com/api/fin/index/HKG/realtime?token{token}&ticker{ticker}指定指数代码&#xff0c;获取该指数的实时行情&#xff08;开、高、低、收、量&#xff09;。 更新周期&#xff1a;实时。 请求方式&#xff1a…

vue router 切换路由的时候,页面的动画效果,使页面切换好看,以及控制有的页面使用切换路由特效,有的页面不用

一、使用切换效果 在router文件中 useTransition: true代表需要动画 meta: { title: “新开卡预填表单”, keepAlive: true, useTransition: true }, [{path: "/",name: "Home",meta: {title: "首页",keepAlive: true,useTransition: false},c…

给你的博客添加数据统计功能

20.数据统计 我们可以给博客添加统计功能&#xff0c;这里以百度统计和谷歌统计为例。 ‍ 百度统计 ‍ 添加网站 登陆百度统计后台&#xff1a;百度统计——一站式智能数据分析与应用平台&#xff0c;并登录&#xff0c;然后进入产品&#xff1a; ‍ 在使用设置–网站列…

图解KMP算法,带你彻底吃透KMP

模式串匹配——KMP算法 KMP算法一直是一个比较难以理解的算法&#xff0c;本篇文章主要根据《大话数据结构》中关于KMP算法的讲解&#xff0c;结合自己的思考&#xff0c;对于KMP算法进行一个比较详细的解释。 由于博主本人水平有限&#xff0c;难免会出现一些错误。如果发现…

C语言课程回顾:十、C语言之 指针

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 C语言之 指针 10 指针10.1 地址指针的基本概念10.2 变量的指针和指向变量的指针变量10.2.1 定义一个指针变量10.2.2 指针变量的引用10.2.3 指针变量作为函数参数10.2.4 指针变…

电脑远程开关机

1. 远程开机 参考&#xff1a;https://post.smzdm.com/p/664774/ 1.1 Wake On LAN - 局域网唤醒&#xff08;需要主板支持&#xff0c;一般都支持&#xff09; 要使用远程唤醒&#xff0c;有几种方式&#xff1a;使用类似向日葵开机棒&#xff08;很贵&#xff09;、公网ip&…

MongoDB教程(六):mongoDB复制副本集

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、MongoD…

使用Bind提供的域名解析服务

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 目录 一、DNS域名解析服务 二、安装Bind服务程序 1、正向解析 2、反向解析 三、部署从服务器 四、安全的加密传输 五、部署缓存服务器 六、分…

浅析班组建设在企业发展中的重要作用

众所周知&#xff0c;企业的成功与否往往取决于其内部管理的精细化和团队的高效协作。而班组作为企业最基层的管理单元&#xff0c;其建设质量直接关系到企业的整体运营效率和竞争力。今天&#xff0c;深圳天行健企业管理咨询公司将从多个维度分析班组建设在企业发展中的重要作…

【Python】深入了解 defaultdict:轻松处理默认值与复杂数据结构

文章目录 1. 深入理解 Python 中的 defaultdict&#xff1a;简化数据结构处理的利器2. defaultdict 基础概念3. 创建 defaultdict 实例3.1 基本用法3.2 使用其他工厂函数 4. defaultdict 的应用场景4.1 计数器4.2 分组数据 5. defaultdict 的高级用法5.1 嵌套 defaultdict5.2 自…

为什么流程图在项目管理中如此重要?

在我们的日常学习生活中&#xff0c;是不是感觉工作复杂繁琐&#xff0c;知识杂乱无章呢&#xff1f;那么流程图能够完美的解决这个问题&#xff0c;本文将会用一篇文章告诉你什么是流程图&#xff0c;流程图简单来说就是一种以图形方式表示算法、工作流程或过程的图表&#xf…

云服务器重置密码后,xshell远程连接不上,重新启用密码登录方式

云服务器重置密码后 &#xff0c;xshell连接出现不能使用密码登录 解决方案&#xff1a;以下来自阿里云重新启用密码登录方式帮助文档 为轻量应用服务器创建密钥且重启服务器使密钥生效后&#xff0c;服务器会自动禁止使用root用户及密码登录。如果您需要重新启用密码登录方式&…

数据结构-java中链表的存储原理及使用方式

目录 链表&#xff08;线性表的链式存储&#xff09; 代码实例&#xff1a;&#xff08;链表构建&#xff0c;头插尾插&#xff09; LinkedList LinkedList的使用&#xff1a; 1、构造方法 2、操作方法 LinkedList 和 ArrayList 的区别 链表&#xff08;线性表的链式存储…

基于python的图像去水印

1 代码 import cv2 import numpy as npdef remove_watermark(image_path, output_path):# 读取图片image cv2.imread(image_path)# 转换为灰度图gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 使用中值滤波去除噪声median_filtered cv2.medianBlur(gray, 5)# 计算图像的梯…

【Python学习笔记】:Python爬取音频

【Python学习笔记】&#xff1a;Python爬取音频 背景前摇&#xff08;省流可以不看&#xff09;&#xff1a; 人工智能公司实习&#xff0c;好奇技术老师训练语音模型的过程&#xff0c;遂请教&#xff0c;得知训练数据集来源于爬取某网页的音频。 很久以前看B站同济子豪兄的《…

实验三:图像的平滑滤波

目录 一、实验目的 二、实验原理 1. 空域平滑滤波 2. 椒盐噪声的处理 三、实验内容 四、源程序和结果 (1) 主程序&#xff08;matlab&#xff09; (2) 函数GrayscaleFilter (3) 函数MeanKernel (4) 函数MedFilter 五、结果分析 1. 空域平滑滤波 2. 椒盐噪声的处理…

【数据结构与算法 经典例题】判断二叉树是否对称

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法 经典例题》C语言 期待您的关注 目录 一、问题描述 二、解题思路 三、C语言实现代码 一、问题描述 给你一个二…

AI论文精读笔记-MAE

1. 论文基本信息 论文标题&#xff1a;Masked Autoencoders Are Scalable Vision Learners 作者&#xff1a;Kaiming He∗,† Xinlei Chen∗ Saining Xie Yanghao Li Piotr Doll ́ ar Ross Girshick 发表时间和期刊&#xff1a;19 Dec 2021; arxiv 论文链接&#xff1a;Mas…

【大模型书籍】从零开始大模型开发与微调:基于PyTorch与ChatGLM(附PDF)

哈喽各位&#xff0c;今天又来给大家分享大模型学习书籍了&#xff0c;今天是这本<从零开始大模型开发与微调&#xff1a;基于PyTorch与ChatGLM 书籍PDF分享>&#xff0c;大模型是深度学习自然语言处理皇冠上的一颗明珠&#xff0c;也是当前AI和NLP研究与产业中最重要的方…

HTML+CSS+JS用户管理(可储存用户数据)

使用cookies记录账号密码信息&#xff0c;可以注册、登录、注销账号。 点赞❤️收藏⭐️关注&#x1f60d; 效果图 源代码在效果图后面 源代码 HTML <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <…