当前位置: 首页 > news >正文

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完成)

相关文章:

  • 网站开发的技术意义/免费seo刷排名
  • 温州公司建设网站制作/谷歌广告优化师
  • 深圳在建工程查询/韶关seo
  • 苏州商品关键词优化/肇庆seo
  • 重庆公司转让/seo网络排名优化方法
  • 简述创建一个网站的过程/百度一下你就知道下载
  • 【SpringCloud】Erauke的基本原理与使用
  • WuThreat身份安全云-TVD每日漏洞情报-2023-01-18
  • SELECT必知必会_引擎,PROCEDURE,事务处理
  • Cassandra入门教程
  • SpringCloud:SpringCloudGateway对Forwarded和X-Forwarded-*处理分析
  • Linux 命令(245)—— chage 命令
  • java基础学习 day35(StringJoiner的用法,String和这两个扩展的区别)
  • golang解决跨域问题
  • 2-Node.js 内置模块
  • 杭州到温州老家自驾路线优化与整理
  • 【Android安全】Google Hardware-backed Keystore | SafetyNet | 远程证明Remote Attestation
  • LeetCode 1825 求出MK平均值【Set 队列】 HERODING的LeetCode之路