前言(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”

/*package*/ synchronized boolean attemptAutoPair(String address) {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,已经失去了自动配对的意义,但有可能还是会派上用场。下面我们看看现实情况的自动配对是什么样的吧。

由于BluetoothDevice配对的方法都是hide的,所以我们需要通过反射调用被隐藏的方法,现在基本都是通用的工具类型了,网上模式基本一样。
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;/*** 与设备配对 参考源码:platform/packages/apps/Settings.git* /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java*/@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();} /*** 与设备解除配对 参考源码:platform/packages/apps/Settings.git* /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java*/@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 blocke.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();} /**** @param clsShow*/@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 blocke.printStackTrace();}}
}

Bluetooth1.java 主activity,所有界面操作实现地方

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 {/** Called when the activity is first created. */Button btnSearch, btnDis, btnExit;ToggleButton tbtnSwitch;ListView lvBTDevices;ArrayAdapter<String> adtDevices;List<String> lstDevices = new ArrayList<String>();BluetoothAdapter btAdapt;public static BluetoothSocket btSocket;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);// Button 设置btnSearch = (Button) this.findViewById(R.id.btnSearch);btnSearch.setOnClickListener(new ClickEvent());btnExit = (Button) this.findViewById(R.id.btnExit);btnExit.setOnClickListener(new ClickEvent());btnDis = (Button) this.findViewById(R.id.btnDis);btnDis.setOnClickListener(new ClickEvent()); // ToogleButton设置tbtnSwitch = (ToggleButton) this.findViewById(R.id.tbtnSwitch);tbtnSwitch.setOnClickListener(new ClickEvent()); // ListView及其数据源 适配器lvBTDevices = (ListView) this.findViewById(R.id.lvDevices);adtDevices = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1, lstDevices);lvBTDevices.setAdapter(adtDevices);lvBTDevices.setOnItemClickListener(new ItemClickEvent()); btAdapt = BluetoothAdapter.getDefaultAdapter();// 初始化本机蓝牙功能 // ========================================================// modified by wiley/** if (btAdapt.getState() == BluetoothAdapter.STATE_OFF)// 读取蓝牙状态并显示* tbtnSwitch.setChecked(false); else if (btAdapt.getState() ==* BluetoothAdapter.STATE_ON) tbtnSwitch.setChecked(true);*/if (btAdapt.isEnabled()) {tbtnSwitch.setChecked(false);} else {tbtnSwitch.setChecked(true);}// ============================================================// 注册Receiver来获取蓝牙设备相关的结果IntentFilter intent = new IntentFilter();intent.addAction(BluetoothDevice.ACTION_FOUND);// 用BroadcastReceiver来取得搜索结果intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);registerReceiver(searchDevices, intent);} private final BroadcastReceiver searchDevices = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {      String action = intent.getAction();Bundle b = intent.getExtras();Object[] lstName = b.keySet().toArray(); // 显示所有收到的消息及其细节for (int i = 0; i < lstName.length; i++) {String keyName = lstName.toString();Log.e(keyName, String.valueOf(b.get(keyName)));}BluetoothDevice device = null;// 搜索设备时,取得设备的MAC地址if (BluetoothDevice.ACTION_FOUND.equals(action)) {device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);if (device.getBondState() == BluetoothDevice.BOND_NONE) {String str = "           未配对|" + device.getName() + "|"+ device.getAddress();if (lstDevices.indexOf(str) == -1)// 防止重复添加lstDevices.add(str); // 获取设备名称和mac地址adtDevices.notifyDataSetChanged();}}else if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); switch (device.getBondState()) {case BluetoothDevice.BOND_BONDING:Log.d("BlueToothTestActivity", "正在配对......");break;case BluetoothDevice.BOND_BONDED:Log.d("BlueToothTestActivity", "完成配对");//connect(device);//连接设备break;case BluetoothDevice.BOND_NONE:Log.d("BlueToothTestActivity", "取消配对");default:break;}} }}; @Overrideprotected void onDestroy() {this.unregisterReceiver(searchDevices);super.onDestroy();android.os.Process.killProcess(android.os.Process.myPid());} class ItemClickEvent implements AdapterView.OnItemClickListener { @Overridepublic void onItemClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {if(btAdapt.isDiscovering())btAdapt.cancelDiscovery();String str = lstDevices.get(arg2);String[] values = str.split("\\|");String address = values[2];Log.e("address", values[2]);BluetoothDevice btDev = btAdapt.getRemoteDevice(address);try {Boolean returnValue = false;if (btDev.getBondState() == BluetoothDevice.BOND_NONE) {Toast.makeText(Bluetooth1.this, "远程设备发送蓝牙配对请求", 5000).show();//这里只需要createBond就行了ClsUtils.createBond(btDev.getClass(), btDev);}else if(btDev.getBondState() == BluetoothDevice.BOND_BONDED){Toast.makeText(Bluetooth1.this, btDev.getBondState()+" ....正在连接..", 1000).show();}} catch (Exception e) {e.printStackTrace();}}}class ClickEvent implements View.OnClickListener {@Overridepublic void onClick(View v) {if (v == btnSearch)// 搜索蓝牙设备,在BroadcastReceiver显示结果{if (btAdapt.getState() == BluetoothAdapter.STATE_OFF) {// 如果蓝牙还没开启Toast.makeText(Bluetooth1.this, "请先打开蓝牙", 1000).show();return;}if (btAdapt.isDiscovering())btAdapt.cancelDiscovery();lstDevices.clear();Object[] lstDevice = btAdapt.getBondedDevices().toArray();for (int i = 0; i < lstDevice.length; i++) {BluetoothDevice device = (BluetoothDevice) lstDevice[i];String str = "    已配对|" + device.getName() + "|"+ device.getAddress();lstDevices.add(str); // 获取设备名称和mac地址adtDevices.notifyDataSetChanged();}setTitle("本机:" + btAdapt.getAddress());btAdapt.startDiscovery();} else if (v == tbtnSwitch) {// 本机蓝牙启动/关闭if (tbtnSwitch.isChecked() == false)btAdapt.enable(); else if (tbtnSwitch.isChecked() == true)btAdapt.disable(); } else if (v == btnDis)// 本机可以被搜索{Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);startActivity(discoverableIntent);} else if (v == btnExit) {try {if (btSocket != null)btSocket.close();} catch (IOException e) {e.printStackTrace();}Bluetooth1.this.finish();}}}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.main, menu);return true;}}

