去掉Bluetooth.apk后,如何对接A2DP、AVRCP及播放器CarMediaApp.apk
一、A2dpSinkService.java
1、这个文件位于packages\apps\Bluetooth\src\com\android\bluetooth\a2dpsink。
可以看到这是一个service,看一下packages\apps\Bluetooth\AndroidManifest.xml
可以看到这个通过action:"android.bluetooth.IBluetoothA2dpSink"拉起来的
找到拉起服务的地方:
BluetoothA2dpSink.java
位于frameworks\base\core\java\android\bluetooth
注意:using_anwBt部分是为了剥离原生蓝牙添加的代码,当using_anwBt设置为true时表示不用原生蓝牙,此时不应该拉起A2dpSinkService
2、A2dpSinkService.java中有一个类BluetoothA2dpSinkBinder
可以看到这个类实现了AIDL:IBluetoothA2dpSink
注意:要把原生蓝牙剥离,这个AIDL的实现用到的地方都需要剥离。可以自己模仿A2dpSinkStateMachine写个类,用这个类管理连接状态等信息。
3、A2dpSinkService.java中还启动了一个服务:A2dpMediaBrowserService
A2dpMediaBrowserService即
可以看到这是一个MediaBrowserService。从这里可以知道这是跟播放器(用MediaController)对接的。
注意:要剥离原生蓝牙,MediaBrowserService不能用,要自己实现一套MediaBrowserService。
这里面有一些要点:
(1)AVRCP连接成功时。
mAvrcpCommandQueue.obtainMessage(MSG_DEVICE_CONNECT, btDev).sendToTarget();
找到对应处理
继续
这里面获取AvrcpControllerService,先不管。然后执行了refreshInitialPlayingState();
这里面可以看到执行了MediaSession的setMetadata和setPlaybackState。
setMetadata:设置id3信息等
setPlaybackState:设置播放状态、设置支持哪些action: ACTION_PAUSE、ACTION_PLAY、ACTION_SKIP_TO_NEXT、ACTION_SKIP_TO_PREVIOUS等(一定要设置不然系统播放器不显示上一首/下一首的播放按键)。
(2)AVRCP断开时
这时也是执行了setPlaybackState。
(3)TRACK Change的时候
这里也需要调用setMetadata和setPlaybackState
所以剥离原生蓝牙后,需要在更新ID3、播放进度、播放状态等时,根据需要调用setMetadata和setPlaybackState(注意:每次调用setPlaybackState时都要setActions)
自己的蓝牙service中要添加(这样播放器下达的播放/暂停/上一首/下一首等命令就可以通过MediaSesson从MediaController获取到,而ID3\播放进度\播放状态等信息就可以通过MediaSession的setPlaybackState、setMetadata等设置后发送给MediaController):
private static MediaSession mSession;
private static MediaSession.Callback SessionCallback = new MediaSession.Callback() {@Overridepublic void onPlay() {//playsuper.onPlay();Log.d(DEBUG_TAG, "onPlay");if(MediaController_switch) {switch (GetPowerStatus()) {case POWER_OFFING:case POWER_ONING:case POWER_OFF:Log.d(DEBUG_TAG, GetPowerStatus().toString());break;case POWER_ON:if (main_avrcp_btaddr != null && main_avrcp_btaddr.equals("") == false) {Log.d(DEBUG_TAG, "AVRCPControl play");int ret = AVRCPControl(main_avrcp_btaddr, 0);}break;default:break;}}}@Overridepublic void onPause() {//pausesuper.onPause();Log.d(DEBUG_TAG, "onPause");if(MediaController_switch) {switch (GetPowerStatus()) {case POWER_OFFING:case POWER_ONING:case POWER_OFF:Log.d(DEBUG_TAG, GetPowerStatus().toString());break;case POWER_ON:if (main_avrcp_btaddr != null && main_avrcp_btaddr.equals("") == false) {Log.d(DEBUG_TAG, "AVRCPControl pause");int ret = AVRCPControl(main_avrcp_btaddr, 2);}break;default:break;}}}@Overridepublic void onStop() {//stopsuper.onStop();Log.d(DEBUG_TAG, "onStop");if(MediaController_switch) {switch (GetPowerStatus()) {case POWER_OFFING:case POWER_ONING:case POWER_OFF:Log.d(DEBUG_TAG, GetPowerStatus().toString());break;case POWER_ON:if (main_avrcp_btaddr != null && main_avrcp_btaddr.equals("") == false) {Log.d(DEBUG_TAG, "AVRCPControl stop");int ret = AVRCPControl(main_avrcp_btaddr, 4);}break;default:break;}}}@Overridepublic void onSkipToNext() {//nextsuper.onSkipToNext();Log.d(DEBUG_TAG, "onSkipToNext");if(MediaController_switch) {switch (GetPowerStatus()) {case POWER_OFFING:case POWER_ONING:case POWER_OFF:Log.d(DEBUG_TAG, GetPowerStatus().toString());break;case POWER_ON:if (main_avrcp_btaddr != null && main_avrcp_btaddr.equals("") == false) {Log.d(DEBUG_TAG, "AVRCPControl next");int ret = AVRCPControl(main_avrcp_btaddr, 8);}break;default:break;}}}@Overridepublic void onSkipToPrevious() {//previoussuper.onSkipToPrevious();Log.d(DEBUG_TAG, "onSkipToPrevious");if(MediaController_switch) {switch (GetPowerStatus()) {case POWER_OFFING:case POWER_ONING:case POWER_OFF:Log.d(DEBUG_TAG, GetPowerStatus().toString());break;case POWER_ON:if (main_avrcp_btaddr != null && main_avrcp_btaddr.equals("") == false) {Log.d(DEBUG_TAG, "AVRCPControl Previous");int ret = AVRCPControl(main_avrcp_btaddr, 10);}break;default:break;}}}@Overridepublic void onFastForward() {super.onFastForward();Log.d(DEBUG_TAG, "onFastForward");}@Overridepublic void onRewind() {super.onRewind();Log.d(DEBUG_TAG, "onRewind");}@Overridepublic void onCustomAction(@NonNull String action, @Nullable Bundle extras) {super.onCustomAction(action, extras);//mute/unmuteif(action.equals("mute")){boolean bEnable = extras.getBoolean("mute");Log.d(DEBUG_TAG, "WAVEOUT_Mute " + bEnable);switch (GetPowerStatus()){case POWER_OFFING:case POWER_ONING:case POWER_OFF:Log.d(DEBUG_TAG,GetPowerStatus().toString());break;case POWER_ON:if (bEnable)A2dpMute(1);elseA2dpMute(0);break;default:break;}}}};
public static class MusicService extends MediaBrowserService {@Overridepublic void onCreate() {super.onCreate();Log.d(DEBUG_TAG, "MusicService onCreate");PlaybackState mPlaybackState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE,0,1.0f).setActions(PlaybackStateAction).build();mSession = new MediaSession(this,"MusicService");mSession.setCallback(SessionCallback);mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);mSession.setPlaybackState(mPlaybackState);Log.d(DEBUG_TAG, mSession.getSessionToken().toString());setSessionToken(mSession.getSessionToken());}@androidx.annotation.Nullable@Overridepublic BrowserRoot onGetRoot(@androidx.annotation.NonNull String s, int i, @androidx.annotation.Nullable Bundle bundle) {Log.d(DEBUG_TAG, "MusicService onGetRoot");return new BrowserRoot("YOUR_UNIQUE_MEDIA_ROOT_ID",null);}@Overridepublic void onLoadChildren(@androidx.annotation.NonNull String s, @androidx.annotation.NonNull MediaBrowserService.Result<List<MediaBrowser.MediaItem>> result) {Log.d(DEBUG_TAG, "MusicService onLoadChildren");result.sendResult(mEmptyList);}
}
注意:我的service里面添加的MediaBrowserService(即MusicService),所以我在蓝牙service启动的时候把MusicService拉起来,蓝牙service关闭的时候,需要把MusicService关闭
蓝牙service的OnCreate
蓝牙service的onDestroy
二、AvrcpControllerService.java
位于:packages\apps\Bluetooth\src\com\android\bluetooth\avrcpcontroller
在前面把A2dpMediaBrowserService去掉后,这里主要关注:IBluetoothAvrcpController的实现。需要把用这个AIDL的地方去掉就可以
1、获取已连接AVRCP controller的设备
2、获取avrcp controller处于一定连接状态的设备。其实实现里面描述This function only supports STATE_CONNECTED
3、获取某个设备的AVRCP controller的连接状态
4、获取repeat shuffle mode等
5、设置repeat shuffle mode等
6、这个不知道
这样就可把AVRCP的连接状态、repeat/shuffle mode等跟自己的service对接起来。
去掉Bluetooth.apk后,如何对接A2DP、AVRCP及播放器CarMediaApp.apk相关推荐
- 壳网七彩视界开源对接易支付原生播放器可投屏可选集
源码修复佣金,闪退,等一些问题.免费模块,不用付费模块!完美运营,对接苹果cms上传后台文件到网站根目录解压配置网站伪静态为ThinkPHP:将application目录下的database.php, ...
- 蓝牙协议 HFP,HSP,A2DP,AVRCP,OPP,PBAP
蓝牙协议 HFP,HSP,A2DP,AVRCP,OPP,PBAP 简介: HSP(手机规格)– 提供手机(移动电话)与耳机之间通信所需的基本功能. HFP(免提规格)– 在 HSP 的基础上增加了某些 ...
- 蓝牙协议HFP,HSP,A2DP,AVRCP等
蓝牙协议HFP,HSP,A2DP,AVRCP等 转载于:https://blog.csdn.net/bin_linux96/article/details/88848653 简介 HSP(手机规格,H ...
- android反编译APK后,是smali文件,能反编译成dex文件
我也面临相同的问题,但目前还没找到类似反编译smali文件的方法,一般得到dex文件只需要用WinRAR或其他压缩软件打开apk文件就能得到.但也有例外好像,技术先进了,今天遇到个apk用WinRAR ...
- android下载后的app自动安装,Android 7.0 下载APK后自动安装
随着Android版本越来越高,Android对隐私的保护力度也越来越大.这些隐私权限的更改在为用户带来更加安全的操作系统的同时也为开发者带来了一些新的任务.如何让你的APP能够适应这些改变而不是崩溃 ...
- 给定一个字符串s,返回去掉子串mi后的字符串。
2019独角兽企业重金招聘Python工程师标准>>> 给定一个字符串s,返回去掉子串"mi"后的字符串.(注:删除n个mi后,仍包含mi应一同删除,如ammim ...
- python csv读取数据 去掉标题-Python读csv文件去掉一列后再写入新的文件实例
用了两种方式解决该问题,都是网上现有的解决方案. 场景说明: 有一个数据文件,以文本方式保存,现在有三列user_id,plan_id,mobile_id.目标是得到新文件只有mobile_id,pl ...
- Android APK系列4-------Android编译APK后的系统结构
Android APK系列4-------Android编译APK后的系统结构 system文件夹就是system.img的解压结果,data就是userdata.img的解压结果,root就是ram ...
- Unity打包apk后,应用icon是安卓小机器人
今天遇到一个很麻烦的问题,使用Unity打包成apk后,手机的应用在安卓的文件管理中显示的是包名("com.company.productname", Unity包名),然后图标i ...
最新文章
- php lmpl,tjx-cold: 用于根据配置模板,快速生成controller,service,serviceimpl 代码
- 使用pull解析XML文件
- 如何查询Linux内核版本
- Win10微软帐户切换不回Administrator本地帐户的解决方法【亲测】
- 阿里舆情︱舆情热词分析架构简述(Demo学习)
- 用函数调用的方法输出乘法口诀表
- 《微信背后的产品观》一书
- 博图如何上载wincc程序_博图导入触摸屏程序 如何通过博图下载触摸屏程序
- 计算机管理员命令符怎么关机,详细教您电脑关机命令是什么
- mac分区数据恢复|mac分区后数据丢失怎么恢复?
- 笔记本无线上网卡的种类
- 在32bit操作系统下用好4GB物理内存
- jzoj3457. 【NOIP2013模拟联考3】沙耶的玩偶
- python黑白棋设计思路_[黑白棋]规则、大食策略及AI转化思考
- 软件项目管理_作业1
- 关于粽子的生产产线提速
- HbuilderX 小程序转快应用
- 睿智的目标检测——YoloV7-Tiny-OBB旋转目标检测算法部署
- 通过OSG实现对模型的日照模拟
- 大一转专业计算机考什么,武汉大学 计算机 转专业 经验贴