Android藍牙配對
來自: http://blog.csdn.net//guijiaoba/article/details/41172661
上一篇博客介紹了Android ble的一些情況。
http://blog.csdn.net/guijiaoba/article/details/41172403
藍牙如果鏈接好,能夠讀寫,基本上完成了。藍牙還有個比較煩人的東西,就是藍牙配對。
Android ble4.0使用的配對方式和原先版本的配對方式不一樣。
看了網上很多地方都是hellogv大神的方法,這個真心不錯。
http://blog.csdn.net/hellogv/article/details/6042091
本著認真的態度,我把Android系統中Settings的源碼給研究了下,發現了配對的流程,Settings的源碼大小大概有300m,考慮的攻擊
Ble4.0的配對界面有2個部分,首先的是一個請求的配對的通知,點擊了是一個確認對話框,再點擊,然后又有個通知,點擊通知后,有個輸入密碼的的對話框,輸入密碼后,點確定,稍后就會有配對的結果。
下面是Settings中的源碼
Settings的目錄是/package/app/Settings,下面是Setting的源碼url,感興趣的同學可以去研究下
http://git.omapzoom.org/ 所有的源碼網址
http://git.omapzoom.org/?p=platform/packages/apps/Settings.git;a=summary Settings的網址
git://git.omapzoom.org/platform/packages/apps/Settings.git Settings的git地址
Settings中的藍牙代碼都在/Settings/src/com/android/settings/bluetooth目錄中
主要流程看下面的文件
/Settings/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
代碼中很多的類,在實際的Android應該開發中是沒有的,我這邊準備了三個jar文件,分別是framework.jar,core.jar,ext.jar,這些事Android系統編譯的時候,生產的jar。
導入到elcipse中,關聯此jar就可以。關聯的jar時放在libs中,但是有個必須注意的地方,點擊項目的屬性,在Java BuildPath類別,Order and Export標簽,Android Dependenceis必須要比Android 5.0的在上面,這樣在引用的時候,就會優先引用Android Dependceis中的jar。


