【android bluetooth 协议分析 02】【bluetooth hal 层详解 3】【高通蓝牙hal主要流程介绍-上】

news2025/5/23 1:25:46

1. 背景

本节主要讨论 高通 蓝牙 hal 中,的一些流程。 看看你是否都清楚如下问题:

  1. 高通芯片电如何控制?
  2. 串口是在哪里控制的?
  3. 固件如何下载?
  4. 初始化流程是怎么样的?

如果你已经对上述讨论的问题,已经很清楚了,那你无需阅读该文章,请自行忽略。当然,也可以给笨叔挑挑错。 欢迎评论,一起探讨,毕竟都是笨叔自己的理解,难免有点出入,我也想进步!!!

在阅读这篇文章之前, 可以先阅读一下两篇文章。
【android bluetooth 框架分析 02】【Module详解 3】【HciHal 模块介绍】
【android bluetooth 协议分析 02】【bluetooth hal 层详解 2】【蓝牙hal层hidl_1.0和hidl_1.1层接口的区别是什么】

我们这里按照 hidl 1.0 接口讲解。1.1 类似。

2. bt server 触发流程

我们先来回顾一下,bt.server 是如何触发 hal 之间的通信的。

在 HciHal 模块的 Start 函数中,我们会去向 SM 获取 hal_1.0 服务, 并且调用 hal 的 initialize 接口

  // system/gd/hal/hci_hal_android_hidl.cc
  
  void Start() override {
   
    std::string instance = GetHciInstance();

    if (bt_hci_1_1_ != nullptr) {
    } else {
      // 1. 向 SM 获取 IBluetoothHci_1_0 服务。
      bt_hci_ = IBluetoothHci_1_0::getService(instance);
    }

    LOG_INFO("GetService Done.");

    callbacks_ = new InternalHciCallbacks(btaa_logger_, btsnoop_logger_); // hal进程,会调用我们的 接口

    if (bt_hci_1_1_ != nullptr) {
    } else {
      // 2. 调用 hal 接口, 触发 hal 初始化,将回调接口告知 hal进程。
      bt_hci_->initialize(callbacks_);
    }

    // 3. 等待 hal initialize 执行完成。
    callbacks_->GetInitPromise()->get_future().wait();
  }

可以将上面 HciHal 模块的 Start 函数 总结为三步:

  1. 向 SM 找 bt hal 1.0 服务
  2. 调用 hal initialize
  3. 等待 hal initialize 完成

我们本节讨论的 蓝牙芯片上电,打开蓝牙串口, 固件下载。 都是在 这里调用 hal initialize 到 hal initialize 完成直接 进行的。

1. 向 SM 找 bt hal 1.0 服务

std::string GetHciInstance() {
  char buf[64];
  int hci_adapter = InitFlags::GetAdapterIndex();
  // 0 -> "default" (default bluetooth adapter)
  // 1 -> "hci1" (new bluetooth adapter)
  if (hci_adapter > 0) {
    snprintf(buf, sizeof(buf), "hci%d", hci_adapter);
  } else {
    snprintf(buf, sizeof(buf), "default");
  }
  return std::string(buf);
}
  • 如果是 bt0 就找寻 android.hardware.bluetooth@1.0::IBluetoothHci/default 服务, 如果是 bt1 就找寻 android.hardware.bluetooth@1.0::IBluetoothHci/hci1 服务
bt_hci_ = IBluetoothHci_1_0::getService(instance);
  • 通过 IBluetoothHci_1_0::getService 就能获取到 android.hardware.bluetooth@1.0::IBluetoothHci/default 服务。

2. 调用 hal initialize

callbacks_ = new InternalHciCallbacks(btaa_logger_, btsnoop_logger_); // hal进程,会调用我们的 接口

bt_hci_->initialize(callbacks_);


class InternalHciCallbacks : public IBluetoothHciCallbacks {

};
  • InternalHciCallbacks 继承了 IBluetoothHciCallbacks。 hal 侧的回调就是调用到这里。
  • 通过 调用 hal 的 initialize 方法将 bt.server 的回调函数注册进 hal.

调用 hal 的 initialize 方法 , 将完成很多工作。 完成的工作将在 hal 侧详细介绍。我们这里先梳理流程。

当 hal 侧处理完 initialize 后, 会通知 bt.server.继续执行。

