浅谈android蓝牙电话API调用及相关代码

    前段时间做车机蓝牙电话的项目,接触到了部分关于蓝牙电话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,
    
    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;
    }
我们发现,实现打电话功能函数是dial,而真正实现打电话的是mService.dial(device, number); 这个mService的定义
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, 绑定的服务是个内部类BluetoothHeadsetClientBinder 
它调用了
    boolean 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







 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值