Android Bluetooth 蓝牙设备之间自动配对
Android Bluetooth 学习(3)蓝牙设备之间自动配对
0人收藏此文章, 我要收藏发表于6个月前(2013-03-21 12:01) , 已有654次阅读,共0个评论
前言(android2.3版本,4.0版本由于是随机获取pin值,没有研究过):
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、setPairingConfirmation、setRemoteOutOfBandData)一起用,这几个方法都会remove掉map里面的
key:value(也就是互斥的)。
3、蓝牙耳机、手柄等一些无法手动配置的设备是如何完成自动配对的。
在源码里面有一个自动配对的方法,也就是把pin值自动设为“0000”
1 /*package*/ synchronized boolean attemptAutoPair(String address) {
2 if (!mBondState.hasAutoPairingFailed(address) &&
3 !mBondState.isAutoPairingBlacklisted(address)) {
4 mBondState.attempt(address);
5 setPin(address,
BluetoothDevice.convertPinToBytes("0000"));
6 return true;
7 }
8 return false;
9 } 该方法是在底层回调到java 层的onRequestPinCode 方法时被调用,首先 Check if its a dock (正常输入的密钥,走正常配对方式,双方输入匹配值),然后再 try 0000 once if the device looks dumb (涉及到Device.AUDIO_VIDEO 相关部分如:耳机,免提等进入自动匹配模式)进行自动配对。
言归正传,虽然个人觉得自动配对需要双方乃至多方蓝牙设备都需要装上实现自动配对的apk ,已经失去了自动配对的意义,但有可能还是会派上用场。下面我们看看现实情况的自动配对是什么样的吧。
由于BluetoothDevice 配对的方法都是hide 的,所以我们需要通过反射调用被隐藏的方法,现在基本都是通用的工具类型了,网上模式基本一样。
ClsUtils.java
001 package cn.bluetooth;
002
003 import https://www.360docs.net/doc/46848495.html,ng.reflect.Field;
004 import https://www.360docs.net/doc/46848495.html,ng.reflect.Method;
005 import android.bluetooth.BluetoothAdapter;
006 import android.bluetooth.BluetoothDevice;
007 import android.util.Log;
008 public class ClsUtils
009 {
010 public static BluetoothDevice remoteDevice=null;
011 /**
012 * 与设备配对 参考源码:platform/packages/apps/Settings.git
013 * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevic e.java
014 */
015 @SuppressWarnings("unchecked")
016 static public boolean createBond(@SuppressWarnings("rawtypes") Class btClass, BluetoothDevice btDevice)
017 throws Exception
018 {
019 Method createBondMethod = btClass.getMethod("createBond");
020 Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
021 return returnValue.booleanValue();
022 }
023
024 /**
025 * 与设备解除配对 参考源码:platform/packages/apps/Settings.git
026 * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevic e.java
027 */
028 @SuppressWarnings("unchecked")
029 static public boolean removeBond(Class btClass, BluetoothDevice btDevice)
030 throws Exception
031 {
032 Method removeBondMethod = btClass.getMethod("removeBond");
033 Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
034 return returnValue.booleanValue();
035 }
036
037 @SuppressWarnings("unchecked")
038 static public boolean setPin(Class btClass, BluetoothDevice btDevice,
039 String str) throws Exception
040 {
041 try
042 {
043 Method removeBondMethod = btClass.getDeclaredMethod("setPin",
044 new Class[]
045 {byte[].class});
046 Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,
047 new Object[]
048 {str.getBytes()});
049 Log.d("returnValue", "setPin is success " +btDevice.getAddress()+ returnValue.booleanValue());
050 }
051 catch (SecurityException e)
052 {
053 // throw new RuntimeException(e.getMessage()); 054 e.printStackTrace();
055 }
056 catch (IllegalArgumentException e)
057 {
058 // throw new RuntimeException(e.getMessage()); 059 e.printStackTrace();
060 }
061 catch (Exception e)
062 {
063 // TODO Auto-generated catch block
064 e.printStackTrace();
065 }
066 return true;
067
068 }
069
070 // 取消用户输入
071 @SuppressWarnings("unchecked")
072 static public boolean cancelPairingUserInput(Class btClass, 073 BluetoothDevice device)
074
075 throws Exception
076 {
077 Method createBondMethod = btClass.getMethod("cancelPairingUserInput");
078 // cancelBondProcess()
079 Boolean returnValue = (Boolean) createBondMethod.invoke(device);
080 Log.d("returnValue", "cancelPairingUserInput is success
" + returnValue.booleanValue());
081 return returnValue.booleanValue();
082 }
083
084 // 取消配对
085 @SuppressWarnings("unchecked")
086 static public boolean cancelBondProcess(Class btClass, 087 BluetoothDevice device)
088
089 throws Exception
090 {
091 Method createBondMethod = btClass.getMethod("cancelBondProcess");
092 Boolean returnValue = (Boolean) createBondMethod.invoke(device);
093 return returnValue.booleanValue();
094 }
095
096 /**
097 *
098 * @param clsShow
099 */
100 @SuppressWarnings("unchecked")
101 static public void printAllInform(Class clsShow)
102 {
103 try
104 {
105 // 取得所有方法
106 Method[] hideMethod = clsShow.getMethods(); 107 int i = 0;
108 for (; i < hideMethod.length; i++)
109 {
110 //Log.e("method name", hideMethod.getName() + ";and the i is:"
111 // + i);
112 }
113 // 取得所有常量
114 Field[] allFields = clsShow.getFields();
115 for (i = 0; i < allFields.length; i++)
116 {
117 //Log.e("Field name", allFields.getName()); 118 }
119 }
120 catch (SecurityException e)
121 {
122 // throw new RuntimeException(e.getMessage()); 123 e.printStackTrace();
124 }
125 catch (IllegalArgumentException e)
126 {
127 // throw new RuntimeException(e.getMessage()); 128 e.printStackTrace();
129 }
130 catch (Exception e)
131 {
132 // TODO Auto-generated catch block
133 e.printStackTrace();
134 }
135 }
136 }
Bluetooth1.java主activity,所有界面操作实现地方
001 package cn.bluetooth;
002 import java.io.IOException;
003 import java.util.ArrayList;
004 import java.util.List;
005
006 import android.app.Activity;
007 import android.bluetooth.BluetoothAdapter;
008 import android.bluetooth.BluetoothDevice;
009 import android.bluetooth.BluetoothSocket;
010 import android.content.BroadcastReceiver;
011 import android.content.Context;
012 import android.content.Intent;
013 import android.content.IntentFilter;
014 import android.os.Bundle;
015 import android.util.Log;
016 import android.view.Menu;
017 import android.view.View;
018 import android.widget.AdapterView;
019 import android.widget.ArrayAdapter;
020 import android.widget.Button;
021 import android.widget.ListView;
022 import android.widget.Toast;
023 import android.widget.ToggleButton;
024 public class Bluetooth1 extends Activity {
025 /** Called when the activity is first created. */
026 Button btnSearch, btnDis, btnExit;
027 ToggleButton tbtnSwitch;
028 ListView lvBTDevices;
029 ArrayAdapter
030 List
031 BluetoothAdapter btAdapt;
032 public static BluetoothSocket btSocket;
033 @Override
034 public void onCreate(Bundle savedInstanceState) {
035 super.onCreate(savedInstanceState);
036 setContentView(https://www.360docs.net/doc/46848495.html,yout.main);
037 // Button 设置
038 btnSearch = (Button) this.findViewById(R.id.btnSearch); 039 btnSearch.setOnClickListener(new ClickEvent()); 040 btnExit = (Button) this.findViewById(R.id.btnExit); 041 btnExit.setOnClickListener(new ClickEvent());
042 btnDis = (Button) this.findViewById(R.id.btnDis); 043 btnDis.setOnClickListener(new ClickEvent());
044
045 // ToogleButton 设置
046 tbtnSwitch = (ToggleButton) this.findViewById(R.id.tbtnSwitch);
047 tbtnSwitch.setOnClickListener(new ClickEvent()); 048
049 // ListView 及其数据源 适配器
050 lvBTDevices = (ListView) this.findViewById(R.id.lvDevices);
051 adtDevices = new ArrayAdapter
052 https://www.360docs.net/doc/46848495.html,yout.simple_list_item_1, lstDevices); 053
lvBTDevices.setAdapter(adtDevices); 054 lvBTDevices.setOnItemClickListener(new ItemClickEvent());
055
056 btAdapt = BluetoothAdapter.getDefaultAdapter();// 初始化本机蓝牙功能
057
058 // ========================================================
059 // modified by wiley
060 /*
061 * if (btAdapt.getState() == BluetoothAdapter.STATE_OFF)// 读取蓝牙状态并显示
062 * tbtnSwitch.setChecked(false); else if (btAdapt.getState() ==
063 * BluetoothAdapter.STATE_ON) tbtnSwitch.setChecked(true);
064 */
065 if (btAdapt.isEnabled()) {
066 tbtnSwitch.setChecked(false);
067 } else {
068 tbtnSwitch.setChecked(true);
069 }
070 // ============================================================ 071 // 注册Receiver 来获取蓝牙设备相关的结果
072 IntentFilter intent = new IntentFilter();
073 intent.addAction(BluetoothDevice.ACTION_FOUND);// 用BroadcastReceiver 来取得搜索结果
07 intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANG
4 ED);
075 intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANG ED);
076 intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
077 registerReceiver(searchDevices, intent);
078 }
079
080 private final BroadcastReceiver searchDevices = new BroadcastReceiver() {
081 @Override
082 public void onReceive(Context context, Intent intent) { 083
084 String action = intent.getAction();
085 Bundle b = intent.getExtras();
086 Object[] lstName = b.keySet().toArray();
087
088 // 显示所有收到的消息及其细节
089 for (int i = 0; i < lstName.length; i++) {
090 String keyName = lstName.toString();
091 Log.e(keyName, String.valueOf(b.get(keyName))); 092 }
093 BluetoothDevice device = null;
094 // 搜索设备时,取得设备的MAC 地址
095 if (BluetoothDevice.ACTION_FOUND.equals(action)) {
096 device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
097 if (device.getBondState() == BluetoothDevice.BOND_NONE) {
098 String str = " 未配对|" + device.getName() + "|"
099 + device.getAddress();
100 if (lstDevices.indexOf(str) == -1)// 防止重复添加
101 lstDevices.add(str); // 获取设备名称和mac 地址
102 adtDevices.notifyDataSetChanged();
103
} 104 }else if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.eq uals(action)){
105 device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
106
107 switch (device.getBondState()) {
108 case BluetoothDevice.BOND_BONDING:
109 Log.d("BlueToothTestActivity", "正在配对......");
110 break;
111 case BluetoothDevice.BOND_BONDED:
112 Log.d("BlueToothTestActivity", "完成配对"); 113 //connect(device);//连接设备
114 break;
115 case BluetoothDevice.BOND_NONE:
116 Log.d("BlueToothTestActivity", "取消配对"); 117 default:
118 break;
119 }
120 }
121
122 }
123 };
124
125 @Override
126 protected void onDestroy() {
127 this.unregisterReceiver(searchDevices);
128 super.onDestroy();
129 android.os.Process.killProcess(android.os.Process.myPid());
130 }
131
132 class ItemClickEvent implements AdapterView.OnItemClickListe ner {
133
134 @Override
135 public void onItemClick(AdapterView> arg0, View arg1, int arg2,
136 long arg3) {
137 if(btAdapt.isDiscovering())btAdapt.cancelDiscovery();
138
String str = lstDevices.get(arg2); 139 String[] values = str.split("\\|");
140 String address = values[2];
141 Log.e("address", values[2]);
142 BluetoothDevice btDev = btAdapt.getRemoteDevice(address);
143 try {
144 Boolean returnValue = false;
145 if (btDev.getBondState() == BluetoothDevice.BOND_NONE) {
146 Toast.makeText(Bluetooth1.this, "远程设备发送蓝牙配对请求", 5000).show();
147 //这里只需要createBond 就行了
148 ClsUtils.createBond(btDev.getClass(), btDev);
149 }else if(btDev.getBondState() == BluetoothDevice.BOND_BONDED){
150 Toast.makeText(Bluetooth1.this, btDev.getBondState()+" ....正在连接..", 1000).show();
151 }
152 } catch (Exception e) {
153 e.printStackTrace();
154 }
155 }
156 }
157 class ClickEvent implements View.OnClickListener {
158 @Override
159 public void onClick(View v) {
160 if (v == btnSearch)// 搜索蓝牙设备,在BroadcastReceiver 显示结果
161 {
162 if (btAdapt.getState() == BluetoothAdapter.STATE_OFF) {// 如果蓝牙还没开启
163 Toast.makeText(Bluetooth1.this, "请先打开蓝牙", 1000)
164 .show();
165 return;
166 }
167 if (btAdapt.isDiscovering())
168 btAdapt.cancelDiscovery();
169 lstDevices.clear();
170 Object[] lstDevice = btAdapt.getBondedDevices().toArray();
171 for (int i = 0; i < lstDevice.length; i++) {
172 BluetoothDevice device = (BluetoothDevice) lstDevice[i];
173 String str = " 已配对|" + device.getName() + "|"
174 + device.getAddress();
175 lstDevices.add(str); // 获取设备名称和mac 地址
176 adtDevices.notifyDataSetChanged();
177 }
178 setTitle("本机:" + btAdapt.getAddress()); 179 btAdapt.startDiscovery();
180 } else if (v == tbtnSwitch) {// 本机蓝牙启动/关闭 181 if (tbtnSwitch.isChecked() == false)
182 btAdapt.enable();
183
184 else if (tbtnSwitch.isChecked() == true) 185 btAdapt.disable();
186
187 } else if (v == btnDis)// 本机可以被搜索
188 {
189 Intent discoverableIntent = new Intent(
190 BluetoothAdapter.ACTION_REQUEST_DISCOVER ABLE);
19 discoverableIntent.putExtra(
1
19 2 BluetoothAdapter.EXTRA_DISCOVERABLE_DURAT ION, 300);
193 startActivity(discoverableIntent);
194 } else if (v == btnExit) {
195 try {
196 if (btSocket != null)
197 btSocket.close();
198 } catch (IOException e) {
199 e.printStackTrace();
200 }
201 Bluetooth1.this.finish();
202 }
203 }
204 }
205 @Override
206 public boolean onCreateOptionsMenu(Menu menu) {
207 getMenuInflater().inflate(R.menu.main, menu);
208 return true;
209 }
210
211
212 }
PairingRequest.java (重要部分,自动配对主要是这个部分完成,activity只是创建了一个配对请求)
01 package cn.bluetooth;
02
03 import android.bluetooth.BluetoothDevice;
04 import android.content.BroadcastReceiver;
05 import android.content.Context;
06 import android.content.Intent;
07 import android.widget.Toast;
08
09 public class PairingRequest extends BroadcastReceiver {
10 String strPsw = "0000";
11 final String ACTION_PAIRING_REQUEST
= "android.bluetooth.device.action.PAIRING_REQUEST";
12 static BluetoothDevice remoteDevice = null;
13
14 @Override
15 public void onReceive(Context context, Intent intent) {
16 if (intent.getAction().equals(ACTION_PAIRING_REQUEST)) { 17
18 BluetoothDevice device = intent
19 .getParcelableExtra(BluetoothDevice.EXTRA_DEVI CE);
20 if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
21 try {
22 ClsUtils.setPin(device.getClass(), device, strPsw); // 手机和蓝牙采集器配对
23 // ClsUtils.cancelPairingUserInput(device.getClass(),
24 // device); //一般调用不成功,前言里面讲解过了 25 Toast.makeText(context, "配对信息" + device.getName(), 5000)
26 .show();
27 } catch (Exception e) {
28 // TODO Auto-generated catch block
29 Toast.makeText(context, "请求连接错误...", 1000).show();
30 }
31 }
32 // */
33 // pair(device.getAddress(),strPsw);
34 }
35 }
36 }
AndroidManifest.xml 启动activity ,接收广播
01 02 package="cn.bluetooth" 03 android:versionCode="1" 04 android:versionName="1.0" > 05 06 android:minSdkVersion="8" 07 android:targetSdkVersion="15" /> 08 09 11 android:icon="@drawable/ic_launcher" 12 android:label="@string/app_name" 13 android:theme="@style/AppTheme" > 14 15 android:name=".Bluetooth1" 16 android:label="@string/title_activity_bluetooth1" > 17 19 20 21 22 23 25 26 27 28 29
main.xml 布局
01 02 xmlns:tools="https://www.360docs.net/doc/46848495.html,/tools" 03 android:id="@+id/LinearLayout1" 04 android:layout_width="match_parent" 05 android:layout_height="match_parent" 06 android:orientation="vertical" > 07
我觉得想要真正意义上的完成蓝牙设备的自动配对,方法还是有的,需要研究一下setting 部分的Bluetooth模块,以及根据蓝牙源码进行深入了解,期待着关于android bluetooth 深入浅出的文章,大家有什么好的文章,留个言大家一起好好学习学习。