3. 等待 hal initialize 完成

callbacks_->GetInitPromise()->get_future().wait();



  std::promise<void>* GetInitPromise() {
    return init_promise_;
  }
  • 通过 init_promise_ 实现等待。

我们看一下, hal 是如何通知我们的。

class InternalHciCallbacks : public IBluetoothHciCallbacks {

  Return<void> initializationComplete(HidlStatus status) {
    common::StopWatch stop_watch(__func__);
    //ASSERT(status == HidlStatus::SUCCESS);
    if (status != HidlStatus::SUCCESS) {
      ALOGE("HidlStatus is not SUCCESS");
      exit(EXIT_FAILURE);
    }
    init_promise_->set_value();
    return Void();
  }


}

在第2步,我们将 InternalHciCallbacks 注册进了 hal 进程。 hal 在处理完成 initialize 后,会通过 initializationComplete 告知 .bt.server.

此时,这里的等待就会退出, 继续执行其他操作。

3. hal 侧逻辑

1. hal服务启动和注册

android 系统中 会在对应的 rc 文件中启动当前的 蓝牙hal

service vendor.bluetooth-1-0-xxxx /vendor/bin/hw/android.hardware.bluetooth@1.0-service-xxxx
    interface android.hardware.bluetooth@1.0::IBluetoothHci default
    class hal
    user bluetooth
    group bluetooth system wakelock oem_2901 net_raw oem_2912
    capabilities BLOCK_SUSPEND NET_ADMIN
// hidl_hci/1.0/default/service.cpp

int main() {
    ALOGI("BT-Transport driver main");
    (void)umask(S_IWGRP | S_IWOTH);

    struct sched_param rt_params;
    rt_params.sched_priority = BT_TX_RT_PRIORITY;

    android::hardware::ProcessState::initWithMmapSize((size_t)(256144));

    configureRpcThreadpool(1, true /*callerWillJoin*/);

    ALOGI("isVendorEnhancedFramework: %d", isVendorEnhancedFramework);
    ALOGI("Registering BT Service");

    status_t status;

    status = registerBluetoothHci(); // 注册蓝牙服务

    if (status != OK)
      ALOGI("Error while registering BT service: %d", status);

    ALOGI("BTTPI: Main, joinRpcThreadpool for HIDL");
    joinRpcThreadpool();

    return status;
}

static status_t registerBluetoothHci()
{
    status_t status;
#ifdef DUAL_BT
    const char *bt_hci_instance = "hci1";
#else
    const char *bt_hci_instance = "default";
#endif

#ifdef LAZY_SERVICE
    status = registerLazyPassthroughServiceImplementation<IBluetoothHci>(bt_hci_instance);
#else
    status = registerPassthroughServiceImplementation<IBluetoothHci>(bt_hci_instance);
#endif

    return status;
}

我们当前的 hal 支持双蓝牙, 从 DUAL_BT 可以看出来,我们通过 编译来控制 编译两个不同的 bin. 用于 bt0 和 bt1.

在 registerBluetoothHci 函数中 当 registerPassthroughServiceImplementation<>() 被调用时:

  1. 框架在 libhidltransport.so 内部调用一个动态查找函数
  2. 它尝试在你的 HAL .so 中查找函数:

extern “C” IBluetoothHci* HIDL_FETCH_IBluetoothHci(const char* name);
3. 如果找到了,就调用它并返回 HAL 实例

// hidl_hci/1.0/default/bluetooth_hci.cpp

IBluetoothHci* HIDL_FETCH_IBluetoothHci(const char* name)
{
  ALOGI("%s ,new BluetoothHci() name:%s slot:%d", __func__, name, Util::getHwSlot());
  Util::init(name);
  return new BluetoothHci(Util::getHwSlot());
}

BluetoothHci 继承 IBluetoothHci 接口


class BluetoothHci : public IBluetoothHci {
 public:
  BluetoothHci();
  BluetoothHci(int slot);
  ~BluetoothHci();
  Return<void> initialize(
      const ::android::sp<IBluetoothHciCallbacks>& cb) override;
  Return<void> sendHciCommand(const hidl_vec<uint8_t>& packet) override;
  Return<void> sendAclData(const hidl_vec<uint8_t>& data) override;
  Return<void> sendScoData(const hidl_vec<uint8_t>& data) override;
  Return<void> close() override;
  Return<void> debug(const hidl_handle& handle,
                     const hidl_vec<hidl_string>& options) override;

