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

【android bluetooth 协议分析 03】【蓝牙扫描详解 1】【扫描关键函数 btif_dm_search_devices_evt 分析】

1. 背景

本篇我们来对 btif_dm_search_devices_evt 函数进行分析.
这是系统性分析 Bluetooth 协议栈中的设备扫描流程时必须厘清的一环。

1. 为什么要单独分析 btif_dm_search_devices_evt 函数:

btif_dm_search_devices_evt 是 BTIF 层中处理设备扫描(discovery)阶段事件的核心函数。虽然它不负责服务发现(SDP),但它对扫描流程的结果质量、事件响应、设备发现体验具有决定性影响。

它在整个蓝牙设备发现(scan/inquiry)阶段中,是唯一负责接收和转发底层 BTA_DM 层设备发现结果的入口,因此:

它是“设备被发现的那一刻”开始的所有行为的主控函数。

2. 它在扫描过程中的核心作用(职责)

在用户发起扫描(如调用 BluetoothAdapter.startDiscovery())之后,底层蓝牙协议栈通过如下层级传播事件:

Controller → HCI → BTM → BTA_DM → BTIF (btif_dm_search_devices_evt) → Java Framework
在这个过程中,btif_dm_search_devices_evt 承担了:

职责说明
① 接收扫描结果事件(BTA_DM_INQ_RES_EVT一个远程设备被发现
② 接收扫描完成事件(BTA_DM_INQ_CMPL_EVT设备发现完成,结束通知
③ 接收设备基础信息事件(BTA_DM_DISCOVERY_RESULT_EVT包括名称、RSSI、Class、Bond 状态等
④ 对这些事件进行解析并封装转换为 HAL 到 Java 层可识别格式(如 bt_property)
⑤ 将解析后的信息通过 HAL 层回调通知 Java Framework,更新系统设备列表(如 Settings 页面)

3. 如果扫描行为出现问题,为什么应该从 btif_dm_search_devices_evt 切入排查?

1. 典型问题场景

问题场景原因可能与 btif_dm_search_devices_evt 有关
搜不到任何设备没有收到 BTA_DM_INQ_RES_EVT 或未正确处理
扫描结束没有反馈没有处理 BTA_DM_INQ_CMPL_EVT 或未通知上层
显示设备但没有名称没有正确处理 BTA_DM_DISCOVERY_RESULT_EVT,或未设置名称属性
发现的设备没有正确标注配对状态未解析 bonded 标志位,或未设置 bt_property
Settings 界面显示设备信息不完整bt_property 转换逻辑有误或缺字段
发现设备信息重复或闪烁没有去重、更新逻辑错误

2. 排查路径从该函数切入的优势

信息齐全:它汇聚了 scan 阶段的所有设备信息(包括名称、RSSI、配对状态)

流程中枢:所有底层扫描事件都通过它统一传入 BTIF 层

上层反馈口:最终会通过 HAL 接口向 Java 层发送设备属性信息

因此:一旦设备列表更新、展示、扫描反馈等出了问题,这里一定是最关键的分析锚点。


2. btif_dm_search_devices_evt

上面已经介绍了 btif_dm_search_devices_evt 重要性。那我们正式开始 对 该函数的详细分析。


/******************************************************************************** Function         btif_dm_search_devices_evt** Description      Executes search devices callback events in btif context** Returns          void******************************************************************************/
static void btif_dm_search_devices_evt(tBTA_DM_SEARCH_EVT event,tBTA_DM_SEARCH* p_search_data) {BTIF_TRACE_EVENT("%s event=%s", __func__, dump_dm_search_event(event));switch (event) {case BTA_DM_DISC_RES_EVT: {} break; // 该事件是 SDP 相关的事件。该事件只在 btif_dm_search_services_evt 中被调用。这里不会调用。可以忽略case BTA_DM_INQ_RES_EVT: {}  break;case BTA_DM_INQ_CMPL_EVT: {} break;case BTA_DM_DISC_CMPL_EVT: {}  break; // 该事件是 SDP 相关的事件。该事件只在 btif_dm_search_services_evt 中被调用。这里不会调用。可以忽略case BTA_DM_SEARCH_CANCEL_CMPL_EVT: {} break;}
}

参数说明:

  • event: 搜索事件类型(例如设备发现、发现完成等),枚举值 tBTA_DM_SEARCH_EVT
  • p_search_data: 携带事件相关数据的结构体指针,类型为 tBTA_DM_SEARCH*

该函数分别处理如下几个事件:

事件类型触发背景核心处理内容
BTA_DM_INQ_RES_EVT扫描到新设备或设备状态更新分析 EIR、获取名称和服务 UUID,持久化并通知上层
BTA_DM_INQ_CMPL_EVTInquiry 扫描阶段结束不处理(由其它模块标记状态)
BTA_DM_SEARCH_CANCEL_CMPL_EVT用户取消搜索操作完成判断是否可结束搜索流程并通知上层

接下来针对每个事件做 介绍

1.BTA_DM_INQ_RES_EVT - 设备扫描结果(inquiry)

case BTA_DM_INQ_RES_EVT: 设备扫描结果(inquiry)

背景:
这是“扫描”过程中最常见的事件,每当发现一个新的设备或者更新了已有设备信息,都会触发这个事件。

触发条件:
设备处于扫描模式中(Inquiry),某个远程设备发出 Inquiry Response 或者扫描响应时触发。

{// 准备存放设备名称和 UUID(16-bit 和 128-bit)的缓存区。/* inquiry result */bt_bdname_t bdname;uint8_t remote_name_len;uint8_t num_uuids = 0, num_uuids128 = 0, max_num_uuid = 32;uint8_t uuid_list[32 * Uuid::kNumBytes16];uint8_t uuid_list128[32 * Uuid::kNumBytes128];// 根据 EIR 数据判断是否需要发起 Remote Name Request(即设备是否已广播出名称)。p_search_data->inq_res.remt_name_not_required =check_eir_remote_name(p_search_data, NULL, NULL);// 获取发现设备的地址。RawAddress& bdaddr = p_search_data->inq_res.bd_addr;BTIF_TRACE_DEBUG("%s() %s device_type = 0x%x\n", __func__,bdaddr.ToString().c_str(),p_search_data->inq_res.device_type);bdname.name[0] = 0;// 尝试从 EIR 中读取名称,失败则从缓存中获取。if (!check_eir_remote_name(p_search_data, bdname.name, &remote_name_len))check_cached_remote_name(p_search_data, bdname.name, &remote_name_len);/* Check EIR for services */// 获取 UUID(服务)信息:if (p_search_data->inq_res.p_eir) {BTM_GetEirUuidList(p_search_data->inq_res.p_eir,p_search_data->inq_res.eir_len, Uuid::kNumBytes16,&num_uuids, uuid_list, max_num_uuid);BTM_GetEirUuidList(p_search_data->inq_res.p_eir,p_search_data->inq_res.eir_len, Uuid::kNumBytes128,&num_uuids128, uuid_list128, max_num_uuid);}{/*构建属性列表:将设备属性打包成数组,逐项填充:- 蓝牙地址- 名称- 类别(COD)- 类型(BR/EDR、BLE 或 DUAL)- RSSI- 是否支持 CSIP 协调组- UUID(服务)UUID 部分还会缓存入 eir_uuids_cache,并使用 btif_update_uuid 做去重更新。*/bt_property_t properties[7];bt_device_type_t dev_type;uint32_t num_properties = 0;bt_status_t status;tBLE_ADDR_TYPE addr_type = BLE_ADDR_PUBLIC;memset(properties, 0, sizeof(properties));/* RawAddress */BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],BT_PROPERTY_BDADDR, sizeof(bdaddr), &bdaddr);num_properties++;/* BD_NAME *//* Don't send BDNAME if it is empty */if (bdname.name[0]) {BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],BT_PROPERTY_BDNAME,strlen((char*)bdname.name), &bdname);num_properties++;}/* DEV_CLASS */uint32_t cod = devclass2uint(p_search_data->inq_res.dev_class);BTIF_TRACE_DEBUG("%s cod is 0x%06x", __func__, cod);if (cod != 0) {BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],BT_PROPERTY_CLASS_OF_DEVICE, sizeof(cod),&cod);num_properties++;}/* DEV_TYPE *//* FixMe: Assumption is that bluetooth.h and BTE enums match *//* Verify if the device is dual mode in NVRAM */int stored_device_type = 0;if (btif_get_device_type(bdaddr, &stored_device_type) &&((stored_device_type != BT_DEVICE_TYPE_BREDR &&p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BREDR) ||(stored_device_type != BT_DEVICE_TYPE_BLE &&p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BLE))) {dev_type = (bt_device_type_t)BT_DEVICE_TYPE_DUMO;} else {dev_type = (bt_device_type_t)p_search_data->inq_res.device_type;}if (p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BLE)addr_type = p_search_data->inq_res.ble_addr_type;BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],BT_PROPERTY_TYPE_OF_DEVICE, sizeof(dev_type),&dev_type);num_properties++;/* RSSI */BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],BT_PROPERTY_REMOTE_RSSI, sizeof(int8_t),&(p_search_data->inq_res.rssi));num_properties++;/* CSIP supported device */BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER,sizeof(bool),&(p_search_data->inq_res.include_rsi));num_properties++;/* Cache EIR queried services */if ((num_uuids + num_uuids128) > 0) {uint16_t* p_uuid16 = (uint16_t*)uuid_list;auto uuid_iter = eir_uuids_cache.find(bdaddr);Uuid    new_remote_uuid[BT_MAX_NUM_UUIDS];size_t  dst_max_num = sizeof(new_remote_uuid)/sizeof(Uuid);size_t  new_num_uuid = 0;Uuid    remote_uuid[BT_MAX_NUM_UUIDS];if (uuid_iter == eir_uuids_cache.end()) {auto triple = eir_uuids_cache.try_emplace(bdaddr, std::set<Uuid>{});uuid_iter = std::get<0>(triple);}//LOG_INFO("EIR UUIDs for %s:", bdaddr.ToString().c_str());for (int i = 0; i < num_uuids; ++i) {Uuid uuid = Uuid::From16Bit(p_uuid16[i]);//LOG_INFO("        %s", uuid.ToString().c_str());uuid_iter->second.insert(uuid);if (i < BT_MAX_NUM_UUIDS) {remote_uuid[i] = uuid;} else {LOG_INFO("%d >= %d", i, BT_MAX_NUM_UUIDS);}}for (int i = 0; i < num_uuids128; ++i) {Uuid uuid = Uuid::From128BitBE((uint8_t *)&uuid_list128[i * Uuid::kNumBytes128]);//LOG_INFO("        %s", uuid.ToString().c_str());uuid_iter->second.insert(uuid);if (i < BT_MAX_NUM_UUIDS) {remote_uuid[num_uuids + i] = uuid;} else {LOG_INFO("%d >= %d", i, BT_MAX_NUM_UUIDS);}}//LOG_INFO("%s %d : update EIR UUIDs.", __func__, __LINE__);new_num_uuid = btif_update_uuid(bdaddr, remote_uuid,(num_uuids + num_uuids128), new_remote_uuid,sizeof(new_remote_uuid),dst_max_num);BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],BT_PROPERTY_UUIDS,new_num_uuid * Uuid::kNumBytes128, new_remote_uuid);//LOG_INFO("%s %d : fill BT_PROPERTY_UUIDS property.", __func__, __LINE__);num_properties ++;}// 持久化 & 回调:status =btif_storage_add_remote_device(&bdaddr, num_properties, properties); // 添加进本地数据库ASSERTC(status == BT_STATUS_SUCCESS,"failed to save remote device (inquiry)", status);status = btif_storage_set_remote_addr_type(&bdaddr, addr_type); // 保存 BLE 地址类型ASSERTC(status == BT_STATUS_SUCCESS,"failed to save remote addr type (inquiry)", status);bool restrict_report = osi_property_get_bool("bluetooth.restrict_discovered_device.enabled", false);// 限制上报策略, 可选地根据系统属性是否限制上报某些设备。if (restrict_report &&p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BLE &&!(p_search_data->inq_res.ble_evt_type & BTM_BLE_CONNECTABLE_MASK)) {LOG_INFO("%s: Ble device is not connectable",bdaddr.ToString().c_str());break;}// 上报到上层(Java 层): 触发 BluetoothAdapter.onBluetoothDeviceFound() 等 Java 层通知。/* Callback to notify upper layer of device */invoke_device_found_cb(num_properties, properties);}}

2.BTA_DM_INQ_CMPL_EVT - 扫描完成

该事件标志 Inquiry 扫描结束,但此处未做处理(可能状态已在其它地方标记)。

{/* do nothing */
}

3. BTA_DM_SEARCH_CANCEL_CMPL_EVT - 搜索取消完成

case BTA_DM_SEARCH_CANCEL_CMPL_EVT: 搜索取消完成

{/* if inquiry is not in progress and we get a cancel event, then* it means we are done with inquiry, but remote_name fetches are in* progress** if inquiry  is in progress, then we don't want to act on this* cancel_cmpl_evt* but instead wait for the cancel_cmpl_evt via the Busy Level**/// 如果当前扫描已结束但仍在等待 remote name,则标记整个搜索流程正式结束。if (!btif_dm_inquiry_in_progress) {invoke_discovery_state_changed_cb(BT_DISCOVERY_STOPPED);}}

相关文章:

【android bluetooth 协议分析 03】【蓝牙扫描详解 1】【扫描关键函数 btif_dm_search_devices_evt 分析】

1. 背景 本篇我们来对 btif_dm_search_devices_evt 函数进行分析. 这是系统性分析 Bluetooth 协议栈中的设备扫描流程时必须厘清的一环。 1. 为什么要单独分析 btif_dm_search_devices_evt 函数&#xff1a; btif_dm_search_devices_evt 是 BTIF 层中处理设备扫描&#xff0…...

SpringBoot使用ThreadLocal保存登录用户信息

Java 多线程,系列文章: 《Java多线程》 《Java创建多线程的3种方法:继承Thread类、实现Runnable接口、实现Callable接口》 《Java多线程的同步:synchronized关键字、Lock接口、volatile关键字》 《Java线程池》 《Java线程池实现秒杀功能》 《SpringBoot使用ThreadLocal保存…...

多模态大语言模型arxiv论文略读(102)

Chat2Layout: Interactive 3D Furniture Layout with a Multimodal LLM ➡️ 论文标题&#xff1a;Chat2Layout: Interactive 3D Furniture Layout with a Multimodal LLM ➡️ 论文作者&#xff1a;Can Wang, Hongliang Zhong, Menglei Chai, Mingming He, Dongdong Chen, Ji…...

Ubuntu系统如何部署Crawlab爬虫管理平台(通过docker部署)

Ubuntu系统如何部署Crawlab爬虫管理平台(通过docker部署) 一、安装docker(ubuntu系统版本20.4) 1、更新apt sudo apt-get update2、安装必要的依赖包 sudo apt-get install ca-certificates curl gnupg lsb-release3、添加 Docker 官方 GPG 密钥(清化大学源) # 添加Docke…...

python常用库-pandas、Hugging Face的datasets库(大模型之JSONL(JSON Lines))

文章目录 python常用库pandas、Hugging Face的datasets库&#xff08;大模型之JSONL&#xff08;JSON Lines&#xff09;&#xff09;背景什么是JSONL&#xff08;JSON Lines&#xff09;通过pandas读取和保存JSONL文件pandas读取和保存JSONL文件 Hugging Face的datasets库Hugg…...

高端装备制造企业如何选择适配的项目管理系统提升项目执行效率?附选型案例

高端装备制造项目通常涉及多专业协同、长周期交付和高风险管控&#xff0c;因此系统需具备全生命周期管理能力。例如&#xff0c;北京奥博思公司出品的 PowerProject 项目管理系统就是一款非常适合制造企业使用的项目管理软件系统。 国内某大型半导体装备制造企业与奥博思软件达…...

【Dv3Admin】工具权限配置文件解析

接口级权限控制是后台系统安全防护的核心手段。基于用户角色、请求路径与方法进行细粒度授权&#xff0c;可以有效隔离不同用户的数据访问范围&#xff0c;防止越权操作&#xff0c;保障系统整体稳定性。 本文解析 dvadmin/utils/permission.py 模块&#xff0c;重点关注其在匿…...

AI炼丹日志-22 - MCP 自动操作 Figma+Cursor 自动设计原型

MCP 基本介绍 官方地址&#xff1a; https://modelcontextprotocol.io/introduction “MCP 是一种开放协议&#xff0c;旨在标准化应用程序向大型语言模型&#xff08;LLM&#xff09;提供上下文的方式。可以把 MCP 想象成 AI 应用程序的 USB-C 接口。就像 USB-C 提供了一种…...

Python爬虫:AutoScraper 库详细使用大全(一个智能、自动、轻量级的网络爬虫)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、AutoScraper概述1.1 AutoScraper介绍1.2 安装1.3 注意事项二、基本使用方法2.1 创建 AutoScraper 实例2.2 训练模型2.3 保存和加载模型2.4 数据提取方法2.5 自定义规则三、高级功能3.1 多规则抓取3.2 分页抓取3.3 代…...

2025.6.1总结

今天又上了一天课&#xff0c;假期三天&#xff0c;上了两天的课&#xff0c;明天还得刷题。利用假期时间上课学习&#xff0c;并没有让我感到有多充实&#xff0c;反而让我感到有些小压抑。 在下午的好消息分享环节&#xff0c;我分享了毕业工作以来的一些迷茫。我不知道自己…...

[嵌入式实验]实验四:串口打印电压及温度

一、实验目的 熟悉开发环境在开发板上读取电压和温度信息使用串口和PC通信在PC上输出当前电压和温度信息 二、实验环境 硬件&#xff1a;STM32开发板、CMSIS-DAP调试工具 软件&#xff1a;STM32CubeMX软件、ARM的IDE&#xff1a;Keil C51 三、实验内容 配置相关硬件设施 &…...

LVS+Keepalived 高可用

目录 一、核心概念 1. LVS&#xff08;Linux Virtual Server&#xff09; 2. Keepalived 二、高可用架构设计 1. 架构拓扑图 2. 工作流程 三、部署步骤&#xff08;以 DR 模式为例&#xff09; 1. 环境准备 2. 主 LVS 节点配置 &#xff08;1&#xff09;安装 Keepali…...

Linux正则三剑客篇

一、历史命令 history 命令 &#xff1a;用于输出历史上使用过的命令行数量及具体命令。通过 history 可以快速查看并回顾之前执行过的命令&#xff0c;方便重复操作或追溯执行过程。 !行号 &#xff1a;通过指定历史命令的行号来重新执行该行号对应的命令。例如&#xff0c;若…...

HTML5 视频播放器:从基础到进阶的实现指南

在现代Web开发中&#xff0c;视频播放功能是许多网站的重要组成部分。无论是在线教育平台、视频分享网站&#xff0c;还是企业官网&#xff0c;HTML5视频播放器都扮演着不可或缺的角色。本文将从基础到进阶&#xff0c;详细介绍如何实现一个功能完善的HTML5视频播放器&#xff…...

鸿蒙HarmonyOS (React Native)的实战教程

一、环境配置 ‌安装鸿蒙专属模板‌ bashCopy Code npx react-native0.72.5 init HarmonyApp --template react-native-template-harmony:ml-citation{ref"4,6" data"citationList"} ‌配置 ArkTS 模块路径‌ 在 entry/src/main/ets 目录下创建原生模块&…...

函数栈帧深度解析:从寄存器操作看函数调用机制

文章目录 一、程序运行的 "舞台"&#xff1a;内存栈区与核心寄存器二、寄存器在函数调用中的核心作用​三、函数调用全流程解析&#xff1a;以 main 调用 func 为例阶段 1&#xff1a;main 函数栈帧初始化**阶段 2&#xff1a;参数压栈&#xff08;右→左顺序&#x…...

【计算机网络】第3章:传输层—可靠数据传输的原理

目录 一、PPT 二、总结 &#xff08;一&#xff09;可靠数据传输原理 关键机制 1. 序号机制 (Sequence Numbers) 2. 确认机制 (Acknowledgements - ACKs) 3. 重传机制 (Retransmission) 4. 校验和 (Checksum) 5. 流量控制 (Flow Control) 协议实现的核心&#xff1a;滑…...

rv1126b sdk移植

DDR rkbin bin/rv11/rv1126bp_ddr_v1.00.bin v1.00 板子2 reboot异常 [ 90.334976] reboot:Restarting system DDR 950804cb85 wesley.yao 25/04/02-15:54:40,fwver: v1.00In Derate1 tREFI1x SR93 PD13 R ddrconf 4 rgef0 rgcsb0 1 ERR: Read gate CS0 err error ERR …...

第6节 Node.js 回调函数

Node.js 异步编程的直接体现就是回调。 异步编程依托于回调来实现&#xff0c;但不能说使用了回调后程序就异步化了。 回调函数在完成任务后就会被调用&#xff0c;Node 使用了大量的回调函数&#xff0c;Node 所有 API 都支持回调函数。 例如&#xff0c;我们可以一边读取文…...

OpenCV CUDA模块直方图计算------在 GPU上执行直方图均衡化(Histogram Equalization)函数equalizeHist

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::equalizeHist 用于增强图像的对比度&#xff0c;通过将图像的灰度直方图重新分布&#xff0c;使得图像整体对比度更加明显。 这在医学…...

构建系统maven

1 前言 说真的&#xff0c;我是真的不想看构建了&#xff0c;因为真的太多了。又多又乱。Maven、Gradle、Make、CMake、Meson、Ninja&#xff0c;Android BP。。。感觉学不完&#xff0c;根本学不完。。。 但是没办法最近又要用一下Maven&#xff0c;所以咬着牙再简单整理一下…...

day13 leetcode-hot100-23(链表2)

206. 反转链表 - 力扣&#xff08;LeetCode&#xff09; 1.迭代 思路 这个题目很简单&#xff0c;最主要的就是了解链表的数据结构。 链表由多个节点构成&#xff0c;每个节点包括值与指针&#xff0c;其中指针指向下一个节点&#xff08;单链表&#xff09;。 方法就是将指…...

Java面试八股(Java基础,Spring,SpringBoot篇)

java基础 JDK,JRE,JVMJava语言的特点Java常见的运行时异常Java为什么要封装​自增自减的隐式转换移位运算符1. 左移运算符&#xff08;<<&#xff09;2. 带符号右移运算符&#xff08;>>&#xff09;3. 无符号右移运算符&#xff08;>>>&#xff09; 可变…...

Python编程基础(二)| 列表简介

引言&#xff1a;很久没有写 Python 了&#xff0c;有一点生疏。这是学习《Python 编程&#xff1a;从入门到实践&#xff08;第3版&#xff09;》的课后练习记录&#xff0c;主要目的是快速回顾基础知识。 练习1&#xff1a; 姓名 将一些朋友的姓名存储在一个列表中&#xf…...

支持向量机(SVM):解锁数据分类与回归的强大工具

在机器学习的世界中&#xff0c;支持向量机&#xff08;Support Vector Machine&#xff0c;简称 SVM&#xff09;一直以其强大的分类和回归能力而备受关注。本文将深入探讨 SVM 的核心功能&#xff0c;以及它如何在各种实际问题中发挥作用。 一、SVM 是什么&#xff1f; 支持…...

代谢组数据分析(二十五):代谢组与蛋白质组数据分析的异同

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍蛋白质组定义与基因的关系蛋白质组学(Proteomics)检测技术蛋白质的鉴定与定量分析蛋白质“鉴定”怎么做蛋白质“定量”怎么做蛋白质鉴定与定量对比应用领域代谢组定义代谢组学(M…...

002 flutter基础 初始文件讲解(1)

在学习flutter的时候&#xff0c;要有“万物皆widget”的思想&#xff0c;这样有利于你的学习&#xff0c;话不多说&#xff0c;开始今天的学习 1.创建文件 进入trae后&#xff0c;按住ctrlshiftP&#xff0c;输入Flutter&#xff1a;New Project&#xff0c;回车&#xff0c…...

AI 让无人机跟踪更精准——从视觉感知到智能预测

AI 让无人机跟踪更精准——从视觉感知到智能预测 无人机跟踪技术正在经历一场前所未有的变革。曾经,我们只能依靠 GPS 或简单的视觉识别来跟踪无人机,但如今,人工智能(AI)结合深度学习和高级视觉算法,正让无人机的跟踪变得更加智能化、精准化。 尤其是在自动驾驶、安防监…...

Launcher3体系化之路

&#x1f44b; 欢迎来到Launcher 3 背景 车企对于桌面的排版布局好像没有手机那般复杂&#xff0c;但也有一定的需求。部分场景下&#xff0c;要考虑的上下文比手机要多一些&#xff0c;比如有如下的一些场景&#xff1a; 手车互联。HiCar&#xff0c;CarPlay&#xff0c;An…...

用wireshark抓了个TCP通讯的包

昨儿个整理了下怎么用wireshark抓包&#xff0c;链接在这里&#xff1a;捋捋wireshark 今天打算抓个TCP通讯的包试试&#xff0c;整体来说比较有收获&#xff0c;给大家汇报一下。 首先就是如何搞到可以用来演示TCP通讯的客户端、服务端&#xff0c;问了下deepseek&#xff0c;…...