之前介绍了蓝牙基本功能实现(开启,扫描,配对,连接等), 在这里继续介绍蓝牙电话 apk 的实现,及相关源码的分析。
蓝牙电话apk实现
只需要两步:
- 注册 BluetoothHeadsetClientCall 相关广播监听来电/接通/挂断的状态,获取蓝牙电话的相关信息,比如号码、设备信息等。
1 | BroadcastReceiver broadcastReceiver = new BroadcastReceiver(){ |
- 通过 BluetoothAdapter 获取并且初始化 BluetoothHeadsetClient 对象,然后就可以调用 api:
dial()/acceptCall()/rejectCall()/terminateCall()方法进行拨号/接通/拒接的操作了。
1 | bluetoothAdapter.getProfileProxy(MainActivity.this, new BluetoothProfile.ServiceListener() { |
蓝牙电话 Api 相关源码分析
路径1: frameworks\base\core\java\android\bluetooth\
蓝牙相关接口,蓝牙各种功能的发起点。
路径2:packages\apps\Bluetooth\src\com\android\bluetooth\
独立的Bluetooth.apk,里面包含蓝牙相关的各种服务,是java层和C/C++层的桥梁。
路径3: packages\apps\Bluetooth\jni\
调用底层C/C++实现各种蓝牙功能,并且反馈给java层。
蓝牙协议
a2dp: 和蓝牙耳机,音频有关,比如听歌等。
avrcp: 音频/视频通过连接的蓝牙控制,比如放歌时控制暂停等。
gatt:低功耗BLE有关,比如蓝牙按键。
hdp: 蓝牙医疗有关
hfp和hfpclient : 蓝牙通话有关,比如蓝牙通话的相关操作
hid: 蓝牙键盘键盘/鼠标
map: 同步蓝牙短信相关
opp: 蓝牙传输,比如传输文件等
pan: 个人局域网
pbap: 同步电话本,比如联系人/通话记录等
sap : 蓝牙通话,主要和SIM卡相关
sdp: 蓝牙服务发现/获取相关
这12个包分别实现了12中蓝牙功能,大多数以服务的形式存在,运行在 Bluetooth.apk 中。不仅如此,还具有以下特点:
1,每一个服务相互独立,互相毫无任何影响, 继承自 ProfileService,由 AdapterService 服务统一管理。
2,每一个服务在路径1中都存在对应的客户端类,通过Binder进行跨进程通信。
3,每一个服务在路径3中都存在对应的C/C++类,通过JNI机制互相调用。
4,每一个服务的启动,对应的Binder以及JNI机制的调用原理,方法,流程几乎都是一样的。
通过蓝牙获取通讯录及通话记录
路径:android-8.0.0_r1\frameworks\base\core\java\android\bluetooth
Pbap 协议:通过 Pbap(Phone Book Access Profile) 协议同步联系人/通话记录
BluetoothPbapClient.java:
该类是 @hide,可以通过修改源码重新编译,或者通过反射的方式获取此类的对象,,但应该是尚不成熟的,所以在此仅做简单学习用吧。
注:使用反射时,我的一加6手机发现该类的构造函数不存在,应该是被阉割了。在 Hikey970 板子上测试是ok的。
1 | // Create a BluetoothPbapClient proxy object. |
蓝牙电话API调用
主要类:
BluetoothHeadsetClient.java 主要负责蓝牙通话的相关动作,比如接听等等
BluetoothHeadsetClientCall.java 主要负责蓝牙通话的状态,比如是来电还是去电等等。
HeadsetClientHalConstants.java 类里面只是定义了一些 int/boolean 类型的值。
HeadsetClientService.java 从名字就知道它是一个服务,里面还有一个BluetoothHeadsetClientBinder内部类,该内部类主要负责和 BluetoothHeadsetClient 进行跨进程通信。另外, HeadsetClientService 也是 BluetoothHeadsetClientBinder 和 HeadsetClientStateMachine 之间的桥梁。
HeadsetClientStateMachine 是一个状态机,即管理连接的状态也是通话时 java 和 C/C++ 之间的桥梁,通过 JNI 机制和 com_android_bluetooth_hfpclient 里面的方法互相调用。
com_android_bluetooth_hfpclient 蓝牙通话动作,拨号/接听/挂断/拒接 实际的执行者。
打电话
1 | // 设置蓝牙电话客户端监听 |
接听电话
如果有来电,服务端手机接通电话或者对方挂断电话,我们怎么知道呢?这些都是 com_android_bluetooth_hfpclient.cpp 通过 JNI 机制调用通话状态机的方法 sendCallChangedIntent,将电话的状态(包含在 BluetoothHeadsetClientCall.java 中)通过广播发送出来,第三方apk监听状态就可以进行相应的操作了。
HeadsetClientStateMachine.java:
1 | private void sendCallChangedIntent(BluetoothHeadsetClientCall c) { |
Car 模块拨打电话
Android 7.0 增加车载新特性,在Car模块中实现了蓝牙电话功能
路径: android-8.0.0_r1\packages\apps\Car\Dialer\src\com\android\car\dialer
1 | DialerFragment.java: |
TelecomManager.java:
1 | public void placeCall(Uri address, Bundle extras) { |
TelecomService.java:
1 |
|
TelecomServiceImpl.java:
1 |
|
UserCallIntentProcessor.java:
1 | public void processIntent(Intent intent, String callingPackageName, |
PrimaryCallReceiver.java:
1 |
|
CallIntentProcessor.java:
1 | public void processIntent(Intent intent) { |
NewOutgoingCallBroadcastIntentReceiver.java:
1 | public int processIntent() { |
CallsManager.java:
1 | Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras, |
Call.java:
1 | void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) { |
CreateConnectionProcessor.java:
1 | public void process() { |
最终还是调用 NativeInterface.dialNative(), 同上
Car 模块接听电话
android-8.0.0_r1\packages\apps\Car\Dialer\src\com\android\car\dialer\telecom\embedded
1 | TelecomUiCallManager.java: |
最终还是调用 NativeInterface.handleCallActionNative(), 同上