 private:
  void sendDataToController(HciPacketType type, const hidl_vec<uint8_t>& data);
  void startDetectBluetooth(int slot);
  void stopDetectBluetooth();
  ::android::sp<IBluetoothHciCallbacks> event_cb_;
  ::android::sp<BluetoothDeathRecipient> deathRecipient;
  int hw_slot_;
  std::thread detect_bt_thread_;
};

extern "C" IBluetoothHci* HIDL_FETCH_IBluetoothHci(const char* name);

// bt0 调用

01-13 02:03:20.629763   714   714 I vendor.RunningAndroid.bluetooth@1.0-bluetooth_hci: HIDL_FETCH_IBluetoothHci ,new BluetoothHci() name:default slot:0


// bt1 调用
01-13 02:03:20.630275   715   715 I vendor.RunningAndroid.bluetooth@1.0-bluetooth_hci: HIDL_FETCH_IBluetoothHci ,new BluetoothHci() name:hci1 slot:0


// 此时 bt0 的服务就已经注册成功了。 bt.server 就可以 get 当前服务了。
01-13 02:03:20.641529   714   714 I HidlServiceManagement: Registered android.hardware.bluetooth@1.0::IBluetoothHci/default

2. initialize 调用

当 bt.server 调用 bt_hci_->initialize(callbacks_); 将调用到如下代码

// hidl_hci/1.0/default/bluetooth_hci.cpp

Return<void> BluetoothHci::initialize(
  const ::android::sp<IBluetoothHciCallbacks>& cb)
{
  bool rc = false;

  ALOGW("BluetoothHci::initialize(), slot%d", hw_slot_);
  if (cb == nullptr) {
    ALOGE("%s: Received NULL callback from BT client", __func__);
    return Void();
  }
  ::android::sp<IBluetoothHciCallbacks> event_cb_tmp;
  event_cb_tmp = cb; // 将我们 bt.server 传递进来的 回调保存在 event_cb_tmp 中了。

   // 1. 调用 DataHandler::Init 方法
    rc = DataHandler::Init( TYPE_BT,
      [this, event_cb_tmp](bool status) {
        if (event_cb_tmp != nullptr) {
          ALOGI("%s: Set callbacks received from BT client inorder "
                 "to provide status and data through them", __func__);
          event_cb_ = event_cb_tmp;
        }
        if (event_cb_ != nullptr) {
          auto hidl_client_status = event_cb_->initializationComplete(
            status ? Status::SUCCESS : Status::INITIALIZATION_ERROR);
          if(!hidl_client_status.isOk()) {
            ALOGE("Client dead, callback initializationComplete failed");
          }
        }
      },
      [this, event_cb_tmp](HciPacketType type, const hidl_vec<uint8_t> *packet) {
        DataHandler *data_handler = DataHandler::Get();
        if (event_cb_tmp == nullptr) {
          ALOGE("BluetoothHci: event_cb_tmp is null");
          if (data_handler)
            data_handler->SetClientStatus(false, TYPE_BT);
          return;
        }
        /* Skip calling client callback when client is dead */
        if(data_handler && (data_handler->GetClientStatus(TYPE_BT) == false)) {
          ALOGI("%s: Skip calling client callback when client is dead", __func__);
          return;
        }
        Logger::Get()->UpdateRxTimeStamp();
        switch (type) {
          case HCI_PACKET_TYPE_EVENT:
          {
#ifdef DUMP_RINGBUF_LOG
            Logger::Get()->UpdateRxEventTag(RX_PRE_STACK_EVT_CALL_BACK);
#endif
            auto hidl_client_status = event_cb_tmp->hciEventReceived(*packet);
            if(!hidl_client_status.isOk()) {
              ALOGE("Client dead, callback hciEventReceived failed");
              if (data_handler)
                data_handler->SetClientStatus(false, TYPE_BT);
            }
#ifdef DUMP_RINGBUF_LOG
            Logger::Get()->UpdateRxEventTag(RX_POST_STACK_EVT_CALL_BACK);
#endif
          }
          break;
          case HCI_PACKET_TYPE_ACL_DATA:
          {
#ifdef DUMP_RINGBUF_LOG
            Logger::Get()->UpdateRxEventTag(RX_PRE_STACK_ACL_CALL_BACK);
#endif
            auto hidl_client_status = event_cb_tmp->aclDataReceived(*packet);
            if(!hidl_client_status.isOk()) {
              ALOGE("Client dead, callback aclDataReceived failed");
              if (data_handler)
                data_handler->SetClientStatus(false, TYPE_BT);
            }
#ifdef DUMP_RINGBUF_LOG
            Logger::Get()->UpdateRxEventTag(RX_POST_STACK_ACL_CALL_BACK);
#endif
          }
          break;
          default:
            ALOGE("%s Unexpected event type %d", __func__, type);
            break;
        }
      });


  if (!rc && (cb != nullptr)) {
     ...
  } else if (rc && (cb != nullptr)) {
     ALOGI("%s: linking to deathRecipient", __func__);
     cb->linkToDeath(deathRecipient, 0);
  }

  return Void();
}