下面是Setting中的BluetoothPairingDialog.java,簡單分析下,中文部分是我添加的注釋。
package com.android.settings.bluetooth;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.text.Editable;
import android.text.Html;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.InputFilter.LengthFilter;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.TextView;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
import com.android.settings.R;
import android.view.KeyEvent;
import java.util.Locale;
/**
* BluetoothPairingDialog asks the user to enter a PIN / Passkey / simple
* confirmation for pairing with a remote Bluetooth device. It is an activity
* that appears as a dialog.
*/
// BluetoothPairingDialog 說明這個節目是一個對話框
public final class BluetoothPairingDialog extends AlertActivity implements CompoundButton.OnCheckedChangeListener, DialogInterface.OnClickListener, TextWatcher {
private static final String TAG = "BluetoothPairingDialog";
private static final int BLUETOOTH_PIN_MAX_LENGTH = 16;
private static final int BLUETOOTH_PASSKEY_MAX_LENGTH = 6;
private BluetoothDevice mDevice;
private int mType;
private String mPairingKey;
private EditText mPairingView;
private Button mOkButton;
/**
* Dismiss the dialog if the bond state changes to bonded or none, or if
* pairing was canceled for {@link #mDevice}.
*/
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
// 當設備的配對狀態改變的時候,就把對話框去掉
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
if (bondState == BluetoothDevice.BOND_BONDED || bondState == BluetoothDevice.BOND_NONE) {
dismiss();
}
} else if (BluetoothDevice.ACTION_PAIRING_CANCEL.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device == null || device.equals(mDevice)) {
dismiss();
}
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 必須是ACTION_PAIRING_REQUEST,否則不運行,ACTION_PAIRING_REQUEST是一個請求的配對的廣播
Intent intent = getIntent();
if (!intent.getAction().equals(BluetoothDevice.ACTION_PAIRING_REQUEST是一個請求的配對的廣播)) {
Log.e(TAG, "Error: this activity may be started only with intent " + BluetoothDevice.ACTION_PAIRING_REQUEST);
finish();
return;
}
LocalBluetoothManager manager = LocalBluetoothManager.getInstance(this);
if (manager == null) {
Log.e(TAG, "Error: BluetoothAdapter not supported by system");
finish();
return;
}
CachedBluetoothDeviceManager deviceManager = manager.getCachedDeviceManager();
// 獲取配對的設備和配對類型,我的項目中配對類型是BluetoothDevice.PAIRING_VARIANT_PIN
mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
mType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
// 根據配對的類型,進行分發,我們重點關注PAIRING_VARIANT_PIN
switch (mType) {
case BluetoothDevice.PAIRING_VARIANT_PIN:
case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
// 創建用戶輸入配對密碼的對話框
createUserEntryDialog(deviceManager);
break;
case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
int passkey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
if (passkey == BluetoothDevice.ERROR) {
Log.e(TAG, "Invalid Confirmation Passkey received, not showing any dialog");
return;
}
mPairingKey = String.format(Locale.US, "%06d", passkey);
createConfirmationDialog(deviceManager);
break;
case BluetoothDevice.PAIRING_VARIANT_CONSENT:
case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
createConsentDialog(deviceManager);
break;
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
int pairingKey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
if (pairingKey == BluetoothDevice.ERROR) {
Log.e(TAG, "Invalid Confirmation Passkey or PIN received, not showing any dialog");
return;
}
if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
mPairingKey = String.format("%06d", pairingKey);
} else {
mPairingKey = String.format("%04d", pairingKey);
}
createDisplayPasskeyOrPinDialog(deviceManager);
break;
default:
Log.e(TAG, "Incorrect pairing type received, not showing any dialog");
}
/*
* Leave this registered through pause/resume since we still want to
* finish the activity in the background if pairing is canceled.
*/
registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_PAIRING_CANCEL));
registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
}
// 創建用戶輸入配對密碼的對話框
private void createUserEntryDialog(CachedBluetoothDeviceManager deviceManager) {
// 創建對話框的界面,
// 在看看對話框的確定的回調函數,由于本類是實現DialogInterface.OnClickListener的方法的,我們可以去看下
// public void onClick(DialogInterface dialog, int which)
final AlertController.AlertParams p = mAlertParams;
p.mIconId = android.R.drawable.ic_dialog_info;
p.mTitle = getString(R.string.bluetooth_pairing_request);
p.mView = createPinEntryView(deviceManager.getName(mDevice));
p.mPositiveButtonText = getString(android.R.string.ok);
p.mPositiveButtonListener = this;
p.mNegativeButtonText = getString(android.R.string.cancel);
p.mNegativeButtonListener = this;
setupAlert();
mOkButton = mAlert.getButton(BUTTON_POSITIVE);
mOkButton.setEnabled(false);
}
private View createPinEntryView(String deviceName) {
View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_entry, null);
TextView messageView = (TextView) view.findViewById(R.id.message);
TextView messageView2 = (TextView) view.findViewById(R.id.message_below_pin);
CheckBox alphanumericPin = (CheckBox) view.findViewById(R.id.alphanumeric_pin);
mPairingView = (EditText) view.findViewById(R.id.text);
mPairingView.addTextChangedListener(this);
alphanumericPin.setOnCheckedChangeListener(this);
int messageId1;
int messageId2;
int maxLength;
switch (mType) {
case BluetoothDevice.PAIRING_VARIANT_PIN:
messageId1 = R.string.bluetooth_enter_pin_msg;
messageId2 = R.string.bluetooth_enter_pin_other_device;
// Maximum of 16 characters in a PIN
maxLength = BLUETOOTH_PIN_MAX_LENGTH;
break;
case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
messageId1 = R.string.bluetooth_enter_passkey_msg;
messageId2 = R.string.bluetooth_enter_passkey_other_device;
// Maximum of 6 digits for passkey
maxLength = BLUETOOTH_PASSKEY_MAX_LENGTH;
alphanumericPin.setVisibility(View.GONE);
break;
default:
Log.e(TAG, "Incorrect pairing type for createPinEntryView: " + mType);
return null;
}
// HTML escape deviceName, Format the message string, then parse HTML style tags
String messageText = getString(messageId1, Html.escapeHtml(deviceName));
messageView.setText(Html.fromHtml(messageText));
messageView2.setText(messageId2);
mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER);
mPairingView.setFilters(new InputFilter[] { new LengthFilter(maxLength) });
return view;
}
private View createView(CachedBluetoothDeviceManager deviceManager) {
View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_confirm, null);
// Escape device name to avoid HTML injection.
String name = Html.escapeHtml(deviceManager.getName(mDevice));
TextView messageView = (TextView) view.findViewById(R.id.message);
String messageText; // formatted string containing HTML style tags
switch (mType) {
case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
messageText = getString(R.string.bluetooth_confirm_passkey_msg, name, mPairingKey);
break;
case BluetoothDevice.PAIRING_VARIANT_CONSENT:
case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
messageText = getString(R.string.bluetooth_incoming_pairing_msg, name);
break;
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
messageText = getString(R.string.bluetooth_display_passkey_pin_msg, name, mPairingKey);
break;
default:
Log.e(TAG, "Incorrect pairing type received, not creating view");
return null;
}
messageView.setText(Html.fromHtml(messageText));
return view;
}
private void createConfirmationDialog(CachedBluetoothDeviceManager deviceManager) {
final AlertController.AlertParams p = mAlertParams;
p.mIconId = android.R.drawable.ic_dialog_info;
p.mTitle = getString(R.string.bluetooth_pairing_request);
p.mView = createView(deviceManager);
p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
p.mPositiveButtonListener = this;
p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
p.mNegativeButtonListener = this;
setupAlert();
}
private void createConsentDialog(CachedBluetoothDeviceManager deviceManager) {
final AlertController.AlertParams p = mAlertParams;
p.mIconId = android.R.drawable.ic_dialog_info;
p.mTitle = getString(R.string.bluetooth_pairing_request);
p.mView = createView(deviceManager);
p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
p.mPositiveButtonListener = this;
p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
p.mNegativeButtonListener = this;
setupAlert();
}
private void createDisplayPasskeyOrPinDialog(CachedBluetoothDeviceManager deviceManager) {
final AlertController.AlertParams p = mAlertParams;
p.mIconId = android.R.drawable.ic_dialog_info;
p.mTitle = getString(R.string.bluetooth_pairing_request);
p.mView = createView(deviceManager);
p.mNegativeButtonText = getString(android.R.string.cancel);
p.mNegativeButtonListener = this;
setupAlert();
// Since its only a notification, send an OK to the framework,
// indicating that the dialog has been displayed.
if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
mDevice.setPairingConfirmation(true);
} else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) {
byte[] pinBytes = BluetoothDevice.convertPinToBytes(mPairingKey);
mDevice.setPin(pinBytes);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
}
public void afterTextChanged(Editable s) {
if (mOkButton != null) {
mOkButton.setEnabled(s.length() > 0);
}
}
// 進行配對
private void onPair(String value) {
// 根據類型進行分發
switch (mType) {
case BluetoothDevice.PAIRING_VARIANT_PIN:
// 注意這里是用了轉換的方法,不是直接調用value.getBytes();方法
byte[] pinBytes = BluetoothDevice.convertPinToBytes(value);
if (pinBytes == null) {
return;
}
// 直接調用setPin方法,然后就沒有了,等到收到狀態改變的廣播后就進行dismiss,請看54行的mReceiver
mDevice.setPin(pinBytes);
break;
case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
int passkey = Integer.parseInt(value);
mDevice.setPasskey(passkey);
break;
case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
case BluetoothDevice.PAIRING_VARIANT_CONSENT:
mDevice.setPairingConfirmation(true);
break;
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
// Do nothing.
break;
case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
mDevice.setRemoteOutOfBandData();
break;
default:
Log.e(TAG, "Incorrect pairing type received");
}
}
private void onCancel() {
mDevice.cancelPairingUserInput();
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
onCancel();
}
return super.onKeyDown(keyCode, event);
}
// 對話框的確定的回調
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case BUTTON_POSITIVE:
// 進行配對
if (mPairingView != null) {
onPair(mPairingView.getText().toString());
} else {
onPair(null);
}
break;
case BUTTON_NEGATIVE:
default:
onCancel();
break;
}
}
/* Not used */
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
/* Not used */
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// change input type for soft keyboard to numeric or alphanumeric
if (isChecked) {
mPairingView.setInputType(InputType.TYPE_CLASS_TEXT);
} else {
mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER);
}
}
} 1、對話框的創建
當收到請求配對的廣播后,此對話框會顯示。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 必須是ACTION_PAIRING_REQUEST,否則不運行,ACTION_PAIRING_REQUEST是一個請求的配對的廣播
Intent intent = getIntent();
if (!intent.getAction().equals(BluetoothDevice.ACTION_PAIRING_REQUEST是一個請求的配對的廣播)) {
Log.e(TAG, "Error: this activity may be started only with intent " + BluetoothDevice.ACTION_PAIRING_REQUEST);
finish();
return;
}
LocalBluetoothManager manager = LocalBluetoothManager.getInstance(this);
if (manager == null) {
Log.e(TAG, "Error: BluetoothAdapter not supported by system");
finish();
return;
}
CachedBluetoothDeviceManager deviceManager = manager.getCachedDeviceManager();
// 獲取配對的設備和配對類型,我的項目中配對類型是BluetoothDevice.PAIRING_VARIANT_PIN
mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
mType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
// 根據配對的類型,進行分發,我們重點關注PAIRING_VARIANT_PIN
switch (mType) {
case BluetoothDevice.PAIRING_VARIANT_PIN:
case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
// 創建用戶輸入配對密碼的對話框
createUserEntryDialog(deviceManager);
break;
case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
int passkey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
if (passkey == BluetoothDevice.ERROR) {
Log.e(TAG, "Invalid Confirmation Passkey received, not showing any dialog");
return;
}
mPairingKey = String.format(Locale.US, "%06d", passkey);
createConfirmationDialog(deviceManager);
break;
case BluetoothDevice.PAIRING_VARIANT_CONSENT:
case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
createConsentDialog(deviceManager);
break;
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
int pairingKey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
if (pairingKey == BluetoothDevice.ERROR) {
Log.e(TAG, "Invalid Confirmation Passkey or PIN received, not showing any dialog");
return;
}
if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
mPairingKey = String.format("%06d", pairingKey);
} else {
mPairingKey = String.format("%04d", pairingKey);
}
createDisplayPasskeyOrPinDialog(deviceManager);
break;
default:
Log.e(TAG, "Incorrect pairing type received, not showing any dialog");
}
/*
* Leave this registered through pause/resume since we still want to
* finish the activity in the background if pairing is canceled.
*/
// 注冊廣播
registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_PAIRING_CANCEL));
registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
} 2、創建有輸入框的界面
// 創建用戶輸入配對密碼的對話框
private void createUserEntryDialog(CachedBluetoothDeviceManager deviceManager) {
// 創建對話框的界面,
// 在看看對話框的確定的回調函數,由于本類是實現DialogInterface.OnClickListener的方法的,我們可以去看下
// public void onClick(DialogInterface dialog, int which)
final AlertController.AlertParams p = mAlertParams;
p.mIconId = android.R.drawable.ic_dialog_info;
p.mTitle = getString(R.string.bluetooth_pairing_request);
p.mView = createPinEntryView(deviceManager.getName(mDevice));
p.mPositiveButtonText = getString(android.R.string.ok);
p.mPositiveButtonListener = this;
p.mNegativeButtonText = getString(android.R.string.cancel);
p.mNegativeButtonListener = this;
setupAlert();
mOkButton = mAlert.getButton(BUTTON_POSITIVE);
mOkButton.setEnabled(false);
} 3、查看輸入框的確定回調函數
// 對話框的確定的回調
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case BUTTON_POSITIVE:
// 進行配對
if (mPairingView != null) {
onPair(mPairingView.getText().toString());
} else {
onPair(null);
}
break;
case BUTTON_NEGATIVE:
default:
onCancel();
break;
}
} 4、配對
// 進行配對
private void onPair(String value) {
// 根據類型進行分發
switch (mType) {
case BluetoothDevice.PAIRING_VARIANT_PIN:
// 注意這里是用了轉換的方法,不是直接調用value.getBytes();方法
byte[] pinBytes = BluetoothDevice.convertPinToBytes(value);
if (pinBytes == null) {
return;
}
// 直接調用setPin方法,然后就沒有了,等到收到狀態改變的廣播后就進行dismiss,請看54行的mReceiver
mDevice.setPin(pinBytes);
break;
case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
int passkey = Integer.parseInt(value);
mDevice.setPasskey(passkey);
break;
case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
case BluetoothDevice.PAIRING_VARIANT_CONSENT:
mDevice.setPairingConfirmation(true);
break;
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
// Do nothing.
break;
case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
mDevice.setRemoteOutOfBandData();
break;
default:
Log.e(TAG, "Incorrect pairing type received");
}
}
// 注意這里是用了轉換的方法,不是直接調用value.getBytes();方法
byte[] pinBytes = BluetoothDevice.convertPinToBytes(value);
下面是系統的中的配對密碼轉換
/**
* Check that a pin is valid and convert to byte array.
*
* Bluetooth pin's are 1 to 16 bytes of UTF-8 characters.
* @param pin pin as java String
* @return the pin code as a UTF-8 byte array, or null if it is an invalid
* Bluetooth pin.
* @hide
*/
public static byte[] convertPinToBytes(String pin) {
if (pin == null) {
return null;
}
byte[] pinBytes;
try {
pinBytes = pin.getBytes("UTF-8");
} catch (UnsupportedEncodingException uee) {
Log.e(TAG, "UTF-8 not supported?!?"); // this should not happen
return null;
}
if (pinBytes.length <= 0 || pinBytes.length > 16) {
return null;
}
return pinBytes;
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
// 當設備的配對狀態改變的時候,就把對話框去掉
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
if (bondState == BluetoothDevice.BOND_BONDED || bondState == BluetoothDevice.BOND_NONE) {
dismiss();
}
} else if (BluetoothDevice.ACTION_PAIRING_CANCEL.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device == null || device.equals(mDevice)) {
dismiss();
}
}
}
}; 以上就是系統的設置的配對流程。
下面是自己的項目中的業務代碼
BroadcastReceiver bleReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
McLog.mByStackTrace();
String action = intent.getAction();
McLog.i("action = " + action);
if (BluetoothDevice.ACTION_FOUND.equals(action)) {// 搜索設備
doDeviceSearch(intent);
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { //搜索結束
doDeviceSearchFinished(intent);
} else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { // 設備狀態改變
doStateChange(intent);
} else if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)) {// 請求匹配
doRequestPari(intent);
}
}
void doStateChange(Intent intent) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
switch (device.getBondState()) {
case BluetoothDevice.BOND_NONE:
McLog.i("取消配對");
break;
case BluetoothDevice.BOND_BONDING:
McLog.i("正在配對......");
break;
case BluetoothDevice.BOND_BONDED:
McLog.i("完成配對");
break;
}
}
void doDeviceSearch(Intent intent) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
McLog.i("device = " + device);
if (device.getBondState() == BluetoothDevice.BOND_BONDED) {// 已經配對的則跳過
} else {
if (!searchDevices.contains(searchDevices)) {
searchDevices.add(device);
}
}
}
void doDeviceSearchFinished(Intent intent) {
McLog.i("search finished, searchDevices's size = " + searchDevices.size());
boolean isFindRightDevice = false;
for (BluetoothDevice device : searchDevices) {
if (isRightDevice(device)) { // 如果是我想要的設備,那么我就進行配對
doNormalPair(device);
isFindRightDevice = true;
break;
}
}
if (!isFindRightDevice) {
McLog.i("sorry,do't search user's device[" + bleDeviceMac + "]");
}
}
void doNormalPair(BluetoothDevice device) {
McLog.mByStackTrace();
currentPairDevice = device;
try {
// 調用配對的方法,此方法是異步的,系統會觸發BluetoothDevice.ACTION_PAIRING_REQUEST的廣播
// 收到此廣播后,設置配對的密碼
ClsUtils.createBond(BluetoothDevice.class, currentPairDevice);
} catch (Exception e) {
e.printStackTrace();
}
}
void doRequestPari(Intent intent) {
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (btDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
McLog.e("已經綁定");
} else {
McLog.e("沒有綁定");
if (currentPairDevice != null && btDevice.getAddress().equalsIgnoreCase(currentPairDevice.getAddress())) {
try {
McLog.i("invoke setpin :" + bleDeviceMac);
// 設置配對的密碼 <span style="font-family: Arial, Helvetica, sans-serif;">bleDevicePasswd 設備的配對密碼</span>
ClsUtils.setPin2(BluetoothDevice.class, currentPairDevice, bleDevicePasswd);
} catch (Exception e) {
McLog.i("e = " + e);
}
currentPairDevice = null;
uiHandler.postDelayed(new Runnable() {
public void run() {
loadBleData();
// 注銷監聽
unregisterReceiver(bleReceiver);
}
}, 1000);
}
}
}
};
以上就能完成ble4.0的密碼自動配對,當然中間的對話框會顯示1s左右時間,然后會再次消失。