Android 蓝牙开发——蓝牙配对(五)
一、APP端调用
//配对
try {
Method method = BluetoothDevice.class.getMethod("createBond");
method.invoke(BluetoothDevice device);
} catch (Exception e) {
e.printStackTrace();
}
//解除配对
try {
Method removeBondMethod = BluetoothDevice.class.getMethod("removeBond");
removeBondMethod.invoke(BluetoothDevice device);
} catch (Exception e) {
e.printStackTrace();
}
二、配对源码分析
上面代码可以看出,使用了 Java 的反射机制去调用 BluetoothDevice 里的 createBond() 和 removeBond() 方法,这里以 createBond() 为例看一下源码的调用过程。
1、配对请求
1)BluetoothDevice.createBond()
源码位置:/frameworks/base/core/java/android/bluetooth/BluetoothDevice.java
public static final int TRANSPORT_AUTO = 0;
public boolean createBond() {
return createBond(TRANSPORT_AUTO);
}
public boolean createBond(int transport) {
return createBondInternal(transport, null, null);
}
private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data, @Nullable OobData remoteP256Data) {
final IBluetooth service = sService;
if (service == null) {
Log.w(TAG, "BT not enabled, createBondOutOfBand failed");
return false;
}
try {
return service.createBond(this, transport, remoteP192Data, remoteP256Data, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
return false;
}
可以看到,最后调用 service.createBond(),即为 AdapterService 中的 createBond()。
2)AdapterService.createBond()
public boolean createBond(BluetoothDevice device, int transport, OobData remoteP192Data, OobData remoteP256Data, AttributionSource attributionSource) {
Attributable.setAttributionSource(device, attributionSource);
AdapterService service = getService();
if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG, "createBond") || !Utils.checkConnectPermissionForDataDelivery(
service, attributionSource, "AdapterService createBond")) {
return false;
}
// 权限验证
service.enforceBluetoothPrivilegedPermissionIfNeeded(remoteP192Data, remoteP256Data);
// 调用自身的 createBond()
return service.createBond(device, transport, remoteP192Data, remoteP256Data, attributionSource.getPackageName());
}
boolean createBond(BluetoothDevice device, int transport, OobData remoteP192Data, OobData remoteP256Data, String callingPackage) {
DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);
// 属性检查
if (deviceProp != null && deviceProp.getBondState() != BluetoothDevice.BOND_NONE) {
return false;
}
if (!isPackageNameAccurate(this, callingPackage, Binder.getCallingUid())) {
return false;
}
CallerInfo createBondCaller = new CallerInfo();
createBondCaller.callerPackageName = callingPackage;
createBondCaller.user = UserHandle.of(UserHandle.getCallingUserId());
mBondAttemptCallerInfo.put(device.getAddress(), createBondCaller);
mRemoteDevices.setBondingInitiatedLocally(Utils.getByteAddress(device));
// Pairing is unreliable while scanning, so cancel discovery
// Note, remove this when native stack improves
// 再次取消扫描
cancelDiscoveryNative();
// 将配对任务转交状态机处理
Message msg = mBondStateMachine.obtainMessage(BondStateMachine.CREATE_BOND);
msg.obj = device;
msg.arg1 = transport;
Bundle remoteOobDatasBundle = new Bundle();
boolean setData = false;
if (remoteP192Data != null) {
remoteOobDatasBundle.putParcelable(BondStateMachine.OOBDATAP192, remoteP192Data);
setData = true;
}
if (remoteP256Data != null) {
remoteOobDatasBundle.putParcelable(BondStateMachine.OOBDATAP256, remoteP256Data);
setData = true;
}
if (setData) {
msg.setData(remoteOobDatasBundle);
}
mBondStateMachine.sendMessage(msg);
return true;
}
这里最终将信息发送到状态机,交由状态机处理。
3)BondStateMachine 状态机
源码位置:/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/BondStateMachine.java
private class StableState extends State {
@Override
public synchronized boolean processMessage(Message msg) {
BluetoothDevice dev = (BluetoothDevice) msg.obj;
Attributable.setAttributionSource(dev, ActivityThread.currentAttributionSource());
switch (msg.what) {
case CREATE_BOND:
OobData p192Data = (msg.getData() != null) ? msg.getData().getParcelable(OOBDATAP192) : null;
OobData p256Data = (msg.getData() != null) ? msg.getData().getParcelable(OOBDATAP256) : null;
// 调用自身的createBond()
createBond(dev, msg.arg1, p192Data, p256Data, true);
break;
}
}
}
private boolean createBond(BluetoothDevice dev, int transport, OobData remoteP192Data, OobData remoteP256Data, boolean transition) {
if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
infoLog("Bond address is:" + dev);
byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
boolean result;
// 如果我们有一些数据
if (remoteP192Data != null || remoteP256Data != null) {
result = mAdapterService.createBondOutOfBandNative(addr, transport, remoteP192Data, remoteP256Data);
} else {
// 调用createBondNative JNI方法
result = mAdapterService.createBondNative(addr, transport);
}
BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED, mAdapterService.obfuscateAddress(dev), transport, dev.getType(), BluetoothDevice.BOND_BONDING, remoteP192Data == null && remoteP256Data == null ? BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN : BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_OOB_DATA_PROVIDED, BluetoothProtoEnums.UNBOND_REASON_UNKNOWN);
if (!result) {
BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED, mAdapterService.obfuscateAddress(dev), transport, dev.getType(), BluetoothDevice.BOND_NONE, BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN, BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS);
// 由于遗留原因使用 UNBOND_REASON_REMOVED
// 发送正在绑定的广播
sendIntent(dev, BluetoothDevice.BOND_NONE, BluetoothDevice.UNBOND_REASON_REMOVED);
return false;
} else if (transition) {
transitionTo(mPendingCommandState);
}
return true;
}
return false;
}
JNI 方法 createBondNative 实现在 com_android_bluetooth_btservice_AdapterService.cpp中。Native 层绑定状态改变会调用 bondStateChangeCallback() 回调方法。
void bondStateChangeCallback(int status, byte[] address, int newState) {
BluetoothDevice device = mRemoteDevices.getDevice(address);
Message msg = obtainMessage(BONDING_STATE_CHANGE);
msg.obj = device;
if (newState == BOND_STATE_BONDED) {
msg.arg1 = BluetoothDevice.BOND_BONDED;
} else if (newState == BOND_STATE_BONDING) {
msg.arg1 = BluetoothDevice.BOND_BONDING;
} else {
msg.arg1 = BluetoothDevice.BOND_NONE;
}
msg.arg2 = status;
sendMessage(msg);
}
private class StableState extends State {
@Override
public synchronized boolean processMessage(Message msg) {
BluetoothDevice dev = (BluetoothDevice) msg.obj;
Attributable.setAttributionSource(dev, ActivityThread.currentAttributionSource());
switch (msg.what) {
case BONDING_STATE_CHANGE:
int newState = msg.arg1;
/* 如果传入配对,则转换到挂起状态 */
if (newState == BluetoothDevice.BOND_BONDING) {
sendIntent(dev, newState, 0);
transitionTo(mPendingCommandState);
} else if (newState == BluetoothDevice.BOND_NONE) {
/* 如果链路键被堆栈删除 */
sendIntent(dev, newState, 0);
} else {
Log.e(TAG, "In stable state, received invalid newState: " + state2str(newState));
}
break;
}
return true;
}
}
void sendIntent(BluetoothDevice device, int newState, int reason) {
......
// 发送绑定状态广播
Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState); // 绑定状态
intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
if (newState == BluetoothDevice.BOND_NONE) {
intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
}
mAdapterService.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
}
发送广播通知 APP 配对状态改变。
4)广播处理
源码位置:/packages/apps/Car/Settings/src/com/android/car/settings/bluetooth/
BluetoothPairingDialog
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();
}
}
}
};
BluetoothPairingService
private final BroadcastReceiver mCancelReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
tring action = intent.getAction();
if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
if ((bondState != BluetoothDevice.BOND_NONE) && (bondState != BluetoothDevice.BOND_BONDED)) {
return;
}
}
stopForeground(true);
stopSelf();
}
};
在 /packages/apps/Settings/src/com/android/settings/bluetooth/ 下同样有着 BluetoothPairingDialog 和 BluetoothPairingService,处理方式基本与上面相同,很多其他位置也需要对该广播进行处理,这里就不再进行分析。
2、配对确认
1)请求回调
配对成功或失败,收到回调 BondStateMachine.sspRequestCallback。
void sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant, int passkey) {
......
BluetoothDevice device = mRemoteDevices.getDevice(address);
......
Message msg = obtainMessage(SSP_REQUEST);
msg.obj = device;
if (displayPasskey) {
msg.arg1 = passkey;
}
msg.arg2 = variant;
sendMessage(msg);
}
private class PendingCommandState extends State {
private final ArrayList<BluetoothDevice> mDevices = new ArrayList<BluetoothDevice>();
@Override
public synchronized boolean processMessage(Message msg) {
switch (msg.what) {
case SSP_REQUEST:
int passkey = msg.arg1;
int variant = msg.arg2;
sendDisplayPinIntent(devProp.getAddress(), passkey, variant);
break;
}
return true;
}
}
private void sendDisplayPinIntent(byte[] address, int pin, int variant) {
Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevices.getDevice(address));
if (pin != 0) {
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pin);
}
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, variant);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
// Android 车机的解决方案,直到预先接受配对请求。
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mAdapterService.sendOrderedBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions(), null/* resultReceiver */, null/* scheduler */, Activity.RESULT_OK/* initialCode */, null/* initialData */, null/* initialExtras */);
}
车机侧自动同意配对请求,等待手机端请求回调。
总结
配对是用来与连接设备创建加密连接的过程。
1、蓝牙配对成功后才进行各种协议(A2dpSink、HFPClient、PbapClient等)的连接。
2、配对过程中的ssp_request,即加密请求,需要用户同意,也可以由 Framework 侧自动回复同意,不弹出用户提示框。
3、配对成功需要两个条件:
a.协议栈 bondStateChangeCallback 回调通知 BOND_BONDED。
b.devicePropertyChangedCallback 回调通知更新配对设备的UUIDs。(即SDP完成)