// BluetoothHci::initialize 函数调用开始
01-13 02:03:26.451268   714   714 W vendor.RunningAndroid.bluetooth@1.0-bluetooth_hci: BluetoothHci::initialize(), slot0

01-13 02:03:26.451326   714   714 W vendor.RunningAndroid.bluetooth@1.0-data_handler: DataHandler:: Init()
01-13 02:03:26.451454   714   714 I vendor.RunningAndroid.bluetooth@1.0-data_handler: data_service_setup_sighandler: Entry
01-13 02:03:26.451487   714   714 D vendor.RunningAndroid.bluetooth@1.0-data_handler: isProtocolAdded:
01-13 02:03:26.451498   714   714 I vendor.RunningAndroid.bluetooth@1.0-data_handler: isProtocolAdded: status:0
01-13 02:03:26.451507   714   714 I vendor.RunningAndroid.bluetooth@1.0-data_handler: Open init_status 0 
01-13 02:03:26.451897   714   714 D vendor.RunningAndroid.bluetooth@1.0-wake_lock: Init wakelock is initiated 

// BluetoothHci::initialize 函数调用退出
01-13 02:03:26.452016   714   714 I vendor.RunningAndroid.bluetooth@1.0-bluetooth_hci: initialize: linking to deathRecipient

在 BluetoothHci::initialize 中最主要就是调用 DataHandler::Init

1. DataHandler::Init

// hidl_hci/1.0/default/data_handler.cpp
bool DataHandler::Init(ProtocolType type, InitializeCallback init_cb,
                       DataReadCallback data_read_cb)
{
  // lock required incase of multiple binder threads
  ALOGW("DataHandler:: Init()");
  std::unique_lock<std::mutex> guard(init_mutex_);
  Get();
  // 将上述两个回调 传入 他的 Open 函数
  return data_handler->Open(type, init_cb, data_read_cb);
}

DataHandler::Init 有两个回调 :

  1. InitializeCallback init_cb

  2. DataReadCallback data_read_cb

1. InitializeCallback init_cb

其中 InitializeCallback init_cb 传入的内容是:

[this, event_cb_tmp](bool status) {
        if (event_cb_tmp != nullptr) {
          ALOGI("%s: Set callbacks received from BT client inorder "
                 "to provide status and data through them", __func__);
          event_cb_ = event_cb_tmp;
        }
        if (event_cb_ != nullptr) {
          auto hidl_client_status = event_cb_->initializationComplete(
            status ? Status::SUCCESS : Status::INITIALIZATION_ERROR);
          if(!hidl_client_status.isOk()) {
            ALOGE("Client dead, callback initializationComplete failed");
          }
        }
      }
2. DataReadCallback data_read_cb

