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


                  }        


         /*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 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();         


                  }         


                  


                  /**         


                  *          


                  * @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 block         


                  e.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;         


                  @Override        


                  public          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() {           


                  @Override        


                  public          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         ;         


                  }         


                  }         


                  


                  }         


                  };         


                  


                  @Override        


                  protected          void          onDestroy() {         


                  this         .unregisterReceiver(searchDevices);         


                  super         .onDestroy();         


                  android.os.Process.killProcess(android.os.Process.myPid());         


                  }         


                  


                  class          ItemClickEvent          implements          AdapterView.OnItemClickListener {         


                  


                  @Override        


                  public          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 {         


                  @Override        


                  public          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();         


                  }         


                  }         


                  }         


                  @Override        


                  public          boolean          onCreateOptionsMenu(Menu menu) {        


                  getMenuInflater().inflate(R.menu.main, menu);        


                  return          true         ;        


                  }        


                  


                  


         }        

        212


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         ;        


                  


                  @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);        


                  }        


                  }        


         }        


AndroidManifest.xml  启动activity,接收广播 


?



<         manifest          xmlns:android         =         "http://schemas.android.com/apk/res/android"        


                  package         =         "cn.bluetooth"        


                  android:versionCode         =         "1"        


                  android:versionName         =         "1.0"          >        


                  <         uses-sdk        


                  android:minSdkVersion         =         "8"        


                  android:targetSdkVersion         =         "15"          />        


         <         uses-permission          android:name         =         "android.permission.BLUETOOTH_ADMIN"          />        


         <         uses-permission          android:name         =         "android.permission.BLUETOOTH"          />        


                  <         application        


                  android:icon         =         "@drawable/ic_launcher"        


                  android:label         =         "@string/app_name"        


                  android:theme         =         "@style/AppTheme"          >        


                  <         activity        


                  android: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"          >        


                  <         Button        


                  android:layout_width         =         "wrap_content"        


                  android:layout_height         =         "wrap_content"        


                  android:id         =         "@+id/btnSearch"        


                  android:text         =         "btnSearch"        


         />        


         <         Button        


                  android:layout_width         =         "wrap_content"        


                  android:layout_height         =         "wrap_content"        


                  android:id         =         "@+id/btnExit"        


                  android:text         =         "btnExit"        


         />        


         <         Button        


                  android: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"        


         />        


         <         ListView        


                  android:layout_width         =         "fill_parent"        


                  android:layout_height         =         "wrap_content"        


                  android:id         =         "@+id/lvDevices"        


                  


         />        



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