PairingRequest.java (重要部分,自动配对主要是这个部分完成,activity只是创建了一个配对请求)

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;@Overridepublic 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 blockToast.makeText(context, "请求连接错误...", 1000).show();}}// */// pair(device.getAddress(),strPsw);}}
}

AndroidManifest.xml 启动activity,接收广播

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="cn.bluetooth"android:versionCode="1"android:versionName="1.0" ><uses-sdkandroid:minSdkVersion="8"android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" /><applicationandroid:icon="@drawable/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme" ><activityandroid:name=".Bluetooth1"android:label="@string/title_activity_bluetooth1" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><receiver android:name=".PairingRequest"><intent-filter><action android:name="android.bluetooth.device.action.PAIRING_REQUEST" /></intent-filter></receiver></application>
</manifest>

main.xml 布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/LinearLayout1"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/btnSearch"android:text="btnSearch"
/>
<Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/btnExit"android:text="btnExit"
/>
<Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/btnDis"android:text="btnDis"
/>
<ToggleButton
android:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/tbtnSwitch"android:text="tbtnSwitch"
/>
<ListViewandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:id="@+id/lvDevices"/>
</LinearLayout>

我觉得想要真正意义上的完成蓝牙设备的自动配对,方法还是有的,需要研究一下setting部分的Bluetooth模块,以及根据蓝牙源码进行深入了解,期待着关于android bluetooth深入浅出的文章,大家有什么好的文章,留个言大家一起好好学习学习。

