有没有人有一个完整的工作示例,说明如何在Android 4.4或更高版本上以编程方式与使用密钥输入(即6位PIN)或数字比较的BLE(非Bluetooth Classic)设备配对?我说的“编程”是指我告诉Android密码——用户不会被提示。
关于这一点,有许多类似的问题,但它们要么是a)关于蓝牙经典,要么是旧的(在setPin()
和createBond()
公开之前),要么是没有答案。
我的理解如下。
我使用运行在nRF51 DK上的mBed创建了一个设备,并为其提供了一个单一特征。
我设置安全参数如下:
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主控制面板阅读它时,我会收到如下配对请求通知:
我可以输入这个PIN,然后MCP说我已经绑定了,并且可以读取特征。
然而,在我的应用程序中,我希望避免让用户输入PIN,因为我已经知道了。有没有人最近有一个完整的例子来说明如何做到这一点?
编辑:顺便说一句,这是我在SO上找到的最相关的问题,但那里的答案似乎不起作用。
我也面临同样的问题,经过所有的研究,我想出了下面的解决方案,无需任何手动干预即可与BLE配对。
(已测试并工作!!!)
我基本上是寻找一个特定的蓝牙设备(我知道MAC地址)和配对,一旦找到。要做的第一件事是使用广播接收器创建配对请求,并按以下方式处理该请求。
IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);
intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(broadCastReceiver,intentFilter);
您需要编写broadcastReceiver并按如下方式处理它。
String BLE_PIN = "1234"
private BroadcastReceiver broadCastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action))
{
BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
bluetoothDevice.setPin(BLE_PIN.getBytes());
Log.e(TAG,"Auto-entering pin: " + BLE_PIN);
bluetoothDevice.createBond();
Log.e(TAG,"pin entered and request sent...");
}
}
};
瞧!您应该能够在没有任何手动干预的情况下与蓝牙设备配对。
希望这有助于:-)请让它正确的答案,如果它为你工作。
<罢工> 我 几乎让它工作。它以编程方式配对,但我无法摆脱“配对请求”通知。这个问题的一些答案声称可以在使用隐藏方法显示后隐藏它 取消PairingUserInput()
但这似乎对我不起作用。
我最终求助于阅读BluetoothPairingRequest的源代码和发送配对请求广播的代码,并意识到我应该拦截动作配对请求。幸运的是,这是一个有序的意图广播,因此您可以在系统之前拦截它。
这是程序。
这是代码。
/* 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设备上,当设备插入计算机的USB端口或什至在某些USB充电设备上时,手机都会进入USB存储模式。当设备处于此模式时,Android应用无法访问sdcard。有什么方法(1)检测设备何时处于此模式,以及(2)以编程方式(至少暂时)关闭USB存储设备,以便我的Android应用程序可以访问sdcard? 问题答案: 您可以检测到它(有了链接),但是AFAIK至少不能使用
问题内容: 我需要使用Java获取我的android设备的MAC地址。我已经在网上搜索过,但没有发现任何有用的信息。 问题答案: 正如评论中已经指出的那样,可以通过接收MAC地址。 同样不要忘记在你的计算机中添加适当的权限
服务器时间在单击按钮事件中自动设置为我的设备。
我正在使用AutoLayout开发iPad应用程序,如果用户启用某个模式(“平视”模式),我希望只支持纵向(或纵向倒置)方向,而且,如果设备处于横向,我希望自动切换到纵向模式。 在顶视图控制器中,我有以下内容: 根据我在这里其他地方看到的答案,答案似乎是我应该使用“application SetStatusBaroOrientation”。因此,在用户选择“抬头”模式的方法中,我包括: 然而,这似
场景:我将默认设备方向设置为纵向。但是,我想允许横向模式的一些视图。 我的大多数视图不需要是横向的。但是有一些带有方向检测,允许以横向模式显示全尺寸线性图。 我希望我的应用程序在大多数情况下都以肖像为中心。 问:我如何在需要的时候冻结肖像,但在情况允许的情况下允许风景?
我使用与相同的代码来配对设备,就像我用于配对一样。我不确定该代码是否适用于BLE,因为我目前或以后没有BLE设备要测试(只有客户端可以测试)。 我的蓝牙经典配对代码- 这里的每个列表项都是需要绑定的设备。 所以我们点击这个项目,然后它就会被上面的代码绑定。(这适用于经典的蓝牙配对)。 相同的代码是否适用于BLE配对? 我没有找到BLE设备的任何配对代码,也没有找到任何与配对相关的在线/官方信息。这