车载蓝牙开发一

公司用的是android8.1的源码,系统api有改动,改动的地方会稍微标明一下。我是在系统源码上开发的,所以有些类或者api@hide了 在开发工具上会报错,但是可以编译通过。如果是纯应用上层需要利用反射,有一部分功能需要移植代码。

车载蓝牙主要是实现蓝牙电话,蓝牙音乐,同步通讯录。这些功能都是用到蓝牙的配置文件协议。下面简单介绍一下这几个协议。

1.HFP(Hands-free Profile),让蓝牙设备可以控制电话,如接听、挂断、拒接、语音拨号等,拒接、语音拨号要视蓝牙耳机及电话是否支持。

2.A2DP(Advanced Audio Distribution Profile)是蓝牙的音频传输协议,典型应用为蓝牙耳机。A2DP协议的音频数据在ACL Link上传输。

3.AVRCP(Audio/Video Remote Control Profile)作用是支持CT控制TG,具体来说如果手机和一个蓝牙音箱设备连接上了,那么音箱可以控制手机播放/暂停/切歌以及获得手机上播放歌曲的信息,如专辑,歌名,歌手,时长等信息。

4.PBAP(Phone Book Access Profile)访问下载通讯录以及通话记录。

以上是简单介绍这些协议的用途,想具体了解可以分别取查资料,这方面资料很多。

一.打开蓝牙,查找蓝牙设备,连接蓝牙协议。

要使用蓝牙必须先在文件清单加权限


   

检测设备蓝牙是否打开,没有可以自动打开,也可以请求,给用户提示,让用户自己打开。


查找蓝牙之前可以先查看是否有以前绑定过的蓝牙设备

Set devices = mBluetoothAdapter.getBondedDevices();
    	if (devices != null && devices.size() > 0) {
    		for (Iterator it = devices.iterator(); it.hasNext();) {
    			BluetoothDevice device = it.next();
    			if (device != null) {
    			    addBluetoothDevice(device);	
    			}
			}
    	}

mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (!mBluetoothAdapter.isEnabled()) {
   mBluetoothAdapter.enable();
}

查找蓝牙可以先查找低功耗蓝牙,一般查找时间为8-10秒,然后再查找经典蓝牙。两种查找用的api不同,先说一下查找低功耗蓝牙设备。

拿到BluetoothLeScanner:mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();

调用startScan,参数为ScanCallback对象,它里面有各种回调,根据自己的需求实现。

private final ScanCallback scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                BluetoothDevice device = result.getDevice();
                Log.i(TAG, "scan succeed device ==  " +device);
                addBluetoothDevice(device);
            }
        }

        @Override
        public void onBatchScanResults(List results) {
            super.onBatchScanResults(results);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
                for (ScanResult result: results){
                	Log.i(TAG, "scan succeed device ==  " +result.getDevice());
                    addBluetoothDevice(result.getDevice());
                }
            }
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
            Log.i(TAG,"scan fail");
        }
    };

查看经典蓝牙直接使用BluetoothAdapter的startDiscovery方法,注册BluetoothDevice.ACTION_FOUND广播接收搜索到的蓝牙设备。使用intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)可以得到BluetoothDevice。

接着连接设备,连接设备之前需要绑定设备,就是确认双方设备的存在通过相同秘钥,如果需要访问通讯录,需要勾选权限。

可以先判断BluetoothDevice的状态,如果状态为BluetoothDevice.BOND_NONE,调用bluetoothDevice.createBond()进行绑定。

讲连接之前说一下自己踩过的坑,也不能说是坑,自己能力不足所以花了不少时间。开始我以为连接蓝牙需要socket通讯,查google文档说要   两台设备一台充当客户端一台充当服务端,然后用io流传输数据。我一直在纠结,我只能操作车载应用,用户手机安装不了我开发的应用,那这怎么连接呢。后来看到系统setting有连接蓝牙的效果就果断去setting的Bluetooth模块查看连接蓝牙的代码,终于找到了。在frameworks\base\packages\SettingsLib\src\com\android\settingslib\bluetooth中专门有个类存储管理蓝牙设备CachedBluetoothDevice.java,他连接远程蓝牙设备就是

synchronized void connectInt(LocalBluetoothProfile profile) {
        if (!ensurePaired()) {
            return;
        }
        if (profile.connect(mDevice)) {
            if (Utils.D) {
                Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
            }
            return;
        }
        Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
    }

