gpt4 book ai didi

android - 以编程方式与 Android 4.4+ 上的 BLE 设备配对

转载 作者:IT老高 更新时间:2023-10-28 21:46:47 28 4
gpt4 key购买 nike

有没有人有一个完整的工作示例,说明如何在 Android 4.4 或之后? “以编程方式”是指我告诉 Android PIN - 不会提示用户。

在 SO 上有很多类似的问题,但它们要么是 a) 关于蓝牙经典,b) 旧的(在 setPin()createBond() 公开之前),要么 c) 没有答案。

我的理解如下。

  1. 您连接到设备并发现它的服务。
  2. 您尝试读取“ protected ”特征。
  3. 设备返回身份验证错误。
  4. Android 会以某种方式启动配对,然后你告诉它 PIN。
  5. 您现在可以读取特征了。

我创建了一个使用 mBednRF51-DK 上运行的设备,并为其赋予了单一特性。

我这样设置安全参数:

ble.securityManager().init(
true, // Enable bonding (though I don't really need this)
true, // Require MitM protection. I assume you don't get a PIN prompt without this, though I'm not 100% sure.
SecurityManager::IO_CAPS_DISPLAY_ONLY, // This makes it us the Passkey Entry (PIN) pairing method.
"123456"); // Static PIN

然后在我使用的特性中

requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);

现在,当我尝试使用 Nordic Master Control Panel 读取它时,我会收到这样的配对请求通知:

pairing request

passkey entry

我可以输入此 PIN,然后 MCP 说我已绑定(bind),并且可以读取特征。

但是,在我的应用中,我想避免让用户输入 PIN,因为我已经知道了。有没有人有一个完整的最近的例子来说明如何做到这一点?

编辑:顺便说一句,this 是我在 SO 上找到的最相关的问题,但那里的答案似乎不起作用。

最佳答案

几乎让它工作了。它以编程方式配对,但我无法摆脱“配对请求”通知。这个问题的一些答案声称能够在使用隐藏方法 cancelPairingUserInput() 显示它之后将其隐藏,但这似乎对我不起作用。

编辑:成功!

我最终求助于阅读 BluetoothPairingRequest 的源代码和 the code that sends the pairing request broadcast并意识到我应该拦截 ACTION_PAIRING_REQUEST。幸运的是,这是一个有序的 Intent 广播,因此您可以在系统之前拦截它。

这是程序。

  1. 注册以接收 BluetoothDevice.ACTION_PAIRING_REQUEST 更改的广播 Intent 。 使用高优先级!
  2. 连接到设备。
  3. 发现服务。
  4. 如果您现在已经断开连接,可能是因为绑定(bind)信息不正确(例如外围设备清除了它)。在这种情况下,请使用隐藏方法(严重的是 Google)删除绑定(bind)信息,然后重新连接。
  5. 尝试读取需要加密 MitM 保护的特征。
  6. ACTION_PAIRING_REQUEST广播接收器中,检查配对类型是否为BluetoothDevice.PAIRING_VARIANT_PIN,如果是,则调用setPin()abortBroadcast()。否则,您可以让系统处理它,或者显示错误或其他任何内容。

这里是代码。

/* This implements the BLE connection logic. Things to watch out for:

1. If the bond information is wrong (e.g. it has been deleted on the peripheral) then
discoverServices() will cause a disconnect. You need to delete the bonding information and reconnect.

2. If the user ignores the PIN request, you get the undocumented GATT_AUTH_FAILED code.

*/
public class ConnectActivityLogic extends Fragment
{
// The connection to the device, if we are connected.
private BluetoothGatt mGatt;

// This is used to allow GUI fragments to subscribe to state change notifications.
public static class StateObservable extends Observable
{
private void notifyChanged() {
setChanged();
notifyObservers();
}
};

// When the logic state changes, State.notifyObservers(this) is called.
public final StateObservable State = new StateObservable();

public ConnectActivityLogic()
{
}

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

// Tell the framework to try to keep this fragment around
// during a configuration change.
setRetainInstance(true);

// Actually set it in response to ACTION_PAIRING_REQUEST.
final IntentFilter pairingRequestFilter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);
pairingRequestFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY - 1);
getActivity().getApplicationContext().registerReceiver(mPairingRequestRecevier, pairingRequestFilter);

// Update the UI.
State.notifyChanged();

// Note that we don't actually need to request permission - all apps get BLUETOOTH and BLUETOOTH_ADMIN permissions.
// LOCATION_COARSE is only used for scanning which I don't need (MAC is hard-coded).

// Connect to the device.
connectGatt();
}

@Override
public void onDestroy()
{
super.onDestroy();

// Disconnect from the device if we're still connected.
disconnectGatt();

// Unregister the broadcast receiver.
getActivity().getApplicationContext().unregisterReceiver(mPairingRequestRecevier);
}

// The state used by the UI to show connection progress.
public ConnectionState getConnectionState()
{
return mState;
}