DataReadCallback data_read_cb 传入的内容是:

 
      [this, event_cb_tmp](HciPacketType type, const hidl_vec<uint8_t> *packet) {
        DataHandler *data_handler = DataHandler::Get();
        if (event_cb_tmp == nullptr) {
          ALOGE("BluetoothHci: event_cb_tmp is null");
          if (data_handler)
            data_handler->SetClientStatus(false, TYPE_BT);
          return;
        }
        /* Skip calling client callback when client is dead */
        if(data_handler && (data_handler->GetClientStatus(TYPE_BT) == false)) {
          ALOGI("%s: Skip calling client callback when client is dead", __func__);
          return;
        }
        Logger::Get()->UpdateRxTimeStamp();
        switch (type) {
          case HCI_PACKET_TYPE_EVENT:
          {
#ifdef DUMP_RINGBUF_LOG
            Logger::Get()->UpdateRxEventTag(RX_PRE_STACK_EVT_CALL_BACK);
#endif
            auto hidl_client_status = event_cb_tmp->hciEventReceived(*packet);
            if(!hidl_client_status.isOk()) {
              ALOGE("Client dead, callback hciEventReceived failed");
              if (data_handler)
                data_handler->SetClientStatus(false, TYPE_BT);
            }
#ifdef DUMP_RINGBUF_LOG
            Logger::Get()->UpdateRxEventTag(RX_POST_STACK_EVT_CALL_BACK);
#endif
          }
          break;
          case HCI_PACKET_TYPE_ACL_DATA:
          {
#ifdef DUMP_RINGBUF_LOG
            Logger::Get()->UpdateRxEventTag(RX_PRE_STACK_ACL_CALL_BACK);
#endif
            auto hidl_client_status = event_cb_tmp->aclDataReceived(*packet);
            if(!hidl_client_status.isOk()) {
              ALOGE("Client dead, callback aclDataReceived failed");
              if (data_handler)
                data_handler->SetClientStatus(false, TYPE_BT);
            }
#ifdef DUMP_RINGBUF_LOG
            Logger::Get()->UpdateRxEventTag(RX_POST_STACK_ACL_CALL_BACK);
#endif
          }
          break;
          default:
            ALOGE("%s Unexpected event type %d", __func__, type);
            break;
        }
      }

将上述两个回调 传入 他的 Open 函数

2. DataHandler::Open

bool DataHandler::Open(ProtocolType type, InitializeCallback init_cb,
                       DataReadCallback data_read_cb)
{
  char dst_buff[MAX_BUFF_SIZE];
  char init_buff[MAX_BUFF_SIZE];
  struct timeval tv;
  std::map<ProtocolType, ProtocolCallbacksType *>::iterator it;
  std::unique_lock<std::mutex> guard(internal_mutex_);


  ALOGI("Open init_status %d \n", init_status_);


  // update the pending Init cb and other callbacks
  it = protocol_info_.find(type);
  if (it == protocol_info_.end()) {
    ProtocolCallbacksType *cb_data  = new (ProtocolCallbacksType);
    cb_data->type = type;
    cb_data->is_pending_init_cb = true;
    cb_data->init_cb = init_cb; // 将我们回调保存在 cb_data 中
    cb_data->data_read_cb = data_read_cb;
    protocol_info_[type] = cb_data;
  }
  switch (init_status_) { // 这里默认的 状态是 0: INIT_STATUS_IDLE
    ...
    case INIT_STATUS_IDLE:
      init_status_ = INIT_STATUS_INITIALIZING;
      break;
  }


  // 在这里 创建了一个 线程
  init_thread_ = std::thread(...);

  return true;
}

也就是说 bt.server 调用 initialize 函数后,hal 侧启动了一个线程 专门干这个事情。 initialize 函数就顺利返回了。

主要 initialize 的工作全部在 该线程中处理了。

3. 转门的线程处理 initialize 工作

线程处理函数如下:

