前段时间做车机蓝牙电话的项目,接触到了部分关于蓝牙电话API的调用。
首先车机与手机端配好对,其中配置项中的手机音频和共享联系人要打开,等两端连接好后,就进入正题,
通过 adapter= BluetoothAdapter.getDefaultAdapter();拿到蓝牙适配器,然后 通过
adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.HEADSET_CLIENT);
设置蓝牙电话客户端的监听mProfileServiceListener,
private final BluetoothProfile.ServiceListener mProfileServiceListener = new BluetoothProfile.ServiceListener()
{
public void onServiceConnected(int paramInt, BluetoothProfile paramBluetoothProfile)
{
mHeadsetClient = ((BluetoothHeadsetClient)paramBluetoothProfile);
}
public void onServiceDisconnected(int paramInt)
{
}
};
这样当连接好了,我们就拿到了蓝牙电话客户端的代理mHeadsetClient 。关于这部分的API,文件在frameworks/base/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl,网上链接 https://source.codeaurora.org/quic/la/platform/frameworks/base/tree/core/java/android/bluetooth/BluetoothHeadsetClientCall.aidl?h=m。
常用的接口有
boolean acceptCall(in BluetoothDevice device, int flag);
boolean holdCall(in BluetoothDevice device);
boolean rejectCall(in BluetoothDevice device);
boolean terminateCall(in BluetoothDevice device, int index);
boolean dial(in BluetoothDevice device, String number);
比如通过调用mHeadsetClient.dial(mDevice, "110");就可以给110打电话了。
上面是关于接口的简单调用。我们再来看看这个打电话的接口是怎么实现的。
android frameworks层给我们开放了大量的接口,供开发者使用。上面提到了IBluetoothHeadsetClient.aidl,
看文件后辍,知道是通过aidl调用来实现的,实现它的文件是 BluetoothHeadsetClient.java,
我们发现,实现打电话功能函数是dial,而真正实现打电话的是mService.dial(device, number); 这个mService的定义public boolean dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.dial(device, number); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; }
private IBluetoothHeadsetClient mService;
当我们获取得到一个BluetoothHeadsetClient时,会调用
BluetoothHeadsetClient的构造函数,它会执行一个doBind(),boolean doBind() { Intent intent = new Intent(IBluetoothHeadsetClient.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, android.os.Process.myUserHandle())) { Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent); return false; } return true; }
当绑定服务成功时,mService 被赋值
private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { mService = IBluetoothHeadsetClient.Stub.asInterface(service);
}
@Override public void onServiceDisconnected(ComponentName className) { } };
原来我们获得一个IBluetoothHeadsetClient 对象时,系统会去绑定一个服务,并获取这个服务的代理,而这个服务才是真正实现打电话的地方。
这个服务实现是在packages/apps/Bluetooth/src/com/android/bluetooth/hfpclient/它调用了HeadsetClientService
.java, 绑定的服务是个内部类BluetoothHeadsetClientBinderboolean dial(BluetoothDevice device, String number) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); int connectionState = mStateMachine.getConnectionState(device); if (connectionState != BluetoothProfile.STATE_CONNECTED && connectionState != BluetoothProfile.STATE_CONNECTING) { return false; } Message msg = mStateMachine.obtainMessage(HeadsetClientStateMachine.
DIAL_NUMBER
); msg.obj = number; mStateMachine.sendMessage(msg); return true; }仔细一看,怎么又冒出个
mStateMachine,这是安卓中的状态机,我们跟进到HeadsetClientStateMachine.java文件中去。当mStateMachine将信息发出(
mStateMachine.sendMessage(msg)
),此时蓝牙电话状态机状态为Connected ,进入processMessage流程分支case DIAL_NUMBER: if (dialNative((String) message.obj)) { addQueuedAction(DIAL_NUMBER, message.obj); } else { Log.e(TAG, "ERROR: Cannot dial with a given number:" + (String) message.obj); } break;
我们终于发现,实现打电话的方法是dialNative, 一个本地方法。
static jboolean dialNative(JNIEnv *env, jobject object, jstring number_str) { bt_status_t status; const char *number = NULL; if (!sBluetoothHfpClientInterface) return JNI_FALSE; if (number_str != NULL) { number = env->GetStringUTFChars(number_str, NULL); } if (number != NULL) { if ( (status = sBluetoothHfpClientInterface->dial(number)) != BT_STATUS_SUCCESS) { ALOGE("Failed to dial, status: %d", status); } } else { if ( (status = sBluetoothHfpClientInterface->dial("")) != BT_STATUS_SUCCESS) { ALOGE("Failed to dial, status: %d", status); } } if (number != NULL) { env->ReleaseStringUTFChars(number_str, number); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; }
有关于HAL层的打电话实现,有兴趣的朋友可以去看
bt_hf_client.h