Android蓝牙自动配对和Pin码设置相关推荐

  1. 蓝牙自动配对警惕PIN码漏洞攻击

    蓝牙自动配对警惕PIN码漏洞攻击 配对是蓝牙设备间身份认证的一个过程,只有成功配对的两个设备才能连接并进行数据交互,所以配对是蓝牙操作中必不可少的流程. 在<蓝牙配对协议分析一>和< ...

  2. 【转载】Android蓝牙自动配对Demo

    注:新版本安卓需增加权限 <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION ...

  3. android 取消蓝牙配对框 实现自动配对 输入PIN码

    前言(android2.3版本,4.0版本由于是随机获取pin值,没有研究过):     1.蓝牙设备之间自动配对,需要两个设备都安装进行配对的apk(网上好多自动配对的帖子都没有说明情况)      ...

  4. Android 蓝牙自动匹配PIN码跳过用户交互

    近期项目中需要连接蓝牙设备,起初只是设置蓝牙列表界面让用户点击然后输入默认PIN码,后来改需求了 = = ,要求自动连接指定设备并不需要用户手动输入PIN码,作为Android 小白的我是拒绝的,但是 ...

  5. android 实现ble蓝牙自动配对连接

    蓝牙自动配对,即搜索到其它蓝牙设备之后直接进行配对,不需要弹出配对确认框或者密钥输入框. 本文章用来连接蓝牙设备ai-thinker,如果你要连接其他蓝牙设备,注意修改相关名字以及修改设备初试pin值 ...

  6. Android下的蓝牙自动配对

    转载        源码下载地址 经过最近一段时间得研究,针对网上给出的案例.总结了一个亲测好使的Demo. 说明如下: 1.本Demo用来连接蓝牙设备HC-05,如果你要连接其他蓝牙设备,注意修改相 ...

  7. android蓝牙配对 自动联接,Android系统下蓝牙自动配对连接方法

    Android系统下蓝牙自动配对连接方法 [专利摘要]本发明涉及一种Android系统下蓝牙自动配对连接方法,其包括如下步骤:步骤1.在Android设备端内存储上次进行蓝牙连接蓝牙外设的蓝牙地址,并 ...

  8. 第3章 PIN码设置、加密、存储

    第3章 PIN码设置.加密.存储 1.前提介绍:该密码保存在/data/misc/keystore/user_0/.masterkey,可在adb shell后,ls –al /data/misc/k ...

  9. android 蓝牙自动连接,蓝牙自动连接实现

    实现的主要功能(蓝牙配对成功如何与远程设备一直连接) 1.当蓝牙配对成功连接时,断开远程端设备会自动连接 2.当设备长时间锁屏会导致CachedBluetoothDevice自动清空,如果蓝牙断开就不 ...

  10. Android代码修改SIM的PIN码,中兴U970如何修改SIM卡或UIM卡的PIN码

    中兴U970修改SIM卡或UIM卡的PIN码方法:选择设置>安全>设置SIM/UIM卡锁定>更改SIM/UIM卡PIN. 中兴U970拥有一块4.3英寸的qHD高清屏幕,分辨率也是达 ...

最新文章

  1. 相机与激光雷达融合的3D目标检测方法MVAF-Net
  2. retinaface验证
  3. linux 利用yum源安装mysql5.7
  4. css 中的伪类选择器before 与after
  5. php流程控制语句,php学习之道:php 流程控制语句
  6. 【SqlServer】Sqlserver中的DOS命令操作
  7. libreoffice error while loading shared libraries: libSM.so.6: cannot open shared object
  8. clion 引用dll_用CLion实现本地方法并给java调用
  9. 【Django】毕设学习笔记(六)
  10. mysql 2100,MySQL 实现准实时的表级别DML计数
  11. iOS-按钮单选与多选逻辑处理
  12. eclips调试linux内核,使用Eclipse调试Qemu及Linux Kernel
  13. 权限汇总 uses-permission
  14. cmd用管理员权限连接远程桌面命令
  15. QT将英文星期转为中文星期
  16. plt.text函数用法
  17. ps无缝拼图教程一:无缝拼接花纹图案
  18. html图片水印的代码,简单实用的给图片加水印源代码
  19. vue 背景透明度_vue添加星空背景特效
  20. HDU - 1242

热门文章

  1. 商品详情页面html,div+css+JQuery仿京东商品详情界面
  2. 软件测试入门知识了解
  3. 计算机二级大题知识点汇总,计算机二级office复习知识点「汇总」
  4. PHP8.0尝鲜系列(一):Windows 10安装PHP8.0
  5. GAPS-银行综合前置系统
  6. 搜狗拼音输入发-自定义短语记录
  7. oracle 快速检索表名称及包含的字段名称
  8. EXCEL的去重去除某个字段后全部操作
  9. 安卓手机使用VNET抓包京东wskey
  10. 圆的半径java_计算圆的半径