[this, type]() {


    // 1. 启动定时器
    StartInitTimer();

    if (!IsSocAlwaysOnEnabled()) {
        soc_need_reload_patch = true;
    }
    ALOGI("%s: soc_need_reload_patch = %d", __func__, soc_need_reload_patch); // 这里是1 

    if (soc_type_ == BT_SOC_SMD) {
    } else {
      // 2. 创建 UartController 对象
      controller_ = static_cast<Controller *> (new UartController(soc_type_));
    }

    if (controller_) {
      int retry_count = 0;
      // 进入循环 初始化。如果初始化失败,接着尝试
      while (retry_count < INIT_MAX_RETRY_TIMES) {
        // 3. 调用 controller_->Init 函数
        status = controller_->Init([this](ProtocolType ptype, HciPacketType type,
                                          const hidl_vec<uint8_t> *hidl_data)   {
                                     OnPacketReady(ptype, type, hidl_data);
                                   });
        if (status)
          break;
        ++retry_count;
      }
      
    }

    ...

	// 4. 初始化成功后 停止定时器
    StopInitTimer();
    std::unique_lock<std::mutex> guard(internal_mutex_);
    if (status) {
      /* Stop moving further if timeout detected */
      {
        guard.unlock();
        std::unique_lock<std::mutex> lock(DataHandler::init_timer_mutex_);
        if (GetInitTimerState() == TIMER_OVERFLOW) {
          ALOGW("Initialization timeout detected cleanup is in process");
          // Init thread exited.
          is_init_thread_killed = true;
          return;
        }
        guard.lock();
        init_status_ = INIT_STATUS_SUCCESS;
        ALOGD("Firmware download succeded.");
      }
    } else {
		...
    }

    std::map<ProtocolType, ProtocolCallbacksType *>::iterator it;
    for (auto& it: protocol_info_) {
      ProtocolCallbacksType *cb_data = (ProtocolCallbacksType*)it.second;
      cb_data->is_pending_init_cb = false;
      gettimeofday(&tv, NULL);
      snprintf(dst_buff, sizeof(dst_buff), "Init callback status = %d", status);
      BtState::Get()->AddLogTag(cb_status_buf, tv, dst_buff);
      BtState::Get()->SetTsStatusOfCbSent(cb_status_buf);
      // 5. 调用回调
      cb_data->init_cb(status); 
    }

    // clear the list if the controller open call fails
    if (!status) {
	    ...
    }
    guard.unlock();
    // BT ON successful
    property_set("persist.vendor.service.bdroid.system_delay_crash_count", "0");
    // Init thread exited.
    is_init_thread_killed = true;
    ALOGD("%s: init thread exited now", __func__);
  }

这个线程 主要做如下几件事情:

  1. 启动定时器
  2. 创建 UartController 对象
  3. 调用 controller_->Init 函数
  4. 初始化成功后 停止定时器
  5. 调用回调 init_cb

1. 启动定时器

启动定时器的目的是, 为了 防止 Controller 交互时, 无响应。 这里不是重点。暂时忽略。

2.创建 UartController 对象

// hidl_hci/1.0/default/uart_controller.cpp

UartController::UartController(BluetoothSocType soc_type)
  : soc_crashed(false), soc_type_(soc_type),
  hci_packetizer_([this](hidl_vec<uint8_t> *data) { OnPacketReady(data); })
{
...
}

UartController 没有啥可以讲述的,一堆变量。暂时忽略

3. 调用 controller_->Init 函数


bool UartController::Init(PacketReadCallback pkt_read_cb)
{

	power_manager_.Init(soc_type_);



  // 1. 给芯片 上电
  if (soc_need_reload_patch) {
    // power off the chip first
    power_manager_.SetPower(false);

    // power on the chip using power manager
    power_manager_.SetPower(true);
  }



  // 2. 初始化 HciTransport
  hci_transport_ = static_cast<HciTransport*> (uart_transport);
  ret = uart_transport->Init(soc_type_, soc_need_reload_patch);

  // 3. 创建 固件 补丁管理器
  patch_dl_manager = new (std::nothrow)PatchDLManager(soc_type_, uart_transport, &power_manager_);

  uart_transport->ClockOperation(USERIAL_OP_CLK_ON);



  //Download the NVM/RAM patch
  if (soc_need_reload_patch) {
    logger_->PropertyGet("vendor.wc_transport.skip_patch_dload", skip_patch_download, "false");
    if (strcmp(skip_patch_download, "true") != 0) {
      // 4. 开始打补丁, 下载固件
      if (patch_dl_manager->PerformChipInit() < 0) {
      }
      temp_add_on_features = patch_dl_manager->GetAddOnFeatureList();
    } else {}
  }


  // 获取 controller 的芯片版本, 做记录使用
  chipset_ver_ = patch_dl_manager->GetChipVersion();

  init_done_ = true;

  ALOGD("Init succeded");
  return init_done_;
}

由于篇幅原因,这里将于 下一篇,中讲解 这部分内容

4. 初始化成功后 停止定时器

这里不是重点。暂时忽略。

5. 调用回调 init_cb

当芯片上完电, 已经下载完固件后, 将调用 回调。

cb_data->init_cb(status); 

这里将回调到 3.2.1.1 小节中的回调。

