Android Bluetooth Profile通信

随着智能设备的普及,蓝牙开发在手机应用端使用的越来越多。但是目前网上对蓝牙开发的介绍还相当少,刚开始接触蓝牙开发时,都是边网上搜边看源码,花费了不少功夫,总算是对蓝牙开发略知一二了。

蓝牙设备之间建立连接分为三个部分:1.Bond(Pair)、2.Profile、3.Socket

1.Bond,Bond即设备之间绑定(配对),这是蓝牙设备之间通信的基础。当搜索到需要bond的设备时,获取到设备对应的BluetoothDevice对象时,我们就可以进行bond操作了。查看BluetoothDevice源码,在其方法中可以找到一个叫“createBond()”方法,该方法即是蓝牙bond的核心方法,但需要注意的是,该方法在API19之前是@hide的,所以minSdk设置小于API19时,需要通过反射来调用该方法

 /**
     * Start the bonding (pairing) process with the remote device.
     * 

This is an asynchronous call, it will return immediately. Register * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when * the bonding process completes, and its result. *

Android system services will handle the necessary user interactions * to confirm and complete the bonding process. *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return false on immediate error, true if bonding will begin */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean createBond() { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); return false; } try { return sService.createBond(this, TRANSPORT_AUTO); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; }

通过查看源码,不难看出,createBond方法在执行时,调用了IPC,需要了解具体实现过程的,可以去查看“sService ”这个IPC类的源码,这里就不再深入。

2.Profile,Profile是蓝牙的一个很重要特性,Profile定义了设备如何实现一种连接或者应用,你可以把Profile理解为连接层或者应用层协。
关于蓝牙Profile的介绍可以看这里:
下面来介绍Profile的连接:
在android framework层中,Profile同样是封装成了一个个IPC类,在BluetoothAdapter中提供了”getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, int profile)”方法连接IPC实例获取到这些Profile服务的代理来操作这些profile以及”closeProfileProxy(int profile, BluetoothProfile profile)”来关闭这些Profile的代理

/**
     * Get the profile proxy object associated with the profile.
     *
     * 

Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET}, * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, or * {@link BluetoothProfile#GATT_SERVER}. Clients must implement * {@link BluetoothProfile.ServiceListener} to get notified of * the connection status and to get the proxy object. * * @param context Context of the application * @param listener The service Listener for connection callbacks. * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH}, * {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. * {@link BluetoothProfile#GATT} or {@link BluetoothProfile#GATT_SERVER}. * @return true on success, false on error */ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, int profile) { if (context == null || listener == null) return false; if (profile == BluetoothProfile.HEADSET) { BluetoothHeadset headset = new BluetoothHeadset(context, listener); return true; } else if (profile == BluetoothProfile.A2DP) { BluetoothA2dp a2dp = new BluetoothA2dp(context, listener); return true; } else if (profile == BluetoothProfile.A2DP_SINK) { BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener); return true; } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) { BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener); return true; } else if (profile == BluetoothProfile.INPUT_DEVICE) { BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener); return true; } else if (profile == BluetoothProfile.PAN) { BluetoothPan pan = new BluetoothPan(context, listener); return true; } else if (profile == BluetoothProfile.HEALTH) { BluetoothHealth health = new BluetoothHealth(context, listener); return true; } else if (profile == BluetoothProfile.MAP) { BluetoothMap map = new BluetoothMap(context, listener); return true; } else if (profile == BluetoothProfile.HEADSET_CLIENT) { BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener); return true; } else if (profile == BluetoothProfile.SAP) { BluetoothSap sap = new BluetoothSap(context, listener); return true; } else { return false; } }

该方法支持连接HEADSET、A2DP、A2DP_SINK、AVRCP、INPUT_DEVICE、PAN、HEALTH、MAP、HEADSET_CLIENT、SAP等相应的Profile。
参数中,BluetoothProfile.SearchListener是对外用来监听是否成功连接Profile的IPC实例的,与普通AIDL用来监听连接的ServiceConnection类似,当“onServiceConnected(int profile, BluetoothProfile proxy)”被调用时,我们就可以获取到该Profile的IPC实例来做我们想做的事情。

这里已Headset来讲解Profile连接设备的方式,其实上述这些Profile的连接方式都大同小异。在上面方法中,当获取HEADSET Profile时,生成了一个BluetoothHeadset对象,该对象即是Headset IPC的代理,在该类中有"connect(BluetoothDevice device)"、"disconnect(BluetoothDevice device)"方法来连接和断开与设备的Profile链接等。
/**
     * Initiate connection to a profile of the remote bluetooth device.
     *
     * 

Currently, the system supports only 1 connection to the * headset/handsfree profile. The API will automatically disconnect connected * devices before connecting. * *

This API returns false in scenarios like the profile on the * device is already connected or Bluetooth is not turned on. * When this API returns true, it is guaranteed that * connection state intent for the profile will be broadcasted with * the state. Users can get the connection state of the profile * from this intent. * *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * permission. * * @param device Remote Bluetooth Device * @return false on immediate error, * true otherwise * @hide */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.connect(device); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; } /** * Initiate disconnection from a profile * *

This API will return false in scenarios like the profile on the * Bluetooth device is not in connected state etc. When this API returns, * true, it is guaranteed that the connection state change * intent will be broadcasted with the state. Users can get the * disconnection state of the profile from this intent. * *

If the disconnection is initiated by a remote device, the state * will transition from {@link #STATE_CONNECTED} to * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the * host (local) device the state will transition from * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to * state {@link #STATE_DISCONNECTED}. The transition to * {@link #STATE_DISCONNECTING} can be used to distinguish between the * two scenarios. * *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * permission. * * @param device Remote Bluetooth Device * @return false on immediate error, * true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.disconnect(device); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; }

源码中都有非常详细的方法介绍,这里就不再敖诉。

关于连接蓝牙Profile的例子,可以参考这里:

3.Socket,绑定后的蓝牙设备之间是可以建立Socket通信的,这种Socket类似于TCP Socket,但略有不同,该Socket只能通过调用Android API来获取并连接,但通信操作是与TCP相同的,可以获取InputStream以及OutputStream来实现数据的交互。在蓝牙规范中,有个SPP(全称Serial Port Profile) Profile,定义了如何在两台蓝牙设备之间建立虚拟串口并进行连接,顾名思义,就是来支持蓝牙设备之间的Socket通信。该Profile在蓝牙设备绑定之后便会连接,所以我们只需在蓝牙绑定成功后,通过调用相应的API,即可获取BluetoothSocket对象,在该对象中提供了”getInputStream”、”getOutputStream”来获取输入输出流,然后即可通过IO流来传输数据,实现设备通讯。

一般的,我们通过连接该Socket来实现手机控制智能设备的功能,但是Socket也仅仅只能用来数据通讯,要想实现免提电话、听音乐、外接键盘等功能,还是需要去连接对应的Profile。

下面附上自己做的一个小Demo:https://github.com/Joy-Whale/BluetoothProfile

你可能感兴趣的:(android)