// Internal state machine.
public enum ConnectionState
{
IDLE,
CONNECT_GATT,
DISCOVER_SERVICES,
READ_CHARACTERISTIC,
FAILED,
SUCCEEDED,
}
private ConnectionState mState = ConnectionState.IDLE;

// When this fragment is created it is given the MAC address and PIN to connect to.
public byte[] macAddress()
{
return getArguments().getByteArray("mac");
}
public int pinCode()
{
return getArguments().getInt("pin", -1);
}

// Start the connection process.
private void connectGatt()
{
// Disconnect if we are already connected.
disconnectGatt();

// Update state.
mState = ConnectionState.CONNECT_GATT;
State.notifyChanged();

BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress());

// Connect!
mGatt = device.connectGatt(getActivity(), false, mBleCallback);
}

private void disconnectGatt()
{
if (mGatt != null)
{
mGatt.disconnect();
mGatt.close();
mGatt = null;
}
}

// See https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/master/stack/include/gatt_api.h
private static final int GATT_ERROR = 0x85;
private static final int GATT_AUTH_FAIL = 0x89;

private android.bluetooth.BluetoothGattCallback mBleCallback = new BluetoothGattCallback()
{
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
{
super.onConnectionStateChange(gatt, status, newState);
switch (newState)
{
case BluetoothProfile.STATE_CONNECTED:
// Connected to the device. Try to discover services.
if (gatt.discoverServices())
{
// Update state.
mState = ConnectionState.DISCOVER_SERVICES;
State.notifyChanged();
}
else
{
// Couldn't discover services for some reason. Fail.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
}
break;
case BluetoothProfile.STATE_DISCONNECTED:
// If we try to discover services while bonded it seems to disconnect.
// We need to debond and rebond...

switch (mState)
{
case IDLE:
// Do nothing in this case.
break;
case CONNECT_GATT:
// This can happen if the bond information is incorrect. Delete it and reconnect.
deleteBondInformation(gatt.getDevice());
connectGatt();
break;
case DISCOVER_SERVICES:
// This can also happen if the bond information is incorrect. Delete it and reconnect.
deleteBondInformation(gatt.getDevice());
connectGatt();
break;
case READ_CHARACTERISTIC:
// Disconnected while reading the characteristic. Probably just a link failure.
gatt.close();
mState = ConnectionState.FAILED;
State.notifyChanged();
break;
case FAILED:
case SUCCEEDED:
// Normal disconnection.
break;
}
break;
}
}

@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
{
super.onServicesDiscovered(gatt, status);

// Services have been discovered. Now I try to read a characteristic that requires MitM protection.
// This triggers pairing and bonding.

BluetoothGattService nameService = gatt.getService(UUIDs.NAME_SERVICE);
if (nameService == null)
{
// Service not found.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
return;
}
BluetoothGattCharacteristic characteristic = nameService.getCharacteristic(UUIDs.NAME_CHARACTERISTIC);
if (characteristic == null)
{
// Characteristic not found.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
return;
}

// Read the characteristic.
gatt.readCharacteristic(characteristic);
mState = ConnectionState.READ_CHARACTERISTIC;
State.notifyChanged();
}

@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
{
super.onCharacteristicRead(gatt, characteristic, status);

if (status == BluetoothGatt.GATT_SUCCESS)
{
// Characteristic read. Check it is the right one.
if (!UUIDs.NAME_CHARACTERISTIC.equals(characteristic.getUuid()))
{
// Read the wrong characteristic. This shouldn't happen.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
return;
}

// Get the name (the characteristic I am reading just contains the device name).
byte[] value = characteristic.getValue();
if (value == null)
{
// Hmm...
}

disconnectGatt();
mState = ConnectionState.SUCCEEDED;
State.notifyChanged();

// Success! Save it to the database or whatever...
}
else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION)
{
// This is where the tricky part comes
if (gatt.getDevice().getBondState() == BluetoothDevice.BOND_NONE)
{
// Bonding required.
// The broadcast receiver should be called.
}
else
{
// ?
}
}
else if (status == GATT_AUTH_FAIL)
{
// This can happen because the user ignored the pairing request notification for too long.
// Or presumably if they put the wrong PIN in.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
}
else if (status == GATT_ERROR)
{
// I thought this happened if the bond information was wrong, but now I'm not sure.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
}
else
{
// That's weird.
disconnectGatt();
mState = ConnectionState.FAILED;
State.notifyChanged();
}
}
};


private final BroadcastReceiver mPairingRequestRecevier = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction()))
{
final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);

if (type == BluetoothDevice.PAIRING_VARIANT_PIN)
{
device.setPin(Util.IntToPasskey(pinCode()));
abortBroadcast();
}
else
{
L.w("Unexpected pairing type: " + type);
}
}
}
};

public static void deleteBondInformation(BluetoothDevice device)
{
try
{
// FFS Google, just unhide the method.
Method m = device.getClass().getMethod("removeBond", (Class[]) null);
m.invoke(device, (Object[]) null);
}
catch (Exception e)
{
L.e(e.getMessage());
}
}
}

关于android - 以编程方式与 Android 4.4+ 上的 BLE 设备配对,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38055699/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com