[this, event_cb_tmp](bool status) {
        if (event_cb_tmp != nullptr) {
          ALOGI("%s: Set callbacks received from BT client inorder "
                 "to provide status and data through them", __func__);
          event_cb_ = event_cb_tmp;
        }
        if (event_cb_ != nullptr) {
          auto hidl_client_status = event_cb_->initializationComplete(
            status ? Status::SUCCESS : Status::INITIALIZATION_ERROR);
          if(!hidl_client_status.isOk()) {
            ALOGE("Client dead, callback initializationComplete failed");
          }
        }
      }
  • 最终会回调到 bt.server 中的 initializationComplete 函数。告知 bt.server 初始化成功。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2383517.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C# 深入理解类(实例构造函数)

实例构造函数 实例构造函数是一个特殊的方法&#xff0c;它在创建类的每个新实例时执行。 构造函数用于初始化类实例的状态。如果希望能从类的外部创建类的实例&#xff0c;需要将构造函数声明为public。 图7-2阐述了构造函数的语法。除了下面这几点&#xff0c;构造函数看起…

RabbitMQ——消息确认

一、消息确认机制 生产者发送的消息&#xff0c;可能有以下两种情况&#xff1a; 1> 消息消费成功 2> 消息消费失败 为了保证消息可靠的到达消费者&#xff08;&#xff01;&#xff01;&#xff01;注意&#xff1a;消息确认机制和前面的工作模式中的publisher confi…

测试W5500的第2步_使用ioLibrary库创建TCP客户端

ioLibrary库下载地址&#xff1a;文件下载地址:https://gitee.com/wiznet-hk/STM32F10x_W5500_Examples 源文件下载地址:https://gitee.com/wiznet-hk 没有注册的&#xff0c;只能复制粘贴了。 本文介绍了如何初始化STM32的硬件资源&#xff0c;配置W5500的网络参数&#xff…

深度学习之用CelebA_Spoof数据集搭建一个活体检测-训练好的模型用MNN来推理

一、模型转换准备 首先确保已完成PyTorch到ONNX的转换&#xff1a;深度学习之用CelebA_Spoof数据集搭建活体检测系统&#xff1a;模型验证与测试。这里有将PyTorch到ONNX格式的模型转换。 二、ONNX转MNN 使用MNN转换工具进行格式转换&#xff1a;具体的编译过程可以参考MNN的…

开源安全大模型Foundation-Sec-8B实操

一、兴奋时刻 此时此刻,晚上22点55分,从今天早上6点左右开始折腾,花费了接近10刀的环境使用费,1天的休息时间,总算是把Foundation-Sec-8B模型跑起来了,中间有两次胜利就在眼前,但却总在远程端口转发环节出问题,让人难受。直到晚上远程Jupyter访问成功那一刻,眉开眼笑,…

【JavaWeb】MySQL

1 引言 1.1 为什么学&#xff1f; 在学习SpringBootWeb基础知识(IOC、DI等)时&#xff0c;在web开发中&#xff0c;为了应用程序职责单一&#xff0c;方便维护&#xff0c;一般将web应用程序分为三层&#xff0c;即&#xff1a;Controller、Service、Dao 。 之前的案例中&am…

微信小游戏流量主广告自动化浏览功能案例5

功能需求&#xff1a; 支持APP单行文本框输入1个小程序链接&#xff0c;在“文件传输助手”界面发送小程序链接并进入。 主要有“文章列表首页”和“文章内容”页面。每个页面支持点击弹窗广告、槽位广告、视频广告入口、视频广告内第三方广告。 弹窗广告、槽位广告、视频广…

软件的技术架构、应用架构、业务架构、数据架构、部署架构

一、各架构定义 1. 技术架构&#xff08;Technical Architecture&#xff09; 定义&#xff1a;技术架构关注的是支撑系统运行的底层技术基础设施和软件平台&#xff0c;包括硬件、操作系统、中间件、编程语言、框架、数据库管理系统等技术组件的选择和组合方式。它描述了系统…

CSS 文字样式全解析:从基础排版到视觉层次设计

CSS 文字样式目录 一、字体家族&#xff08;font-family&#xff09; 二、字体大小&#xff08;font-size&#xff09; 三、字体粗细&#xff08;font-weight&#xff09; 四、字体样式&#xff08;font-style&#xff09; 五、文本转换&#xff08;text-transform&#xf…

