Android 蓝牙开发——蓝牙配对(五)

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一、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、配对请求

1BluetoothDevice.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()。

2AdapterService.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;
}

        这里最终将信息发送到状态机交由状态机处理。

3BondStateMachine 状态机

源码位置/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完成

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: android