Bluetooth结构 ClientThread.java 蓝牙设备之间自动配对 3、蓝牙耳机、手柄等一些无法手动配置的设备是如何完成自动配对的。 synchronized boolean attemptAutoPair(String address) { 由于BluetoothDevice配对的方法都是hide的,所以我们需要通过反射调用被隐藏的方法,现在基本都是通用的工具类型了,网上模式基本一样。 Bluetooth1.java 主activity,所有界面操作实现地方 PairingRequest.java (重要部分,自动配对主要是这个部分完成,activity只是创建了一个配对请求) AndroidManifest.xml 启动activity,接收广播 main.xml 布局
1、JAVA层
frameworks/base/core/java/android/bluetooth/
包含了bluetooth的JAVA类。
2、JNI层
frameworks/base/core/jni/android_bluetooth_开头的文件
定义了bluez通过JNI到上层的接口。
frameworks/base/core/jni/android_server_bluetoothservice.cpp
调用硬件适配层的接口system/bluetooth/bluedroid/bluetooth.c
3、bluez库
external/bluez/
这是bluez用户空间的库,开源的bluetooth代码,包括很多协议,生成libbluetooth.so。
4、硬件适配层
system/bluetooth/bluedroid/bluetooth.c
包含了对硬件操作的接口
system/bluetooth/data
mserverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(Bluetoothprotocol.PROTOCOL_SCHEME_RFCOMM,
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
//
socket = mserverSocket.accept();
//下面代码作者偷懒,读写另外起一个线程最好
//接收数据
byte[] buffer = new byte[1024];
int bytes;
InputStream mmInStream = null;
try {
mmInStream = socket.getInputStream();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println("zhoulc server");
while(true){
if( (bytes = mmInStream.read(buffer)) > 0 )
{
byte[] buf_data = new byte[bytes];
for(int i=0; i
package com.example.thecaseforbluetooth;
import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.widget.Toast;
public class ClientThread extends Thread {
public BluetoothSocket socket;
public BluetoothDevice device;
public Context context;
public ClientThread(BluetoothDevice device,Context context){
this.device = device;
this.context = context;
}
public void run() {
try {
//创建一个Socket连接:只需要服务器在注册时的UUID号
socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
//连接
socket.connect();
//下面代码作者偷懒,读写另外起一个线程最好
//发送数据
if (socket == null)
{
Toast.makeText(context, "链接失败", 5000).show();
return;
}
System.out.println("zhoulc client");
while(true){
try {
System.out.println("zhoulc client is in");
String msg = "hello everybody I am client";
OutputStream os = socket.getOutputStream();
os.write(msg.getBytes());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
1、蓝牙设备之间自动配对,需要两个设备都安装进行配对的apk(网上好多自动配对的帖子都没有说明情况)
2、在自动匹配的时候想通过反射调用BluetoothDevice的setPin、createBond、cancelPairingUserInput实现设置密钥、配对请求创建、取消密钥信息输入等。
1)createBond()创建,最终会调到源码的BluetoothService的createBond(String address)方法,通过对源码浅显的了解,createBond主要是写入匹配密钥(BluetoothService的writeDockPin())以及进入jni注册回调函数onCreatePairedDeviceResult观察匹配结果
比如: // Pins did not match, or remote device did not respond to pin
// request in time
// We rejected pairing, or the remote side rejected pairing. This
// happens if either side presses 'cancel' at the pairing dialog.
// Not sure if this happens
// Other device is not responding at all
// already bonded
等,在jni中创建了进行匹配的device("CreatePairedDevice"),这时bluetooth会发送一个ACTION_PAIRING_REQUEST的广播,只有当前会出现密钥框的蓝牙设备收到。写完密钥之后,发送广播给另外一个蓝牙设备接收,然后打开密钥输入框进行匹配。
2)setPin()设置密钥,通过查看setting源码,发现在确认输入密钥之后会调用setPin()(如果点取消,就会调用cancelPairingUserInput,取消密钥框),setPin具体通过D-BUS做了什么没有去深究,但是在调用setPin的时候会remove掉一个map里面的键值对(address:int),也就是我们在调用setPin之后如果再去调用onCreatePairedDeviceResult,则该方法一定返回false,并且出现下面的打印提示:cancelUserInputNative(B8:FF:FE:55:EF:D6) called but no native data available, ignoring. Maybe the PasskeyAgent Request was already cancelled by the remote or by bluez.(因为该方法也会remove掉一个键值对)
3)cancelPairingUserInput()取消用户输入密钥框,个人觉得一般情况下不要和setPin(setPasskey、setPairing/confirm/iation、setRemoteOutOfBandData)一起用,这几个方法都会remove掉map里面的key:value(也就是互斥的)。
在源码里面有一个自动配对的方法,也就是把pin值自动设为“0000”
if (!mBondState.hasAutoPairingFailed(address) &&
!mBondState.isAutoPairingBlacklisted(address)) {
mBondState.attempt(address);
setPin(address, BluetoothDevice.convertPinToBytes("0000"));
return true;
}
return false;
}
该方法是在底层回调到java层的onRequestPinCode方法时被调用,首先 Check if its a dock(正常输入的密钥,走正常配对方式,双方输入匹配值),然后再 try 0000 once if the device looks dumb(涉及到Device.AUDIO_VIDEO相关部分如:耳机,免提等进入自动匹配模式)进行自动配对。
言归正传,虽然个人觉得自动配对需要双方乃至多方蓝牙设备都需要装上实现自动配对的apk,已经失去了自动配对的意义,但有可能还是会派上用场。下面我们看看现实情况的自动配对是什么样的吧。
ClsUtils.java
package cn.bluetooth;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.util.Log;
public class ClsUtils
{
public static BluetoothDevice remoteDevice=null;
@SuppressWarnings("unchecked")
static public boolean createBond(@SuppressWarnings("rawtypes") Class btClass, BluetoothDevice btDevice)
throws Exception
{
Method createBondMethod = btClass.getMethod("createBond");
Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
return returnValue.booleanValue();
}
@SuppressWarnings("unchecked")
static public boolean removeBond(Class btClass, BluetoothDevice btDevice)
throws Exception
{
Method removeBondMethod = btClass.getMethod("removeBond");
Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
return returnValue.booleanValue();
}
@SuppressWarnings("unchecked")
static public boolean setPin(Class btClass, BluetoothDevice btDevice,
String str) throws Exception
{
try
{
Method removeBondMethod = btClass.getDeclaredMethod("setPin",
new Class[]
{byte[].class});
Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,
new Object[]
{str.getBytes()});
Log.d("returnValue", "setPin is success " +btDevice.getAddress()+ returnValue.booleanValue());
}
catch (SecurityException e)
{
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
// 取消用户输入
@SuppressWarnings("unchecked")
static public boolean cancelPairingUserInput(Class btClass,
BluetoothDevice device)
throws Exception
{
Method createBondMethod = btClass.getMethod("cancelPairingUserInput");
// cancelBondProcess()
Boolean returnValue = (Boolean) createBondMethod.invoke(device);
Log.d("returnValue", "cancelPairingUserInput is success " + returnValue.booleanValue());
return returnValue.booleanValue();
}
// 取消配对
@SuppressWarnings("unchecked")
static public boolean cancelBondProcess(Class btClass,
BluetoothDevice device)
throws Exception
{
Method createBondMethod = btClass.getMethod("cancelBondProcess");
Boolean returnValue = (Boolean) createBondMethod.invoke(device);
return returnValue.booleanValue();
}
@SuppressWarnings("unchecked")
static public void printAllInform(Class clsShow)
{
try
{
// 取得所有方法
Method[] hideMethod = clsShow.getMethods();
int i = 0;
for (; i < hideMethod.length; i++)
{
//Log.e("method name", hideMethod.getName() + ";and the i is:"
// + i);
}
// 取得所有常量
Field[] allFields = clsShow.getFields();
for (i = 0; i < allFields.length; i++)
{
//Log.e("Field name", allFields.getName());
}
}
catch (SecurityException e)
{
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package cn.bluetooth;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.ToggleButton;
public class Bluetooth1 extends Activity {
Button btnSearch, btnDis, btnExit;
ToggleButton tbtnSwitch;
ListView lvBTDevices;
ArrayAdapter
package cn.bluetooth;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class PairingRequest extends BroadcastReceiver {
String strPsw = "0000";
final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST";
static BluetoothDevice remoteDevice = null;
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_PAIRING_REQUEST)) {
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
try {
ClsUtils.setPin(device.getClass(), device, strPsw); // 手机和蓝牙采集器配对
// ClsUtils.cancelPairingUserInput(device.getClass(),
// device); //一般调用不成功,前言里面讲解过了
Toast.makeText(context, "配对信息" + device.getName(), 5000)
.show();
} catch (Exception e) {
// TODO Auto-generated catch block
Toast.makeText(context, "请求连接错误...", 1000).show();
}
}
// */
// pair(device.getAddress(),strPsw);
}
}
}



