最近工作中遇到一个蓝牙自动完成配对的问题,感觉比较好玩,尝试了一下,成功了,简单说一下。
原理
通过反射调用BluetoothDevice的相关方法,实现避免手动输入pin(配对码).
限制
所谓Android 蓝牙自动配对,并不能在所有场景实现,说一下限制:
1、Android8.0以上版本手机,由于Google对反射做了限制,无法实现;
2、厂商限制,华为手机,华为应该对蓝牙部分做了特殊处理,该方法在华为设备上无效;
低版本可以自己玩一玩,高版本基本没戏了,除非root。
Talk is cheep show the code:
代码如下:
<application
android:name=".MyApplication"
android:icon="@mipmap/ic_logo"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_logo"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<receiver android:name=".ble.receiver.BleAutoPairReceiver" >
<intent-filter android:priority="1000">
<action android:name="android.bluetooth.device.action.PAIRING_REQUEST"/>
<action android:name="android.bluetooth.device.action.FOUND" />
</intent-filter>
</receiver>
</application>
public class BleAutoPairReceiver extends BroadcastReceiver {
/**
* 目标蓝牙设备mac
*/
private static String sAimedBluetoothDeviceMac = "";
/**
* 配对所用的配对码
*/
private static String sCurrentPin = "";
public BleAutoPairReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
LogUtil.i("ble_action=" + action);
BluetoothDevice bluetoothDevice = null;
// 从Intent中获取设备对象
bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (isAimedDevice(bluetoothDevice)) {
LogUtil.i("aimed_ble_device:" +
"[" + bluetoothDevice.getName() + "]"
+ ":" + bluetoothDevice.getAddress());
if (!BleFactoryTool.INSTANCE.isAutoPair()) {
LogUtil.i("ble_auto_pair_not_enabled,return");
return;
}
LogUtil.i("ble_auto_pair_enabled__try_auto_pair");
switch (action) {
case BluetoothDevice.ACTION_FOUND:
//发现设备
if (bluetoothDevice.getBondState() == BluetoothDevice.BOND_NONE) {
boolean isCreateBondSuccess = BleAutoPairHelper.createBond(bluetoothDevice);
LogUtil.i("try to bond:isCreateBondSuccess=" + isCreateBondSuccess);
}
break;
case BluetoothDevice.ACTION_PAIRING_REQUEST:
// 设备处于待配对状态
//1.确认配对
boolean isSetPairingConfirmationSuccess =
BleAutoPairHelper.setPairingConfirmation(bluetoothDevice, true);
LogUtil.i("isSetPairingConfirmationSuccess=" + isSetPairingConfirmationSuccess);
//2.调用setPin方法进行配对...
boolean isSetPinSuccess =
BleAutoPairHelper.setPin(bluetoothDevice, getPin());
LogUtil.i("isSetPinSuccess=" + isSetPinSuccess);
if (isSetPinSuccess) {
//3.终止有序广播
//如果没有将广播终止,则会出现一个一闪而过的配对框。
LogUtil.i("isSetPairingConfirmationSuccess=" + isSetPairingConfirmationSuccess);
abortBroadcast();
}
break;
default:
LogUtil.e("unsupported action=" + action);
break;
}
}
}
private boolean isAimedDevice(BluetoothDevice bluetoothDevice) {
if (null == bluetoothDevice) {
LogUtil.e("bluetoothDevice cannot be null");
return false;
}
if (TextUtils.isEmpty(sAimedBluetoothDeviceMac)) {
LogUtil.e("AimedBluetoothDeviceMac cannot be empty");
return false;
}
return TextUtils.equals(getAimedDeviceMac(), bluetoothDevice.getAddress());
}
public static void setAimedBluetoothDeviceMac(String bluetoothDeviceMac) {
LogUtil.i("setAimedBluetoothDeviceMac bluetoothDeviceMac=" + bluetoothDeviceMac);
sAimedBluetoothDeviceMac = bluetoothDeviceMac;
}
public static void setCurrentPin(String pin) {
LogUtil.i("setCurrentPin pin=" + pin);
sCurrentPin = pin;
}
private String getPin() {
return sCurrentPin;
}
private String getAimedDeviceMac() {
return sAimedBluetoothDeviceMac;
}
}
/**
* 蓝牙配对辅助类
* Android 8.0及以下版本大多数设备有效(已知华为手机除外)
* 参考源码:platform/packages/apps/Settings.git
* Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
*
* @date 2020/5/7
*/
public class BleAutoPairHelper {
private static final String TAG = "BleAutoPairHelper";
/**
*
* @param bluetoothDevice
* @return
*/
public static boolean createBond(BluetoothDevice bluetoothDevice) {
return invokeMethod(bluetoothDevice, "createBond");
}
/**
* 与设备解除配对
*/
public static boolean removeBond(BluetoothDevice bluetoothDevice) {
return invokeMethod(bluetoothDevice, "removeBond");
}
/**
* 设置配对码
* @param bluetoothDevice
* @param pinStr
* @return
*/
public static boolean setPin(BluetoothDevice bluetoothDevice,
String pinStr) {
byte[] pinCodeBytes = convertPinToBytes(pinStr);
return invokeMethod(bluetoothDevice, "setPin", byte[].class, pinCodeBytes);
}
/**
* 取消配对框
*
* @param bluetoothDevice
* @return
*/
public static boolean cancelPairingUserInput(BluetoothDevice bluetoothDevice) {
return invokeMethod(bluetoothDevice, "cancelPairingUserInput");
}
/**
* 确认配对
*
* @param bluetoothDevice
* @param isConfirm
* @return
*/
public static boolean setPairingConfirmation(BluetoothDevice bluetoothDevice,
boolean isConfirm) {
return invokeMethod(bluetoothDevice, "setPairingConfirmation",
boolean.class, isConfirm);
}
public static boolean cancelBondProcess(BluetoothDevice bluetoothDevice) {
return invokeMethod(bluetoothDevice, "cancelBondProcess");
}
private static boolean invokeMethod(BluetoothDevice bluetoothDevice, String methodName) {
return invokeMethod(bluetoothDevice, methodName, null, null);
}
private static boolean invokeMethod(BluetoothDevice bluetoothDevice,
String methodName,
Class<?> paramClassType,
Object param) {
if (null == bluetoothDevice) {
LogUtil.e("bluetoothDevice can not be null");
return false;
}
Class<? extends BluetoothDevice> clazz = bluetoothDevice.getClass();
Method method = null;
try {
Boolean isSuccess = false;
if (null == paramClassType) {
method = clazz.getMethod(methodName);
isSuccess = (Boolean) method.invoke(bluetoothDevice);
} else {
method = clazz.getDeclaredMethod(methodName, paramClassType);
isSuccess = (Boolean) method.invoke(bluetoothDevice, param);
}
LogUtil.i(TAG
+ " invokeMethod clazz=" + clazz.getSimpleName()
+ " mac" + bluetoothDevice.getAddress()
+ " methodName=" + methodName
+ " paramType=" + paramClassType
+ " param=" + param
+ " isSuccess=" + isSuccess
);
return isSuccess;
} catch (NoSuchMethodException e) {
e.printStackTrace();
LogUtil.e("NoSuchMethodException", e);
return false;
} catch (IllegalAccessException e) {
e.printStackTrace();
LogUtil.e("IllegalAccessException", e);
return false;
} catch (InvocationTargetException e) {
e.printStackTrace();
LogUtil.e("InvocationTargetException", e);
return false;
}
}
/**
* copy from android.bluetooth.BluetoothDevice
*
* @param pin
* @return
*/
private static byte[] convertPinToBytes(String pin) {
if (pin == null) {
return null;
}
byte[] pinBytes;
try {
pinBytes = pin.getBytes("UTF-8");
} catch (UnsupportedEncodingException uee) {
// this should not happen
Log.e(TAG, "UTF-8 not supported?!?");
return null;
}
if (pinBytes.length <= 0 || pinBytes.length > 16) {
return null;
}
return pinBytes;
}
}
网友评论