LocalBluetoothProfile是一个接口,各种协议的封装者实现了该接口,例如HfpClientProfile它里面封装的是BluetoothHeadsetClient,就是HFP协议。主要看它的connect方法

   @Override
    public boolean connect(BluetoothDevice device) {
        if (mService == null) return false;
        List srcs = getConnectedDevices();
        if (srcs != null) {
            for (BluetoothDevice src : srcs) {
                if (src.equals(device)) {
                    // Connect to same device, Ignore it
                    Log.d(TAG,"Ignoring Connect");
                    return true;
                }
            }
        }
        return mService.connect(device);
    }

先获取已连接的蓝牙设备,如果蓝牙设备已连接返回true,不在列表中则调用BluetoothHeadsetClient的connect方法。接着看下这个BluetoothHeadsetClient怎么实例化。

mLocalAdapter.getProfileProxy(context, new HfpClientServiceListener(),
                BluetoothProfile.HEADSET_CLIENT);

通过BluetoothAdapter的getProfileProxy方法去请求这个协议对象。

private final class HfpClientServiceListener
            implements BluetoothProfile.ServiceListener {

        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            if (V) Log.d(TAG,"Bluetooth service connected");
            mService = (BluetoothHeadsetClient) proxy;
            // We just bound to the service, so refresh the UI for any connected HFP devices.
            List deviceList = mService.getConnectedDevices();
            while (!deviceList.isEmpty()) {
                BluetoothDevice nextDevice = deviceList.remove(0);
                CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
                // we may add a new device here, but generally this should not happen
                if (device == null) {
                    Log.w(TAG, "HfpClient profile found new device: " + nextDevice);
                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
                }
                device.onProfileStateChanged(
                    HfpClientProfile.this, BluetoothProfile.STATE_CONNECTED);
                device.refresh();
            }
            mIsProfileReady=true;
        }

        @Override
        public void onServiceDisconnected(int profile) {
            if (V) Log.d(TAG,"Bluetooth service disconnected");
            mIsProfileReady=false;
        }
    }

监听返回状态,赋值。其实不仅仅是BluetoothHeadsetClient,BluetoothA2dpSink等等请求方式一模一样,参数不同。看到这个之后恍然大悟,车载蓝牙实现的功能不需要进行socket通信,只需要连接这些协议。

好了,整理一下连接过程,先拿到BluetoothAdapter,调用getProfileProxy方法请求协议,第三个参数为协议的种类,都定义在BluetoothProfile中,例如BluetoothProfile.AVRCP_CONTROLLER(蓝牙控制协议),BluetoothProfile.A2DP_SINK(音频协议)。实现BluetoothProfile.ServiceListener接口,监听返回状态。如果返回成功得到对象,就可以调用connect(BluetoothDevice device)连接。

mAdapter = BluetoothAdapter.getDefaultAdapter();

mAdapter.getProfileProxy(mContext, new ProxyServiceListener(), BluetoothProfile.AVRCP_CONTROLLER);
		mAdapter.getProfileProxy(mContext, new ProxyServiceListener(), BluetoothProfile.A2DP_SINK);

private final class ProxyServiceListener implements BluetoothProfile.ServiceListener {

		@Override
		public void onServiceConnected(int profile, BluetoothProfile proxy) {
			Log.d(TAG,"Bluetooth service connected profile == "+profile);
			if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
				isAvrcpProfileReady = true;
				mAvrcpCt = (BluetoothAvrcpController)proxy;
				Log.d(TAG, "AvrcpController Profile Proxy Connected");
			} 
			if (profile == BluetoothProfile.A2DP_SINK) {
				isA2dpProfileReady = true;
				mA2dpSink = (BluetoothA2dpSink)proxy;
				Log.d(TAG, "BluetoothA2dpSink Profile Proxy Connected");
			}
		}

		@Override
		public void onServiceDisconnected(int profile) {
			if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
				isAvrcpProfileReady = false;
				mAvrcpCt = null;
				Log.d(TAG, "AvrcpController Profile Proxy Disconnected");
			}
			if (profile == BluetoothProfile.A2DP_SINK) {
				isA2dpProfileReady = false;
				mA2dpSink = null;
				Log.d(TAG, "BluetoothA2dpSink Profile Proxy Disconnected");
			}
		}
	}

public boolean connect(BluetoothDevice device) {
		if (null != mA2dpSink) {
			return mA2dpSink.connect(device);
		}
		Log.i(TAG, "mA2dpSink == null");
		return false;
	}

你可能感兴趣的:(系统应用)