【高德开放平台-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

RabbitMQ的简介

三个概念 生产者&#xff1a;生产消息的服务消息代理&#xff1a;消息中间件&#xff0c;如RabbitMQ消费者&#xff1a;获取使用消息的服务 消息队列到达消费者的两种形式 队列&#xff08;queue&#xff09;:点对点消息通信&#xff08;point-to-point&#xff09; 消息进入队…

使用Gemini, LangChain, Gradio打造一个书籍推荐系统 (第一部分)

第一部分&#xff1a;数据处理 import kagglehub# Download latest version path kagglehub.dataset_download("dylanjcastillo/7k-books-with-metadata")print("Path to dataset files:", path)自动下载该数据集的 最新版本 并返回本地保存的路径 impo…

大语言模型 16 - Manus 超强智能体 Prompt分析 原理分析 包含工具列表分析

写在前面 Manus 是由中国初创公司 Monica.im 于 2025 年 3 月推出的全球首款通用型 AI 智能体&#xff08;AI Agent&#xff09;&#xff0c;旨在实现“知行合一”&#xff0c;即不仅具备强大的语言理解和推理能力&#xff0c;还能自主执行复杂任务&#xff0c;直接交付完整成…

以太联Intellinet带您深度解析PoE交换机的上行链路端口(Uplink Ports)

在当今网络技术日新月异的时代&#xff0c;以太网供电(PoE)交换机已然成为现代网络连接解决方案中不可或缺的“利器”。它不仅能够出色地完成数据传输任务&#xff0c;还能为所连接的设备提供电力支持&#xff0c;彻底摆脱了单独电源适配器的束缚&#xff0c;让网络部署更加简洁…

分类算法 Kmeans、KNN、Meanshift 实战

任务 1、采用 Kmeans 算法实现 2D 数据自动聚类&#xff0c;预测 V180,V260 数据类别&#xff1b; 2、计算预测准确率&#xff0c;完成结果矫正 3、采用 KNN、Meanshift 算法&#xff0c;重复步骤 1-2 代码工具&#xff1a;jupyter notebook 视频资料 无监督学习&#xff…

网络安全之身份验证绕过漏洞

漏洞简介 CrushFTP 是一款由 CrushFTP LLC 开发的强大文件传输服务器软件&#xff0c;支持FTP、SFTP、HTTP、WebDAV等多种协议&#xff0c;为企业和个人用户提供安全文件传输服务。近期&#xff0c;一个被编号为CVE-2025-2825的严重安全漏洞被发现&#xff0c;该漏洞影响版本1…

MySQL 主从复制搭建全流程:基于 Docker 与 Harbor 仓库

一、引言 在数据库管理中&#xff0c;MySQL 主从复制是一种非常重要的技术&#xff0c;它可以实现数据的备份、读写分离&#xff0c;减轻主数据库的压力。本文将详细介绍如何使用 Docker 和 Harbor 仓库来搭建 MySQL 主从复制环境&#xff0c;适合刚接触数据库和 Docker 的新手…

Django框架的前端部分使用Ajax请求一

Ajax请求 目录 1.ajax请求使用 2.增加任务列表功能(只有查看和新增) 3.代码展示集合 这篇文章, 要开始讲关于ajax请求的内容了。这个和以前文章中写道的Vue框架里面的axios请求, 很相似。后端代码, 会有一些细节点, 跟前几节文章写的有些区别。 一、ajax请求使用 我们先…

cmd如何从C盘默认路径切换到D盘某指定目录

以从C盘cmd打开后的默认目录切换到目录"D:\Program Files\MySQL\MySQL Server 8.0\bin\mysqld"为例 打开cmd 首先点击开始键&#xff0c;搜索cms&#xff0c;右键以管理员身份运行打开管理员端的命令行提示符 1、首先要先切换到D盘 直接输入D:然后回车就可以&…

每日Prompt:实物与手绘涂鸦创意广告

提示词 一则简约且富有创意的广告&#xff0c;设置在纯白背景上。 一个真实的 [真实物体] 与手绘黑色墨水涂鸦相结合&#xff0c;线条松散而俏皮。涂鸦描绘了&#xff1a;[涂鸦概念及交互&#xff1a;以巧妙、富有想象力的方式与物体互动]。在顶部或中部加入粗体黑色 [广告文案…