BluetoothHeadsetClient.java acceptCall 调用流程及日志分析
1. 完整的 acceptCall 调用流程
1.1 调用时序图
应用层 (App)
↓ 1. 调用 acceptCall()
框架层 (BluetoothHeadsetClient.java)
↓ 2. 通过Binder IPC
蓝牙服务进程 (HeadsetClientService)
↓ 3. 状态机处理
HeadsetClientStateMachine
↓ 4. JNI调用
本地层 (android_bluetooth_BluetoothHeadsetClient.cpp)
↓ 5. 协议栈接口
btif_hf_client
↓ 6. BTA层处理
bta_hf_client
↓ 7. 发送AT命令
蓝牙耳机设备
2. 详细的代码调用路径
2.1 应用层调用
// 应用代码
BluetoothHeadsetClient headsetClient; // 已初始化的客户端
BluetoothDevice device; // 已连接的设备
// 调用acceptCall
boolean result = headsetClient.acceptCall(device,
BluetoothHeadsetClient.CALL_ACCEPT_NONE);
Log.d("App", "acceptCall 调用结果: " + result);
2.2 BluetoothHeadsetClient.java
// frameworks/base/core/java/android/bluetooth/BluetoothHeadsetClient.java
public boolean acceptCall(BluetoothDevice device, int flag) {
if (DBG) log("acceptCall()");
final IBluetoothHeadsetClient service = mService;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
return false;
}
// 权限检查
if (!checkBluetoothPermission()) {
return false;
}
try {
// 通过Binder调用服务端
return service.acceptCall(device, flag);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
return false;
}
3. 完整的调用链路日志
3.1 启用详细日志
# 启用所有相关日志
adb shell setprop log.tag.BluetoothHeadsetClient V
adb shell setprop log.tag.HeadsetClientService V
adb shell setprop log.tag.HeadsetClientStateMachine V
adb shell setprop log.tag.HeadsetClientNativeInterface V
adb shell setprop persist.bluetooth.btsnoopenable true
# 清除旧日志
adb logcat -c
3.2 预期日志输出
# 查看完整调用链
adb logcat | grep -E "acceptCall|HeadsetClient"
# 预期日志(按时间顺序):
# 1. 应用层调用
D/BluetoothHeadsetClient: acceptCall() - device: XX:XX:XX:XX:XX:XX, flag: 1
# 2. Binder调用
D/HeadsetClientService: acceptCall() - device: XX:XX:XX:XX:XX:XX, flag: 1
# 3. 状态机处理
D/HeadsetClientStateMachine: acceptCall - device: XX:XX:XX:XX:XX:XX, flag: 1
D/HeadsetClientStateMachine: acceptCall: CurrentState=Connected
# 4. 发送消息到状态机
D/HeadsetClientStateMachine: sendMessage(ACCEPT_CALL)
# 5. 状态机处理消息
D/HeadsetClientStateMachine: processMessage: ACCEPT_CALL
# 6. 调用Native接口
D/HeadsetClientStateMachine: acceptCall: Calling Native
D/HeadsetClientNativeInterface: acceptCall: device=XX:XX:XX:XX:XX:XX, flag=1
# 7. JNI层
D/bt_hf_client: acceptCallNative: device=XX:XX:XX:XX:XX:XX, flag=1
# 8. 协议栈处理
D/btif_hf_client: btif_hf_client_at_cb: AT+ATA
D/BTA_HF_CLIENT: BTA_HfClientAcceptCall
# 9. 发送AT命令
D/BTA_HF_CLIENT: Send AT command: ATA
# 10. 耳机响应
D/BTA_HF_CLIENT: AT response: OK
# 11. 回调到Java层
D/HeadsetClientNativeInterface: onCallChange: call state changed
D/HeadsetClientStateMachine: processCallChange: callId=2, state=ACTIVE
# 12. 发送广播
D/HeadsetClientService: broadcastCallChanged: call state=ACTIVE
I/HeadsetClientService: ACTION_CALL_CHANGED broadcast sent
# 13. 返回结果
D/BluetoothHeadsetClient: acceptCall returned: true
4. 实际代码跟踪
4.1 HeadsetClientService.java
// packages/apps/Bluetooth/src/com/android/bluetooth/hfpclient/
// HeadsetClientService.java
@Override
public boolean acceptCall(BluetoothDevice device, int flag) {
Log.d(TAG, "acceptCall() - device: " + device + ", flag: " + flag);
// 检查设备连接状态
synchronized (mStateMachines) {
HeadsetClientStateMachine stateMachine = mStateMachines.get(device);
if (stateMachine == null) {
Log.w(TAG, "acceptCall: device " + device + " not connected");
return false;
}
// 检查通话状态
if (!hasIncomingCall(device)) {
Log.w(TAG, "acceptCall: no incoming call for device " + device);
return false;
}
// 转发到状态机
int result = stateMachine.acceptCall(flag);
Log.d(TAG, "acceptCall: result=" + result);
return (result == HeadsetClientStateMachine.RESULT_SUCCESS);
}
}
private boolean hasIncomingCall(BluetoothDevice device) {
HeadsetClientStateMachine stateMachine = mStateMachines.get(device);
if (stateMachine != null) {
List<HeadsetClientCall> calls = stateMachine.getCurrentCalls();
for (HeadsetClientCall call : calls) {
if (call.getState() == HeadsetClientCall.CALL_STATE_INCOMING) {
return true;
}
}
}
return false;
}
4.2 HeadsetClientStateMachine.java
// packages/apps/Bluetooth/src/com/android/bluetooth/hfpclient/
// HeadsetClientStateMachine.java
public int acceptCall(int flag) {
Log.d(TAG, "acceptCall - flag: " + flag);
// 检查当前状态
if (getCurrentState() != mConnected && getCurrentState() != mAudioOn) {
Log.w(TAG, "acceptCall: wrong state: " + getCurrentStateName());
return RESULT_FAIL_WRONG_STATE;
}
// 获取当前来电
HeadsetClientCall incomingCall = null;
for (HeadsetClientCall call : mCalls.values()) {
if (call.getState() == HeadsetClientCall.CALL_STATE_INCOMING) {
incomingCall = call;
break;
}
}
if (incomingCall == null) {
Log.w(TAG, "acceptCall: no incoming call found");
return RESULT_FAIL_NO_CALL;
}
// 发送消息到状态机
Message msg = obtainMessage(ACCEPT_CALL, flag, 0, incomingCall);
sendMessage(msg);
return RESULT_SUCCESS;
}
// 处理ACCEPT_CALL消息
private class Connected extends State {
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case ACCEPT_CALL:
Log.d(TAG, "processMessage: ACCEPT_CALL");
HeadsetClientCall call = (HeadsetClientCall) message.obj;
int flag = message.arg1;
// 调用Native接口
if (!mNativeInterface.acceptCall(mDevice.getAddress(), flag)) {
Log.e(TAG, "Native acceptCall failed");
return NOT_HANDLED;
}
// 更新通话状态
call.setState(HeadsetClientCall.CALL_STATE_ACTIVE);
broadcastCallChanged(call);
Log.d(TAG, "Call accepted successfully, callId: " + call.getId());
return HANDLED;
}
return NOT_HANDLED;
}
}
4.3 HeadsetClientNativeInterface.java
// packages/apps/Bluetooth/src/com/android/bluetooth/hfpclient/
// HeadsetClientNativeInterface.java
public boolean acceptCall(String address, int flag) {
Log.d(TAG, "acceptCall: address=" + address + ", flag=" + flag);
synchronized (mLock) {
if (!isNativeAvailable()) {
Log.e(TAG, "Native interface not available");
return false;
}
byte[] addr = Utils.getBytesFromAddress(address);
return acceptCallNative(addr, flag);
}
}
private native boolean acceptCallNative(byte[] address, int flag);
5. JNI 层代码
5.1 android_bluetooth_BluetoothHeadsetClient.cpp
// frameworks/base/core/jni/android_bluetooth_BluetoothHeadsetClient.cpp
static jboolean acceptCallNative(JNIEnv* env, jobject object,
jbyteArray address, jint flag) {
ALOGV("%s", __FUNCTION__);
if (!sBluetoothHfpClientInterface) {
ALOGE("HFP Client Interface is not initialized");
return JNI_FALSE;
}
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
ALOGE("Failed to get device address");
return JNI_FALSE;
}
RawAddress bd_addr;
bd_addr.FromOctets((uint8_t*)addr);
bt_status_t status = sBluetoothHfpClientInterface->accept_call(
bd_addr, (bthf_client_call_accept_t)flag);
env->ReleaseByteArrayElements(address, addr, 0);
ALOGV("acceptCallNative: status=%d", status);
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
6. 底层协议栈日志
6.1 btif_hf_client.cc
// system/bt/btif/src/btif_hf_client.cc
bt_status_t btif_hf_client_accept_call(const RawAddress& bd_addr,
bthf_client_call_accept_t flag) {
BTIF_TRACE_DEBUG("%s: device=%s, flag=%d",
__func__, bd_addr.ToString().c_str(), flag);
btif_hf_client_cb_t* btif_hf = btif_hf_client_find_device_by_bda(bd_addr);
if (btif_hf == NULL) {
BTIF_TRACE_ERROR("%s: device not connected", __func__);
return BT_STATUS_NOT_READY;
}
// 检查是否有来电
if (!btif_hf_client_has_incoming_call(btif_hf)) {
BTIF_TRACE_ERROR("%s: no incoming call", __func__);
return BT_STATUS_NOT_READY;
}
tBTA_HF_CLIENT_AT_CMD_TYPE at_cmd_type = BTA_HF_CLIENT_ATA;
if (flag == BTHF_CLIENT_CALL_ACCEPT_HOLD) {
at_cmd_type = BTA_HF_CLIENT_CHLD_0;
} else if (flag == BTHF_CLIENT_CALL_ACCEPT_TERMINATE) {
at_cmd_type = BTA_HF_CLIENT_CHLD_1;
}
// 发送到BTA层
BTA_HfClientAcceptCall(bd_addr, at_cmd_type);
BTIF_TRACE_DEBUG("%s: success", __func__);
return BT_STATUS_SUCCESS;
}
7. 调试工具脚本
7.1 完整日志收集脚本
#!/bin/bash
# monitor_accept_call.sh
echo "=== 开始监控 acceptCall 调用 ==="
echo "时间: $(date)"
# 1. 启用所有相关日志
echo "启用调试日志..."
adb shell setprop log.tag.BluetoothHeadsetClient VERBOSE
adb shell setprop log.tag.HeadsetClientService VERBOSE
adb shell setprop log.tag.HeadsetClientStateMachine VERBOSE
adb shell setprop log.tag.HeadsetClientNativeInterface VERBOSE
adb shell setprop persist.bluetooth.btsnoopenable true
adb shell setprop persist.bluetooth.btsnooppath /sdcard/btsnoop_hci.log
adb shell setprop persist.bluetooth.btsnoopsize 0xffffffff
# 2. 重启蓝牙服务
echo "重启蓝牙服务..."
adb shell stop bluetoothd
sleep 1
adb shell start bluetoothd
sleep 2
# 3. 清除旧日志
echo "清除日志缓存..."
adb logcat -c
adb shell logcat -b all -c
# 4. 开始监控
echo "开始监控日志..."
echo "按 Ctrl+C 停止监控"
# 保存日志到文件
LOG_FILE="accept_call_$(date +%Y%m%d_%H%M%S).log"
echo "日志保存到: $LOG_FILE"
# 监控所有相关日志
adb logcat -v threadtime \
| grep -E "acceptCall|HeadsetClient|bt_hf|BTA_HF" \
| tee $LOG_FILE
7.2 问题诊断脚本
#!/bin/bash
# diagnose_accept_call.sh
echo "=== acceptCall 问题诊断 ==="
echo "诊断时间: $(date)"
echo ""
# 1. 检查蓝牙状态
echo "1. 蓝牙服务状态:"
adb shell dumpsys bluetooth_manager | grep -A 5 "enabled state"
echo ""
# 2. 检查HFP客户端状态
echo "2. HFP客户端服务状态:"
adb shell dumpsys activity service com.android.bluetooth/.hfpclient.HeadsetClientService
echo ""
# 3. 查看已连接的设备
echo "3. 已连接的蓝牙设备:"
adb shell dumpsys bluetooth_manager | grep -A 10 "Connected devices"
echo ""
# 4. 查看通话状态
echo "4. 当前通话状态:"
adb shell dumpsys bluetooth_manager | grep -A 20 "Headset Client"
echo ""
# 5. 查看系统属性
echo "5. 蓝牙相关属性:"
adb shell getprop | grep -E "bluetooth|hfp|sco"
echo ""
# 6. 查看进程状态
echo "6. 蓝牙相关进程:"
adb shell ps | grep -E "bluetooth|bt"
echo ""
echo "诊断完成"
8. 常见问题的日志表现
8.1 权限问题日志
W/BluetoothHeadsetClient: acceptCall() - Missing BLUETOOTH_CONNECT permission
W/ActivityManager: Permission Denial:
android.permission.BLUETOOTH_CONNECT not granted
8.2 设备未连接日志
W/HeadsetClientService: acceptCall: device XX:XX:XX:XX:XX:XX not connected
E/HeadsetClientStateMachine: acceptCall: device not in connected state
8.3 无来电日志
W/HeadsetClientService: acceptCall: no incoming call for device XX:XX:XX:XX:XX:XX
E/btif_hf_client: btif_hf_client_accept_call: no incoming call
8.4 音频未连接日志
D/HeadsetClientStateMachine: acceptCall: Audio not connected, connecting audio first
D/AudioManager: startBluetoothSco()
D/HeadsetClientStateMachine: audioStateChanged: CONNECTING
9. 添加调试日志
9.1 在代码中添加详细日志
// 在HeadsetClientService中添加
public boolean acceptCall(BluetoothDevice device, int flag) {
Log.d(TAG, "=== acceptCall 开始 ===");
Log.d(TAG, "设备: " + device);
Log.d(TAG, "flag: " + flag);
Log.d(TAG, "调用栈: ", new Throwable());
// 检查设备
HeadsetClientStateMachine stateMachine = mStateMachines.get(device);
if (stateMachine == null) {
Log.e(TAG, "设备未在状态机中注册");
Log.d(TAG, "已注册的设备: " + mStateMachines.keySet());
return false;
}
// 检查通话
List<HeadsetClientCall> calls = stateMachine.getCurrentCalls();
Log.d(TAG, "当前通话数: " + calls.size());
for (HeadsetClientCall call : calls) {
Log.d(TAG, "通话: " + call);
}
// 继续原有逻辑...
}
10. 日志分析工具
10.1 Python 日志分析脚本
#!/usr/bin/env python3
# analyze_accept_call.py
import re
from datetime import datetime
def analyze_log_file(filename):
with open(filename, 'r', encoding='utf-8') as f:
lines = f.readlines()
accept_call_flows = []
current_flow = []
for line in lines:
if 'acceptCall' in line.lower():
if current_flow:
accept_call_flows.append(current_flow)
current_flow = [line]
elif current_flow and any(tag in line for tag in
['HeadsetClient', 'bt_hf', 'BTA_HF', 'NativeInterface']):
current_flow.append(line)
if current_flow:
accept_call_flows.append(current_flow)
print(f"找到 {len(accept_call_flows)} 次 acceptCall 调用")
for i, flow in enumerate(accept_call_flows, 1):
print(f"\n=== 调用 #{i} ===")
for j, line in enumerate(flow, 1):
print(f"{j:2d}. {line.strip()}")
# 分析结果
success = any('success' in line.lower() for line in flow)
error = any('error' in line.lower() or 'fail' in line.lower()
for line in flow)
if success:
print("结果: ✓ 成功")
elif error:
print("结果: ✗ 失败")
else:
print("结果: ? 未知")
if __name__ == '__main__':
analyze_log_file('accept_call.log')
通过以上完整的调用流程和日志分析,您可以系统地排查 acceptCall 无反应的问题。关键是要查看每一层是否有相应的日志输出,以及在哪里失败。

600

被折叠的 条评论
为什么被折叠?



